magento2: X-Magento-Tags header too large

Preconditions

I’ve been reading about people using Magento2 and the size of the X-Magento-Tags header.

Steps to reproduce

For example http://www.maxbucknell.com/blog/2016/2/10/troubleshooting-varnish-with-magento-2 writes:

The rub here is that Magento adds a tag for every product in a category. And the tag is of the form “catalog_product_{{PRODUCT_ID}}”.

Actual result

That’s an integer and 20 bytes of string per product, which gives some users headers several kB in size, when many products are in one category. In that case, the author describes hitting Varnish’s limit at 400 products.

Expected result

I work for a CDN where some of our users use Magento, and we cache their content with Varnish. Increasing Varnish’s http_resp_hdr_len size may suit that one person’s situation, but it is infeasible for us, because it would affect all of our users.

Proposed solution

I have a suggestion to offer, for how to use header space more efficiently:

Each product ID is an integer. The X-Magento-Tags header describes the presence or absence of products only. That information can be done with a single bit per product. The index of the bit would give the product ID.

That’s usually known as a bit vector, or a bitfield, or a bitmap. My suggestion is to switch the header to use a bitmap, encoded in hex. That’d take one bit per product, instead of over 20 bytes.

More on bitmaps: https://en.wikipedia.org/wiki/Bit_array

About this issue

  • Original URL
  • State: closed
  • Created 8 years ago
  • Reactions: 16
  • Comments: 72 (38 by maintainers)

Most upvoted comments

Thank you - Although I’d request that you consider this a blocking item, rather than an optimisation, because this header is so large that it prevents certain users from using Varnish.

As previously stated, this is still an issue in 2.1.8. I have implemented a Plugin around Magento\Catalog\Model\Product like so:

<?php

namespace YourCompany\YourModule\Plugin;

class Product{

    protected $_request;

    public function __construct(\Magento\Framework\App\Request\Http $request){
       $this->_request = $request;
    }

    /*
     * Return no simple product tags when returning category pages
     */
    public function afterGetIdentities(\Magento\Catalog\Model\Product $subject, $tags){
        if(!$this->_request){
            return $tags;
        }
        $r = $this->_request;
        $fullRoute = sprintf(
            '%s_%s_%s', $r->getRouteName(), $r->getControllerName(), $r->getActionName()
        );
        if($fullRoute === 'catalog_category_view'){
            return [];
        }else{
            return $tags;
        }
    }
}

?>

I very much realize that this is a hack, but we simply have too many products (over 64kB of tags) for the tag shortening to work. This will still show any parent product tags, FWIW.

It looks like \Magento\ConfigurableProduct\Model\Plugin\Frontend\ProductIdentitiesExtender plugin, which adds child product identities to a configurable product, can be safely removed. Another plugin adds parent identities to child products. So if child product is changed, cache for configurable product will be flushed even if page doesn’t have cache tag of child product.

I had the same problem on Magento 2.3.2. In my opinion, the cleanest approach is just to disable the \Magento\ConfigurableProduct\Model\Plugin\Frontend\ProductIdentitiesExtender::afterGetIdentities plugin. I did this by creating a new module and then disable the plugin via DI.xml.

Create etc/frontend/di.xml with content:

<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
    <!-- disable \Magento\ConfigurableProduct\Model\Plugin\Frontend\ProductIdentitiesExtender::afterGetIdentities -->
    <type name="Magento\Catalog\Model\Product">
        <plugin name="product_identities_extender" disabled="true"/>
    </type>
</config>

Any update on this? This is still present in 2.1.7.

@choukalos I think this ticket should be reopen, I have encountered this issue on a Magento site running on 2.1.5. Category pages containing a large number of products return a X-Magento-Tag header which is bigger than the size allowed by the HTTP specification.

We have implemented this workaround for now http://devdocs.magento.com/guides/v2.0/config-guide/varnish/tshoot-varnish-503.html

But this is not a solution, just a workaround because the http header size is still larger than the limit set by the HTTp specification.

I did this workaround to get my site working again. Basically disabling the addition of the child products of configurable items to the tag. There are cons’t defined that specify the catalog_product_ tag, so that should be pretty straightforward to shorten.

--- vendor/magento/module-configurable-product/Plugin/Model/Product.php.orig    2016-09-07 14:10:22.201010000 -0400
+++ vendor/magento/module-configurable-product/Plugin/Model/Product.php 2016-09-07 14:08:17.107250000 -0400
@@ -26,9 +26,9 @@
         /** @var Configurable $productType */
         $productType = $product->getTypeInstance();
         if ($productType instanceof Configurable) {
-            foreach ($productType->getChildrenIds($product->getId())[0] as $productId) {
-                $result[] = \Magento\Catalog\Model\Product::CACHE_TAG . '_' . $productId;
-            }
+            #foreach ($productType->getChildrenIds($product->getId())[0] as $productId) {
+            #    $result[] = \Magento\Catalog\Model\Product::CACHE_TAG . '_' . $productId;
+            #}
         }
         return $result;
     }

