Skip to content
Draft
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 21 additions & 6 deletions concepts/framework/http_cache.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
143 changes: 133 additions & 10 deletions guides/plugins/plugins/framework/caching/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,135 @@

### 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.

Check warning on line 32 in guides/plugins/plugins/framework/caching/index.md

View workflow job for this annotation

GitHub Actions / LanguageTool

[LanguageTool] guides/plugins/plugins/framework/caching/index.md#L32

This abbreviation for “identification” is spelled all-uppercase. (ID_CASING[2]) Suggestions: `IDs` Rule: https://community.languagetool.org/rule/show/ID_CASING?lang=en-US&subId=2 Category: CASING
Raw output
guides/plugins/plugins/framework/caching/index.md:32:124: This abbreviation for “identification” is spelled all-uppercase. (ID_CASING[2])
 Suggestions: `IDs`
 Rule: https://community.languagetool.org/rule/show/ID_CASING?lang=en-US&subId=2
 Category: CASING
* `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.

Check warning on line 49 in guides/plugins/plugins/framework/caching/index.md

View workflow job for this annotation

GitHub Actions / LanguageTool

[LanguageTool] guides/plugins/plugins/framework/caching/index.md#L49

This abbreviation for “identification” is spelled all-uppercase. (ID_CASING[2]) Suggestions: `IDs` Rule: https://community.languagetool.org/rule/show/ID_CASING?lang=en-US&subId=2 Category: CASING
Raw output
guides/plugins/plugins/framework/caching/index.md:49:31: This abbreviation for “identification” is spelled all-uppercase. (ID_CASING[2])
 Suggestions: `IDs`
 Rule: https://community.languagetool.org/rule/show/ID_CASING?lang=en-US&subId=2
 Category: CASING

Check warning on line 49 in guides/plugins/plugins/framework/caching/index.md

View workflow job for this annotation

GitHub Actions / LanguageTool

[LanguageTool] guides/plugins/plugins/framework/caching/index.md#L49

This abbreviation for “identification” is spelled all-uppercase. (ID_CASING[2]) Suggestions: `IDs` Rule: https://community.languagetool.org/rule/show/ID_CASING?lang=en-US&subId=2 Category: CASING
Raw output
guides/plugins/plugins/framework/caching/index.md:49:130: This abbreviation for “identification” is spelled all-uppercase. (ID_CASING[2])
 Suggestions: `IDs`
 Rule: https://community.languagetool.org/rule/show/ID_CASING?lang=en-US&subId=2
 Category: CASING
* `version-id`: The specified version in the context.
* `currency-id`: The currency id of the context.

Check warning on line 51 in guides/plugins/plugins/framework/caching/index.md

View workflow job for this annotation

GitHub Actions / LanguageTool

[LanguageTool] guides/plugins/plugins/framework/caching/index.md#L51

This abbreviation for “identification” is spelled all-uppercase. (ID_CASING[2]) Suggestions: `ID` Rule: https://community.languagetool.org/rule/show/ID_CASING?lang=en-US&subId=2 Category: CASING
Raw output
guides/plugins/plugins/framework/caching/index.md:51:30: This abbreviation for “identification” is spelled all-uppercase. (ID_CASING[2])
 Suggestions: `ID`
 Rule: https://community.languagetool.org/rule/show/ID_CASING?lang=en-US&subId=2
 Category: CASING
* `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

Check warning on line 59 in guides/plugins/plugins/framework/caching/index.md

View workflow job for this annotation

GitHub Actions / LanguageTool

[LanguageTool] guides/plugins/plugins/framework/caching/index.md#L59

File types are normally capitalized. (FILE_EXTENSIONS_CASE[1]) Suggestions: `PHP` URL: https://languagetool.org/insights/post/spelling-capital-letters/ Rule: https://community.languagetool.org/rule/show/FILE_EXTENSIONS_CASE?lang=en-US&subId=1 Category: CASING
Raw output
guides/plugins/plugins/framework/caching/index.md:59:3: File types are normally capitalized. (FILE_EXTENSIONS_CASE[1])
 Suggestions: `PHP`
 URL: https://languagetool.org/insights/post/spelling-capital-letters/ 
 Rule: https://community.languagetool.org/rule/show/FILE_EXTENSIONS_CASE?lang=en-US&subId=1
 Category: CASING
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.

Check warning on line 93 in guides/plugins/plugins/framework/caching/index.md

View workflow job for this annotation

GitHub Actions / LanguageTool

[LanguageTool] guides/plugins/plugins/framework/caching/index.md#L93

This abbreviation for “identification” is spelled all-uppercase. (ID_CASING[2]) Suggestions: `IDs` Rule: https://community.languagetool.org/rule/show/ID_CASING?lang=en-US&subId=2 Category: CASING
Raw output
guides/plugins/plugins/framework/caching/index.md:93:66: This abbreviation for “identification” is spelled all-uppercase. (ID_CASING[2])
 Suggestions: `IDs`
 Rule: https://community.languagetool.org/rule/show/ID_CASING?lang=en-US&subId=2
 Category: CASING
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:

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.
* `RuleAreas::PRODUCT_AREA`

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.
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.

```php

Check warning on line 104 in guides/plugins/plugins/framework/caching/index.md

View workflow job for this annotation

GitHub Actions / LanguageTool

[LanguageTool] guides/plugins/plugins/framework/caching/index.md#L104

File types are normally capitalized. (FILE_EXTENSIONS_CASE[1]) Suggestions: `PHP` URL: https://languagetool.org/insights/post/spelling-capital-letters/ Rule: https://community.languagetool.org/rule/show/FILE_EXTENSIONS_CASE?lang=en-US&subId=1 Category: CASING
Raw output
guides/plugins/plugins/framework/caching/index.md:104:3: File types are normally capitalized. (FILE_EXTENSIONS_CASE[1])
 Suggestions: `PHP`
 URL: https://languagetool.org/insights/post/spelling-capital-letters/ 
 Rule: https://community.languagetool.org/rule/show/FILE_EXTENSIONS_CASE?lang=en-US&subId=1
 Category: CASING
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

Check warning on line 123 in guides/plugins/plugins/framework/caching/index.md

View workflow job for this annotation

GitHub Actions / LanguageTool

[LanguageTool] guides/plugins/plugins/framework/caching/index.md#L123

File types are normally capitalized. (FILE_EXTENSIONS_CASE[1]) Suggestions: `PHP` URL: https://languagetool.org/insights/post/spelling-capital-letters/ Rule: https://community.languagetool.org/rule/show/FILE_EXTENSIONS_CASE?lang=en-US&subId=1 Category: CASING
Raw output
guides/plugins/plugins/framework/caching/index.md:123:3: File types are normally capitalized. (FILE_EXTENSIONS_CASE[1])
 Suggestions: `PHP`
 URL: https://languagetool.org/insights/post/spelling-capital-letters/ 
 Rule: https://community.languagetool.org/rule/show/FILE_EXTENSIONS_CASE?lang=en-US&subId=1
 Category: CASING
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
Expand All @@ -47,6 +169,9 @@
// 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;
}
}
```
Expand All @@ -64,21 +189,19 @@
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);
}
}
```
Expand Down