From a04662f2a788dc9f70ea0740112f9bee2cbf7061 Mon Sep 17 00:00:00 2001 From: Jonas Elfering Date: Fri, 31 Oct 2025 13:24:08 +0100 Subject: [PATCH 1/8] Adjust caching guide based on latest caching changes --- concepts/framework/http_cache.md | 27 +++- .../plugins/framework/caching/index.md | 138 ++++++++++++++++-- 2 files changed, 149 insertions(+), 16 deletions(-) diff --git a/concepts/framework/http_cache.md b/concepts/framework/http_cache.md index 8ec60f491..2e49788c1 100644 --- a/concepts/framework/http_cache.md +++ b/concepts/framework/http_cache.md @@ -47,20 +47,35 @@ Set the defaults value of the `_httpCache` key to `true`. Examples for this can public function index(SalesChannelContext $context, Request $request): Response ``` -### Cache Cookies +### Determining the cache key -Shopware uses several client cookies to differentiate the requests. This allows us to save the cache differentiated between clients, e.g., different customer groups or different active rules. +Determining the cache key is one of the most important tasks of the HTTP cache. The cache key is used to identify a request and its corresponding response. If the same request comes in again, the cache key will be used to look up the corresponding response in the cache storage. +For a dynamic system like Shopware, the cache key needs to take the application state into account, as the response to the same request might differ e.g., based on the tax state of the currently logged-in customer. +At the same time, it needs to be possible to generate the cache key directly from the request to support reverse proxy caches, where the caching is handled by a standalone application that has no access to Shopware's internal application state. +Shopware generates a `cache-hash` that encodes the application state and this hash is passed alongside every request and response, the caching component will then generate the exact cache key based on the cache-hash. -#### sw-currency - -This cookie will be set when the non-logged-in customer with an empty cart changes the current currency. Why does Shopware need a separate cookie for currency? It allows us to maximize the cache hits for non-logged-in customers as we separate the cache as less as possible. +Concretely Shopware uses Cookies to store the `cache-hash` as part of the request/response structure. The `cache-hash` describes the current state of the customer "session", every parameter that leads to different responses being generated (e.g. tax-states, matched rules) should be taken into account for the cache hash to ensure that every user sees the correct page. +However, it is equally important to keep the number of different cache entries/permutations as low as possible to maximize the cache hits. +The reason the cache-hash is stored as a cookie is that it needs to be sent with every request and can change on any response sent from shopware. +The client needs to send the latest value back to shopware on every request to ensure the correct cache entry is used. This is needed as the cache is resolved before the request is handled by shopware itself. +To allow reverse proxies to cache based on the application state, the information needs to be present on every request. #### sw-cache-hash -This cookie replaces the `sw-currency` cookie and contains the active rules and active currency. This cookie will be set when the active rules do not match the default anymore \(e.g., customer login/items in cart\). +This cookie contains the hash of all cache-relevant information (e.g. is the user logged-in, what tax state and what currency do they use, which cache-relevant rules have matched). +This cookie will be set as soon as the application state differs from the default (no logged in customer, in default currency, empty cart). + +If you want to know how to manipulate and control the cache hash, you can refer to the [Plugin caching guide](../../guides/plugins/plugins/framework/caching/index.md#http-cache). + +#### sw-currency + +**Note:** The currency cookie is deprecated and will be removed in v6.8.0.0, as the currency information is already part of the `sw-cache-hash` cookie. +This cookie will be set when the non-logged-in customer with an empty cart changes the current currency. Why does Shopware need a separate cookie for currency? It allows us to maximize the cache hits for non-logged-in customers as we separate the cache as less as possible. #### sw-states +**Note:** The states cookie is deprecated and will be removed in v6.8.0.0, as the state information is already part of the `sw-cache-hash` cookie and different caches are used for the different states. +If you want to disable the cache in certain circumstances, you can do so via the `sw-cache-hash` cookie as well. This cookie describes the current session in simple tags like `cart-filled` and `logged-in`. When the client tags fit the response `sw-invalidation-states` header, the cache will be skipped. An example of usage for this feature is to save the cache for logged-in customers only. diff --git a/guides/plugins/plugins/framework/caching/index.md b/guides/plugins/plugins/framework/caching/index.md index 9d312e28e..72dfbb6ff 100644 --- a/guides/plugins/plugins/framework/caching/index.md +++ b/guides/plugins/plugins/framework/caching/index.md @@ -22,13 +22,130 @@ For information on how to configure the different cache layers, please refer to ### HTTP-Cache -#### Modifying the cache keys +Before jumping in and adjusting the HTTP-Caching, please familiarize yourself with the general [HTTP-Cache concept](../../../../../concepts/framework/http_cache.md) first. + +#### Manipulating the cache key + +There are several entry points to manipulate the cache key. + +* `Shopware\Core\Framework\Adapter\Cache\Event\HttpCacheCookieEvent`: used to calculate the cache hash based on the application state, supports both reverse proxy caches and the default symfony HTTP-cache component. +* `Shopware\Core\Framework\Adapter\Cache\Http\Extension\ResolveCacheRelevantRuleIdsExtension`: used to determine which rule ids are relevant for the cache hash. +* `Shopware\Core\Framework\Adapter\Cache\Event\HttpCacheKeyEvent`: used to calculate the exact cache key based on the response, only for symfony's default HTTP-cache component. + +#### Modifying the cache hash + +The cache hash is used as the basis for the cache key. +It is calculated based on the application state, which includes the current user, the current language, and so on. +As the cache hash is calculated based on the application state, you have access to the resolved `SalesChannelContext` to determine the cache hash. +It is stored alongside the response as a cookie and thus also provided with all following requests, to allow differentiating the cache based on the application state. +As the cache hash will be carried over to the next request, the computed cache hash can be used inside reverse proxy caches as well as the default symfony HTTP-cache component. + +:::info +The cache hash is only computed on every response as soon as the application state differs from the default state (no logged in customer, default currency, empty cart). +::: + +By default, the cache hash will consist of the following parts: +* `rule-ids`: The matched rule ids, to reduce possible cache permutations starting with v6.8.0.0, this will only include the rule ids in `rule areas` that are cache relevant. See the next chapter how to extend this. +* `version-id`: The specified version in the context. +* `currency-id`: The currency id of the context. +* `tax-state`: The tax state of the context (gross/net). +* `logged-in`: Whether a customer is logged in in the current state or not. + +To modify the cache hash, you can subscribe to the `HttpCacheCookieEvent` event and add your own parts to the cache hash. +This allows you to add more parts to the cache hash, e.g., the current customer's group. +You can also disable the cache for certain conditions, because if that condition is met, the content is so dynamic that caching is not efficiently possible e.g., if the cart is filled. +```php +class HttpCacheCookieListener implements EventSubscriberInterface +{ + public function __construct( + private readonly CartService $cartService + ) { + } + + public static function getSubscribedEvents(): array + { + return [ + HttpCacheCookieEvent::class => 'onCacheCookie', + ]; + } + + public function onCacheCookie(HttpCacheCookieEvent $event): void + { + // you can add custom parts to the cache hash + // keep in mind that every possible value will increase the number of possible cache permutations + // and therefore directly impact cache hit rates + $event->add('customer-group', $event->context->getCustomerId()); + + // disable cache for filled carts + $cart = $this->cartService->getCart($event->context->getToken(), $event->context); + if ($cart->getLineItems()->count() > 0) { + // you can also explicitly disable caching based on specific conditions + $event->isCacheable = false; + } + } +} +``` + +#### Marking rule areas as cache relevant + +Starting with v6.8.0.0, the cache hash will only include the rule ids in `rule areas` that are cache relevant. +The reason is that a lot of rules are not relevant for the cache, e.g., rules that only affect pricing or shipping methods. +This greatly reduces the number of possible cache permutations, which in turn improves the cache hit rate. + +By default, only the following rule areas are cache relevant: +* `RuleAreas::PRODUCT_AREA` -Every cached item has a unique key used to retrieve the cached data later on. For that, it is important that all the relevant information that affects the data that is being cached is part of the key. -For example, if the same data is cached in multiple languages or for multiple sales channels, the key must contain the language and sales channel information, so that the correct data can be retrieved later on. -Please note that for every potential value your key part can take, a new cache entry will be created. So if you have a key part that can take 10 different values, you will have 10 times the number of cache entries for the same data. +If you use the rule system in a way that is relevant for the cache (because the response differs based on the rules), you should add your rule area to the list of cache relevant rule areas. +To do so, you need to subscribe to the `ResolveCacheRelevantRuleIdsExtension` event. -If you add customization to your projects that lead to different versions of the page being rendered, you need to make sure that the cache key is unique for each version of the page. This can be done by adding a specific part to the cache key that shopware is generating. +```php +class ResolveRuleIds implements EventSubscriberInterface +{ + public static function getSubscribedEvents(): array + { + return [ + ResolveCacheRelevantRuleIdsExtension::NAME . '.pre' => 'onResolveRuleAreas', + ]; + } + + public function onResolveRuleAreas(ResolveCacheRelevantRuleIdsExtension $extension): void + { + $extension->ruleAreas[] = RuleExtension::MY_CUSTOM_RULE_AREA; + } +} +``` + +This implies that you defined the rule area in your custom entities that have an associated rule entity, by using the DAL flag `Shopware\Core\Framework\DataAbstractionLayer\Field\Flag\RuleAreas` on the rule association in the entity extension. +```php +class RuleExtension extends EntityExtension +{ + public const MY_CUSTOM_RULE_AREA = 'custom'; + + public function getEntityName(): string + { + return RuleDefinition::ENTITY_NAME; + } + + public function extendFields(FieldCollection $collection): void + { + $collection->add( + (new ManyToManyAssociationField( + 'myPropertyName', + MyCustomDefinition::class, + MyMappingDefinition::class, + RuleDefinition::ENTITY_NAME . '_id', + MyCustomDefinition::ENTITY_NAME . '_id', + ))->addFlags(new CascadeDelete(), new RuleAreas(self::MY_CUSTOM_RULE_AREA)), + ); + } +} +``` +For details on how to extend core definitions refer to the [DAL Guide](../../framework/data-handling/add-complex-data-to-existing-entities.md). + +#### Modifying the cache keys + +You can also modify the exact cache key used to store the response in the [symfony HTTP-Cache](https://symfony.com/doc/current/http_cache.html). +If possible, you should manipulate the cache hash (as already explained above) instead, as that is also used in reverse proxy caches. You can do so by subscribing to the `HttpCacheKeyEvent` event and add your specific part to the key. ```php @@ -47,6 +164,9 @@ class CacheKeySubscriber implements EventSubscriberInterface // Perform checks to determine the key $key = $this->determineKey($request); $event->add('myCustomKey', $key); + + // You can also disable caching for certain conditions + $event->isCacheable = false; } } ``` @@ -64,21 +184,19 @@ Only invalidating the caches based on the unique cache key is often not that hel Therefore, a tagging system is used alongside the cache keys to make cache invalidations easier and more performant. Every cache entry can be tagged with multiple tags, thus we can invalidate the cache based on the tags. For example, all pages that contain product data are tagged with product ids of all products they contain. So if a product is changed, we can invalidate all cache entries that are tagged with the product id of the changed product. -To add your own cache tags to the HTTP-Cache, you need to dispatch the `AddCacheTagEvent` with the tag you want to add to the cache entry for the current request. +To add your own cache tags to the HTTP-Cache, you can use the `CacheTagCollector` service. ```php class MyCustomEntityExtension { public function __construct( - private readonly EventDispatcherInterface $eventDispatcher, + private readonly CacheTagCollector $cacheTagCollector, ) {} public function loadAdditionalData(): void { // Load the additional data you need, add it to the response, then add the correct tag to the cache entry - $this->eventDispatcher->dispatch( - new AddCacheTagEvent('my-custom-entity-' . $idOfTheLoadedData) - ); + $this->cacheTagCollector->addTag('my-custom-entity-' . $idOfTheLoadedData); } } ``` From f8a5aea15ca4ebca409638ed3a558d28a50dfef5 Mon Sep 17 00:00:00 2001 From: Jonas Elfering Date: Fri, 31 Oct 2025 13:31:30 +0100 Subject: [PATCH 2/8] Fix md lint --- concepts/framework/http_cache.md | 6 +++--- guides/plugins/plugins/framework/caching/index.md | 7 ++++++- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/concepts/framework/http_cache.md b/concepts/framework/http_cache.md index 2e49788c1..81d41e001 100644 --- a/concepts/framework/http_cache.md +++ b/concepts/framework/http_cache.md @@ -55,10 +55,10 @@ At the same time, it needs to be possible to generate the cache key directly fro Shopware generates a `cache-hash` that encodes the application state and this hash is passed alongside every request and response, the caching component will then generate the exact cache key based on the cache-hash. Concretely Shopware uses Cookies to store the `cache-hash` as part of the request/response structure. The `cache-hash` describes the current state of the customer "session", every parameter that leads to different responses being generated (e.g. tax-states, matched rules) should be taken into account for the cache hash to ensure that every user sees the correct page. -However, it is equally important to keep the number of different cache entries/permutations as low as possible to maximize the cache hits. -The reason the cache-hash is stored as a cookie is that it needs to be sent with every request and can change on any response sent from shopware. +However, it is equally important to keep the number of different cache entries/permutations as low as possible to maximize the cache hits. +The reason the cache-hash is stored as a cookie is that it needs to be sent with every request and can change on any response sent from shopware. The client needs to send the latest value back to shopware on every request to ensure the correct cache entry is used. This is needed as the cache is resolved before the request is handled by shopware itself. -To allow reverse proxies to cache based on the application state, the information needs to be present on every request. +To allow reverse proxies to cache based on the application state, the information needs to be present on every request. #### sw-cache-hash diff --git a/guides/plugins/plugins/framework/caching/index.md b/guides/plugins/plugins/framework/caching/index.md index 72dfbb6ff..fa40724f2 100644 --- a/guides/plugins/plugins/framework/caching/index.md +++ b/guides/plugins/plugins/framework/caching/index.md @@ -30,7 +30,7 @@ There are several entry points to manipulate the cache key. * `Shopware\Core\Framework\Adapter\Cache\Event\HttpCacheCookieEvent`: used to calculate the cache hash based on the application state, supports both reverse proxy caches and the default symfony HTTP-cache component. * `Shopware\Core\Framework\Adapter\Cache\Http\Extension\ResolveCacheRelevantRuleIdsExtension`: used to determine which rule ids are relevant for the cache hash. -* `Shopware\Core\Framework\Adapter\Cache\Event\HttpCacheKeyEvent`: used to calculate the exact cache key based on the response, only for symfony's default HTTP-cache component. +* `Shopware\Core\Framework\Adapter\Cache\Event\HttpCacheKeyEvent`: used to calculate the exact cache key based on the response, only for symfony's default HTTP-cache component. #### Modifying the cache hash @@ -45,6 +45,7 @@ The cache hash is only computed on every response as soon as the application sta ::: By default, the cache hash will consist of the following parts: + * `rule-ids`: The matched rule ids, to reduce possible cache permutations starting with v6.8.0.0, this will only include the rule ids in `rule areas` that are cache relevant. See the next chapter how to extend this. * `version-id`: The specified version in the context. * `currency-id`: The currency id of the context. @@ -54,6 +55,7 @@ By default, the cache hash will consist of the following parts: To modify the cache hash, you can subscribe to the `HttpCacheCookieEvent` event and add your own parts to the cache hash. This allows you to add more parts to the cache hash, e.g., the current customer's group. You can also disable the cache for certain conditions, because if that condition is met, the content is so dynamic that caching is not efficiently possible e.g., if the cart is filled. + ```php class HttpCacheCookieListener implements EventSubscriberInterface { @@ -93,6 +95,7 @@ The reason is that a lot of rules are not relevant for the cache, e.g., rules th This greatly reduces the number of possible cache permutations, which in turn improves the cache hit rate. By default, only the following rule areas are cache relevant: + * `RuleAreas::PRODUCT_AREA` If you use the rule system in a way that is relevant for the cache (because the response differs based on the rules), you should add your rule area to the list of cache relevant rule areas. @@ -116,6 +119,7 @@ class ResolveRuleIds implements EventSubscriberInterface ``` This implies that you defined the rule area in your custom entities that have an associated rule entity, by using the DAL flag `Shopware\Core\Framework\DataAbstractionLayer\Field\Flag\RuleAreas` on the rule association in the entity extension. + ```php class RuleExtension extends EntityExtension { @@ -140,6 +144,7 @@ class RuleExtension extends EntityExtension } } ``` + For details on how to extend core definitions refer to the [DAL Guide](../../framework/data-handling/add-complex-data-to-existing-entities.md). #### Modifying the cache keys From fe69a95cc1e86c7db8244bdaa2425d690df0f1fb Mon Sep 17 00:00:00 2001 From: Jonas Elfering Date: Fri, 31 Oct 2025 14:47:00 +0100 Subject: [PATCH 3/8] Update concepts/framework/http_cache.md Co-authored-by: Michael Telgmann --- concepts/framework/http_cache.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/concepts/framework/http_cache.md b/concepts/framework/http_cache.md index 81d41e001..bf2e13ee6 100644 --- a/concepts/framework/http_cache.md +++ b/concepts/framework/http_cache.md @@ -63,7 +63,7 @@ To allow reverse proxies to cache based on the application state, the informatio #### sw-cache-hash This cookie contains the hash of all cache-relevant information (e.g. is the user logged-in, what tax state and what currency do they use, which cache-relevant rules have matched). -This cookie will be set as soon as the application state differs from the default (no logged in customer, in default currency, empty cart). +This cookie will be set as soon as the application state differs from the default, which is: no logged in customer, the default currency and an empty cart. If you want to know how to manipulate and control the cache hash, you can refer to the [Plugin caching guide](../../guides/plugins/plugins/framework/caching/index.md#http-cache). From 39c67755e6284ad985302553f6c44539dcec9534 Mon Sep 17 00:00:00 2001 From: Jonas Elfering Date: Fri, 31 Oct 2025 14:47:12 +0100 Subject: [PATCH 4/8] Update guides/plugins/plugins/framework/caching/index.md Co-authored-by: Michael Telgmann --- guides/plugins/plugins/framework/caching/index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/guides/plugins/plugins/framework/caching/index.md b/guides/plugins/plugins/framework/caching/index.md index fa40724f2..a5ab63e5b 100644 --- a/guides/plugins/plugins/framework/caching/index.md +++ b/guides/plugins/plugins/framework/caching/index.md @@ -41,7 +41,7 @@ It is stored alongside the response as a cookie and thus also provided with all As the cache hash will be carried over to the next request, the computed cache hash can be used inside reverse proxy caches as well as the default symfony HTTP-cache component. :::info -The cache hash is only computed on every response as soon as the application state differs from the default state (no logged in customer, default currency, empty cart). +The cache hash is only computed on every response as soon as the application state differs from the default state, which is: no logged in customer, default currency and an empty cart. ::: By default, the cache hash will consist of the following parts: From 5a765a3068b8b7d8ddf7f963c52ba35336cbc86a Mon Sep 17 00:00:00 2001 From: Jonas Elfering Date: Mon, 3 Nov 2025 09:26:13 +0100 Subject: [PATCH 5/8] Fix CR comments --- concepts/framework/http_cache.md | 11 ++-- devenv.lock | 53 ++++++++++--------- .../plugins/framework/caching/index.md | 2 +- 3 files changed, 35 insertions(+), 31 deletions(-) diff --git a/concepts/framework/http_cache.md b/concepts/framework/http_cache.md index bf2e13ee6..08f2a694d 100644 --- a/concepts/framework/http_cache.md +++ b/concepts/framework/http_cache.md @@ -52,20 +52,21 @@ public function index(SalesChannelContext $context, Request $request): Response Determining the cache key is one of the most important tasks of the HTTP cache. The cache key is used to identify a request and its corresponding response. If the same request comes in again, the cache key will be used to look up the corresponding response in the cache storage. For a dynamic system like Shopware, the cache key needs to take the application state into account, as the response to the same request might differ e.g., based on the tax state of the currently logged-in customer. At the same time, it needs to be possible to generate the cache key directly from the request to support reverse proxy caches, where the caching is handled by a standalone application that has no access to Shopware's internal application state. -Shopware generates a `cache-hash` that encodes the application state and this hash is passed alongside every request and response, the caching component will then generate the exact cache key based on the cache-hash. +Shopware generates a `cache-hash` that encodes the application state and this hash is passed alongside every request and response, the caching component will then generate the exact cache key based on the `cache-hash`. -Concretely Shopware uses Cookies to store the `cache-hash` as part of the request/response structure. The `cache-hash` describes the current state of the customer "session", every parameter that leads to different responses being generated (e.g. tax-states, matched rules) should be taken into account for the cache hash to ensure that every user sees the correct page. +Concretely Shopware uses Cookies to store the `cache-hash` as part of the request/response structure. The `cache-hash` describes the current state of the customer "session", every parameter that leads to different responses being generated (e.g. tax-states, matched rules) should be taken into account for the `cache-hash` to ensure that every user sees the correct page. However, it is equally important to keep the number of different cache entries/permutations as low as possible to maximize the cache hits. -The reason the cache-hash is stored as a cookie is that it needs to be sent with every request and can change on any response sent from shopware. +The reason the `cache-hash` is stored as a cookie is that it needs to be sent with every request and can change on any response sent from shopware. The client needs to send the latest value back to shopware on every request to ensure the correct cache entry is used. This is needed as the cache is resolved before the request is handled by shopware itself. -To allow reverse proxies to cache based on the application state, the information needs to be present on every request. +To allow reverse proxies to cache based on the application state, the information needs to be present on every request. The reverse proxies (e.g. Fastly or Varnish) or the symfony cache component use the provided `cache-hash` as part of the cache key they generate for every request, thus they can differentiate the cache entries for the same request based on the application state. #### sw-cache-hash This cookie contains the hash of all cache-relevant information (e.g. is the user logged-in, what tax state and what currency do they use, which cache-relevant rules have matched). +This is the cookie that stores the `cache-hash`mentioned above. This cookie will be set as soon as the application state differs from the default, which is: no logged in customer, the default currency and an empty cart. -If you want to know how to manipulate and control the cache hash, you can refer to the [Plugin caching guide](../../guides/plugins/plugins/framework/caching/index.md#http-cache). +If you want to know how to manipulate and control the `cache-hash`, you can refer to the [Plugin caching guide](../../guides/plugins/plugins/framework/caching/index.md#http-cache). #### sw-currency diff --git a/devenv.lock b/devenv.lock index 2d93e3dca..489edcbc0 100644 --- a/devenv.lock +++ b/devenv.lock @@ -20,10 +20,10 @@ "flake-compat": { "flake": false, "locked": { - "lastModified": 1747046372, + "lastModified": 1761588595, "owner": "edolstra", "repo": "flake-compat", - "rev": "9100a0f413b0c601e0533d1d94ffd501ce2e7885", + "rev": "f387cd2afec9419c8ee37694406ca490c3f34ee5", "type": "github" }, "original": { @@ -32,10 +32,31 @@ "type": "github" } }, + "git-hooks": { + "inputs": { + "flake-compat": "flake-compat", + "gitignore": "gitignore", + "nixpkgs": [ + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1760663237, + "owner": "cachix", + "repo": "git-hooks.nix", + "rev": "ca5b894d3e3e151ffc1db040b6ce4dcc75d31c37", + "type": "github" + }, + "original": { + "owner": "cachix", + "repo": "git-hooks.nix", + "type": "github" + } + }, "gitignore": { "inputs": { "nixpkgs": [ - "pre-commit-hooks", + "git-hooks", "nixpkgs" ] }, @@ -68,32 +89,14 @@ "type": "github" } }, - "pre-commit-hooks": { - "inputs": { - "flake-compat": "flake-compat", - "gitignore": "gitignore", - "nixpkgs": [ - "nixpkgs" - ] - }, - "locked": { - "lastModified": 1747372754, - "owner": "cachix", - "repo": "pre-commit-hooks.nix", - "rev": "80479b6ec16fefd9c1db3ea13aeb038c60530f46", - "type": "github" - }, - "original": { - "owner": "cachix", - "repo": "pre-commit-hooks.nix", - "type": "github" - } - }, "root": { "inputs": { "devenv": "devenv", + "git-hooks": "git-hooks", "nixpkgs": "nixpkgs", - "pre-commit-hooks": "pre-commit-hooks" + "pre-commit-hooks": [ + "git-hooks" + ] } } }, diff --git a/guides/plugins/plugins/framework/caching/index.md b/guides/plugins/plugins/framework/caching/index.md index a5ab63e5b..e67c4d191 100644 --- a/guides/plugins/plugins/framework/caching/index.md +++ b/guides/plugins/plugins/framework/caching/index.md @@ -47,7 +47,7 @@ The cache hash is only computed on every response as soon as the application sta By default, the cache hash will consist of the following parts: * `rule-ids`: The matched rule ids, to reduce possible cache permutations starting with v6.8.0.0, this will only include the rule ids in `rule areas` that are cache relevant. See the next chapter how to extend this. -* `version-id`: The specified version in the context. +* `version-id`: The context version used to load versioned DAL entities. * `currency-id`: The currency id of the context. * `tax-state`: The tax state of the context (gross/net). * `logged-in`: Whether a customer is logged in in the current state or not. From c80a15f23c63e37053d0c5a8c4af1b3501cffcc6 Mon Sep 17 00:00:00 2001 From: Jonas Elfering Date: Wed, 19 Nov 2025 15:32:51 +0100 Subject: [PATCH 6/8] Add docs for CacheHashRequiredExtension --- .../plugins/framework/caching/index.md | 28 ++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/guides/plugins/plugins/framework/caching/index.md b/guides/plugins/plugins/framework/caching/index.md index e67c4d191..b03046bf4 100644 --- a/guides/plugins/plugins/framework/caching/index.md +++ b/guides/plugins/plugins/framework/caching/index.md @@ -28,10 +28,36 @@ Before jumping in and adjusting the HTTP-Caching, please familiarize yourself wi There are several entry points to manipulate the cache key. +* `Shopware\Core\Framework\Adapter\Cache\Http\Extension\CacheHashRequiredExtension`: used to determine whether the cache hash should be calculated or if the request in running in the default state, and therefore no cache-hash is needed. * `Shopware\Core\Framework\Adapter\Cache\Event\HttpCacheCookieEvent`: used to calculate the cache hash based on the application state, supports both reverse proxy caches and the default symfony HTTP-cache component. * `Shopware\Core\Framework\Adapter\Cache\Http\Extension\ResolveCacheRelevantRuleIdsExtension`: used to determine which rule ids are relevant for the cache hash. * `Shopware\Core\Framework\Adapter\Cache\Event\HttpCacheKeyEvent`: used to calculate the exact cache key based on the response, only for symfony's default HTTP-cache component. +#### Modifying when the cache hash is calculated + +By default, the cache hash is only calculated when the request is not in the default state, which is: no logged in customer, default currency, and an empty cart. +The reason is that the very first request to the application from a client should always be cached in the best case, the state that the application is in then is the "default state", which does not require a cache hash. +You can overwrite the default behaviour and add more conditions where the cache hash needs to be applied, e.g., when you shop needs to be more dynamic e.g. based on campaign query parameters: + +```php +class RequireCacheHash implements EventSubscriberInterface +{ + public static function getSubscribedEvents(): array + { + return [ + CacheHashRequiredExtension::NAME . '.post' => 'onRequireCacheHash',, + ]; + } + + public function onRequireCacheHash(CacheHashRequiredExtension $extension): void + { + if ($extension->request->query->has('campaignId')) { + $extension->result = true; + } + } +} +``` + #### Modifying the cache hash The cache hash is used as the basis for the cache key. @@ -41,7 +67,7 @@ It is stored alongside the response as a cookie and thus also provided with all As the cache hash will be carried over to the next request, the computed cache hash can be used inside reverse proxy caches as well as the default symfony HTTP-cache component. :::info -The cache hash is only computed on every response as soon as the application state differs from the default state, which is: no logged in customer, default currency and an empty cart. +The cache hash is only computed on every response as soon as the application state differs from the default state, which is: no logged in customer, default currency, and an empty cart. ::: By default, the cache hash will consist of the following parts: From 81e6634fed4b9c14001564b32b2237b175f799d7 Mon Sep 17 00:00:00 2001 From: Jonas Elfering Date: Thu, 20 Nov 2025 10:53:51 +0100 Subject: [PATCH 7/8] Update concepts/framework/http_cache.md Co-authored-by: Michael Telgmann --- concepts/framework/http_cache.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/concepts/framework/http_cache.md b/concepts/framework/http_cache.md index 08f2a694d..dadbe7b3c 100644 --- a/concepts/framework/http_cache.md +++ b/concepts/framework/http_cache.md @@ -63,7 +63,7 @@ To allow reverse proxies to cache based on the application state, the informatio #### sw-cache-hash This cookie contains the hash of all cache-relevant information (e.g. is the user logged-in, what tax state and what currency do they use, which cache-relevant rules have matched). -This is the cookie that stores the `cache-hash`mentioned above. +This is the cookie that stores the `cache-hash` mentioned above. This cookie will be set as soon as the application state differs from the default, which is: no logged in customer, the default currency and an empty cart. If you want to know how to manipulate and control the `cache-hash`, you can refer to the [Plugin caching guide](../../guides/plugins/plugins/framework/caching/index.md#http-cache). From 0227ae40a67b8e4b0447bef2a1c49937427bb818 Mon Sep 17 00:00:00 2001 From: Jonas Elfering Date: Thu, 20 Nov 2025 10:57:25 +0100 Subject: [PATCH 8/8] Fix review comments --- guides/plugins/plugins/framework/caching/index.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/guides/plugins/plugins/framework/caching/index.md b/guides/plugins/plugins/framework/caching/index.md index b03046bf4..bba2e13b7 100644 --- a/guides/plugins/plugins/framework/caching/index.md +++ b/guides/plugins/plugins/framework/caching/index.md @@ -30,7 +30,7 @@ There are several entry points to manipulate the cache key. * `Shopware\Core\Framework\Adapter\Cache\Http\Extension\CacheHashRequiredExtension`: used to determine whether the cache hash should be calculated or if the request in running in the default state, and therefore no cache-hash is needed. * `Shopware\Core\Framework\Adapter\Cache\Event\HttpCacheCookieEvent`: used to calculate the cache hash based on the application state, supports both reverse proxy caches and the default symfony HTTP-cache component. -* `Shopware\Core\Framework\Adapter\Cache\Http\Extension\ResolveCacheRelevantRuleIdsExtension`: used to determine which rule ids are relevant for the cache hash. +* `Shopware\Core\Framework\Adapter\Cache\Http\Extension\ResolveCacheRelevantRuleIdsExtension`: used to determine which rule IDs are relevant for the cache hash. * `Shopware\Core\Framework\Adapter\Cache\Event\HttpCacheKeyEvent`: used to calculate the exact cache key based on the response, only for symfony's default HTTP-cache component. #### Modifying when the cache hash is calculated @@ -72,9 +72,9 @@ The cache hash is only computed on every response as soon as the application sta By default, the cache hash will consist of the following parts: -* `rule-ids`: The matched rule ids, to reduce possible cache permutations starting with v6.8.0.0, this will only include the rule ids in `rule areas` that are cache relevant. See the next chapter how to extend this. +* `rule-ids`: The matched rule IDs, to reduce possible cache permutations starting with v6.8.0.0, this will only include the rule IDs in `rule areas` that are cache relevant. See the next chapter how to extend this. * `version-id`: The context version used to load versioned DAL entities. -* `currency-id`: The currency id of the context. +* `currency-id`: The currency ID of the context. * `tax-state`: The tax state of the context (gross/net). * `logged-in`: Whether a customer is logged in in the current state or not. @@ -101,7 +101,7 @@ class HttpCacheCookieListener implements EventSubscriberInterface { // you can add custom parts to the cache hash // keep in mind that every possible value will increase the number of possible cache permutations - // and therefore directly impact cache hit rates + // and therefore directly impact cache hit rates, which in turn decreases performance $event->add('customer-group', $event->context->getCustomerId()); // disable cache for filled carts @@ -116,7 +116,7 @@ class HttpCacheCookieListener implements EventSubscriberInterface #### Marking rule areas as cache relevant -Starting with v6.8.0.0, the cache hash will only include the rule ids in `rule areas` that are cache relevant. +Starting with v6.8.0.0, the cache hash will only include the rule IDs in `rule areas` that are cache relevant. The reason is that a lot of rules are not relevant for the cache, e.g., rules that only affect pricing or shipping methods. This greatly reduces the number of possible cache permutations, which in turn improves the cache hit rate. @@ -213,7 +213,7 @@ Also, with an external reverse proxy, the cache key might be generated on the pr One problem with caching is that you not only need to retrieve the correct data, but also need to have a performant way to invalidate the cache when the data changes. Only invalidating the caches based on the unique cache key is often not that helpful, because you don't know which cache keys are affected by the change of a specific data set. Therefore, a tagging system is used alongside the cache keys to make cache invalidations easier and more performant. Every cache entry can be tagged with multiple tags, thus we can invalidate the cache based on the tags. -For example, all pages that contain product data are tagged with product ids of all products they contain. So if a product is changed, we can invalidate all cache entries that are tagged with the product id of the changed product. +For example, all pages that contain product data are tagged with product IDs of all products they contain. So if a product is changed, we can invalidate all cache entries that are tagged with the product ID of the changed product. To add your own cache tags to the HTTP-Cache, you can use the `CacheTagCollector` service.