Excellent point! But I’m afraid such change is not going to happen anytime soon, maybe for Magento 3.0… and this is only one of the topics out there to discuss when it comes to optimization.

After having raised the http_resp_hdr_len to 1MB (derived by calculating the largest number of products in a category multiplied by 21, as advised by Magento: https://support.magento.com/hc/en-us/articles/360034631211-Troubleshooting-503-error-caused-by-necessity-to-change-default-Varnish-settings), we were still experiencing intermittent 503 errors. These would happen on static files, e.g. javascripts and images.

To further debug this, I opened a varnishlog using the following command: varnishlog -g request -q "RespStatus >= 500" This returns all requests with a response code of 500 or higher. Now we just had to wait for an error to appear.

Soon after, the errors appeared:

--  FetchError     Workspace overflow
--  FetchError     overflow
[...]
--  BerespStatus   503
--  BerespReason   Service Unavailable
--  BerespReason   Backend fetch failed
--  Error          out of workspace (bo)

This is indicating a clear error, and pointed me in the right direction to further raise the workspace parameters as well. I am now using these parameters and the errors haven’t reappeared:

    -p workspace_backend=2097152
    -p http_resp_size=1082768
    -p http_resp_hdr_len=1050000
    -p workspace_client=2097152
    -p http_req_size=65536
    -p http_req_hdr_len=32768

Of course, this shouldn’t be necessary, but given the fact that Magento uses these very large tag headers, I’m afraid we have no choice other than to tune these varnish parameters.

@amenk: as far as I’m aware: yes! The X-Magento-Tags header is only used by Varnish to indicate what tags are associated to a certain url. So when Magento tells Varnish to purge some tags later on, it will purge all url’s from its cache where that particular tag got associated to it. So I believe you should be able to safely remove the header if you aren’t using Varnish (or a similar reverse proxy that works with this header).

Hi @katef, @o-iegorov.

Thank you for your report and collaboration!

The issue was fixed by Magento team.

The fix will be available with the upcoming 2.3.5 release.

I always see the same faces in Magento’s tickets related to performance and huge catalogs. ✋

I believe this plugin is should not have been added to configurable products.

On stores with over 350k products this is what happens :

  1. It increases the size of the tag header from 815b to 18k (showing 48 products per page)
  2. Varnish RAM usage is increased because of the size of the tags.
  3. Header buffer sizes need to be increased for nginx buffers

I really don’t see the point of having all these tags. Note that this was introduced in this commit that was attempting to fix this issue https://github.com/magento/magento2/pull/19313

This was a fix for bundle products but was unfortunately also applied to the configurable products.

This might be a good solution for a store that has only a few products, not many children for configurable products and showing a small number of products per page in the category listings. Not so much for a big store that has configurable products with hundreds of children and shows many products per page.

This also seems to increase the PURGE requests size since it will have all the child tags. I believe that children products should simply return the parent tags and when a change is made to a child or a product the cache should be flushed by the parent tag. In other words, the child/parent relation should be checked when flushing the cache instead of adding all these tags to all the pages.

Maybe @sivaschenko can give us more insight on the reasoning for merging this and the testing that was done to ensure that this change does not break the infrastructure.

Some more detail: The catalog_category_product_ and catalog_category_ could easily be changed to 2 characters that would still be unique and cut down the length of this header by ~80%.

Of critical issue though is that in Magento 2.1 all versions of a configured product are incorrectly added to this header field. In 2.0 only the parent item was added. As a result the size of the field grows exponentially and makes varnish unusable. I am trying to find the code in question that is incorrectly adding all versions so I can comment it out and restore 2.0 functionality and and have an actual working site.

@katef thank you for reporting this issue, internal ticket created MAGETWO-57851

This happens on 2.4.3-p1 . Any updates or patches ? Thanks

@ecoprince, Now these commits are available. Here is merge commit that contains all these changes: https://github.com/magento/magento2/commit/3b611a7cc0040c9e5731cbc8e2080c2adb205d86

Internal Magento team is working on this issue right now.

Hi guys, after looking over the blog page about how the M2 FPC works, other comments on this thread and getting things working on my site I’ve got a couple things I’d like to share.

First off, merely increasing http_resp_hdr_len was not cutting it. There were several other settings I increased in Varnish to stop the 503’s

  • workspace_backend - 256k
  • workspace_client - 256k
  • workspace_session - 256k
  • workspace_thread - 8k (max)

Not sure if all of those are necessary, but the pages are loading. Stumbled into this thread when working on possibly related 504 responses on the same site about a week ago. I also brought http_resp_hdr_len to 2MB, based on what I saw requesting pages directly from the webserver (that would load successfully through curl lol).

The other thing is I think the patch in 2.2.3 is reasonable. Pretty much I agree with @pynej, it’s not a perfect solution, but it’s definitely the low hanging fruit here, and should dramatically reduce the X-Magento-Tags header size. And for an exhaustive solution like what @sambolek has built, I would still suggest the patch in 2.2.3 go along with it, because why store a bunch of extra data if you don’t need to!

@hostep Thanks - we have a patch on StackOverflow: https://magento.stackexchange.com/questions/287621/ah01070-error-parsing-script-headers-in-php-fpm-when-accessing-certain-product .

What should be noted is that the clearing should happen after the tags are extracted from the Header and used for the built-in cache as well.

@ihor-sviziev, I tried this and it seems to work properly. We will do some further testing. Since making patches was new to me a quick hint for other users. We used this file (based on merge commit 3b611a7) to patch using instructions https://devdocs.magento.com/guides/v2.4/comp-mgr/patching.html

@sivaschenko I agree that the child products affect parent product representation. But I strongly believe that we are addressing this issue the wrong way, as far as configurable products are concerned.

Goal : clear parent product caches when the child product is modified.


First approach : (the one implemented now)

  • Add all child product cache tags on parent product pages
  • Add all child products as well as parent products on category pages.
  • Clear caches by the child product cache tag and we get the wanted results. To me, this seems like a back-end/admin issue being fixed by adding non essential data to the front-end.

Pros: I tried to find some

  • We can clear caches by child product tag.
  • Makes the save operation faster (?) - not certain of this and I don’t want to benchmark this.
  • No need to figure out the parent product of a child when changes are done on the back-end (not sure if this is a pro or con actually)

Cons:

  • Header size is increased due to thousands of product tags
    • Think of category with 48 products per page & 5 size super-attributes & 5 colors super-attributes per product => translates into over 1248 tags; That’s huge!
  • Increased overhead processing for all category pages.
  • Every time a category page loaded needs to figure out all the tags of all the child products
  • Server config changes needed to support the increased header size.
  • Higher use of RAM by Varnish
  • Increased bandwidth and page sizes
  • Decreased performance overall - IMO

Second approach: (what I suggest as a more viable solution)

  • Always us the parent product tags on categories, configurable product pages and even the child product pages.
  • When a change is made to a child product, figure out the parent(s) and flush caches based on that tag.
  • When changes are made to a parent product, clear caches by the parent product cache tag. In other words, approach the issue when the caches are getting flushed (back-end), not when the category is getting generated for the front-end.

Cons:

  • Changes to a child product will flush caches for all siblings (if they’re visible individually).
  • Might make back-end operation less performant (save and maybe indexing) - again, not certain of this and I don’t want to benchmark this but feel free to and share the result.

Pros:

  • Fixes all the cons of the first approach.
  • It seems a more viable solution overall.

Now, this might not be a good approach for bundled products. I’m not very familiar with this type of products but I get the gist of it. I’m quite positive that a similar approach could be taken.


Please, let me know if this makes sense.

It looks like \Magento\ConfigurableProduct\Model\Plugin\Frontend\ProductIdentitiesExtender plugin, which adds child product identities to a configurable product, can be safely removed. Another plugin adds parent identities to child products. So if child product is changed, cache for configurable product will be flushed even if page doesn’t have cache tag of child product.

I can confirm that it’s true. The plugin is not useful and causes a varnish 503, when Magento is behind varnish.

Hello @sdzhepa

Could you raise the priority of this issue by talking to internal Magento teams? The problem really needs a solution as @hostep wrote earlier.

Thanks for collaboration!

@hostep I haven’t found any way to get around this with Apache and PHP-FPM. We’ve had to move to nginx to get around this.

We are also running against this problem on our local machines (no Varnish, no Redis) which seems to be caused by a configurable product on which 510 simple products are linked.

Does anybody know what settings to tweak in php-fpm and/or apache2 to get around this?

The solution linked by @mpchadwick on his blog doesn’t seem to resolve the issue over here unfortunately.

Disabling the plugin as mentioned by @Tjitse-E does seem to work, but doesn’t sound like a good solution.

@engcom-Hotel: can you try to bump the priority of this issue internally, because this is an issue on which you can spend many hours staring at a blank screen, finding no decent errors in log files and then accidentally finding this thread here.

Can you pls provide link to pull request?

@choukalos What was the resolution?

This workaround fixes but wastes lots or memory.