Understanding tags is the key to making the most of ezplatform-http-cache
.
They work in a similar way as persistence cache tags in eZ Platform v2:
- A set of secondary keys set on every cache item, on top of "primary key" which in this case is the URI
- Like an index in a database it is typically used for anything relevant that represent the given cache item
- Used for cache invalidation
It works across all supported proxies (see "drivers") by eZ Platform:
- Symfony Proxy (PHP based for single server usage, primarily for smaller web sites)
- Varnish with xkey module (0.10.2+) or Varnish Plus (High performance reverse proxy)
- Fastly (High performance reverse proxy, originally based on Varnish, worldwide as a CDN, driver available in eZ Platform Enterprise)
In order to support several repositories on one installation, tags will be prefixed by repository index in use. I.e. "0p1", where "0" is the repository prefix.
Varnish or Fastly are highly recommended for medium to large traffic needs. Besides being able to handle much more traffic, supported for use in cluster setup, they also both support soft purge (by tags), meaning they are able to serve stale content while it's refreshed in the background on-demand, leading to more stable load on your backend.
-
Content:
c<content-id>
: Used on anything that is affected by changes to content, on content itself as well as location and so on. -
Content Version:
cv<content-id>
: Used for clearing cache for content version list views, when not affecting the published content. -
Content Type:
ct<content-type-id>
: For use when content type changes affecting content of its type. -
Location:
l<location-id>
: Used for clearing all cache relevant for a given location. -
Parent Location:
For responses this represent the parent location, on purges it's used in 2 distinct ways:
pl<parent-location-id>
: Used for clearing cache of all siblings of an location.
pl<location-id>
: Used for clearing cache of all the children of a location. -
Path (all path elements of a location):
p<location-id>
: For operations that change the tree itself, like move/remove/(..). -
Relations:
r<content-id>
&rl<location-id>
: For use when updates affect all their reverse relations. ezplatform-http-cache does not add this tag to responses automatically, just purges on it if present, response tagging with this is currently done inline in template logic / views where relation is actually used for rendering (when using ESI, if inline the Content own tags should be added to response instead, unless you are hitting tag header limits). These differs fromcontent-
andlocation-
by only being purged when relation itself is removed or otherwise affected._
s<section-id>
: For use when section changes affecting section responses (i.e. REST).
-
t<content-type-id>
: For use when content type changes affecting content type responses (i.e. REST). -
tg<content-type-id>
: For use when content type group changes affecting content type group responses (i.e. REST).
-
ez-user-context-hash
: _Internal tag used for tagging /fos_user_context_hash to expire it on role & role assigment changes. -
ez-invalidate-token
: Internal tag for use by token lookup when in token authentication mode (for setups where IP validation is not possible). -
ez-all
: Internal tag used for being able to clear all cache. Main use case is being able to expire (soft purge) all cache on deployment of new versions of your installation which for instance changes representation / design dramatically.
For Content View there is a dedicated response listener HttpCacheResponseSubscriber
that triggers a set of Response
taggers responsible for translating info from the objects involved in generating the view to
corresponding tags as listed above. These can be found in src/ResponseTagger
.
For custom or eZ controllers (like REST at the time of writing) still using X-Location-Id
, a dedicated response
listener XLocationIdResponseSubscriber
handles translating this to tags so the cache can be properly invalidated by
this bundle. It supports comma separated location id values which was only partially supported in earlier versions:
/** @var \Symfony\Component\HttpFoundation\Response $response */
$response->headers->set('X-Location-Id', 123);
// Alternatively using several location id values, requires ezplatform-http-cache to work across all supported proxies
$response->headers->set('X-Location-Id', '123,212,42');
NOTE: This is currently marked as Deprecated, and for rendering eZ content it is thus adviced to refactor to use Content View. For other needs there is an FOS tag handler for Twig and PHP that can be used, see below for further info.
For custom needs, including template logic for eZ content relations which is here used for examples, there are two ways to tag your responses.
For twig usage, you can make sure response is tagged correctly by using the following twig operator in your template:
{{ fos_httpcache_tag('r33') }}
{# Or using array for several values #}
{{ fos_httpcache_tag(['r33', 'r44']) }}
See: http://foshttpcachebundle.readthedocs.io/en/1.3/features/tagging.html#tagging-from-twig-templates
However for relations, which you typically used prior to a ESI include for some content, rather use one of:
{# As of v0.9.3 two twig functions for relation use cases was added, both handling single and array values #}
{# First one is for relation(s) for Content, as shown by it's id #}
{{ ez_http_tag_relation_ids(relation_content.id) }}
{# Second one for relation locations, here shown using array of location id's #}
{{ ez_http_tag_relation_location_ids(relation_location_ids) }}
Alternatively if you have a location(s) that you render inline & want invalidated on any kind of change:
{{ ez_http_tag_location( location ) }}
TIP: Don't use ez_http_tag_location
when you are rendering a large amount of content/location items, it will cause tag
header to become to large. Consider using less tags with for instance ez_http_tag_relation_(location_)ids
, and account for
possible stale cache by reducing cache ttl for the given response.
Also strongly consider to upgrade to ezplatform-http-cache 1.0 or higher which reduces cache tag size.
For PHP usage, a few options exist (autowirable classes of '@fos_http_cache.handler.tag_handler'):
/**
* Using low level response tagger to add tags manually.
*
* @var \FOS\HttpCache\ResponseTagger $responseTagger
*/
$responseTagger->addTags(['r33', 'r44']);
/**
* Better option if you need to add Ibexa specific tags.
*
* @var \EzSystems\PlatformHttpCacheBundle\Handler\ContentTagInterface $responseTagger
*/
$responseTagger->addRelationTags([33, 44]);
See: http://foshttpcachebundle.readthedocs.io/en/1.3/features/tagging.html#tagging-from-code
This bundle uses Repository API Slots to listen to Signals emitted on repository operations, and depending on the operation triggers expiry on a specific tag or set of tags.
E.g. on Move Location signal the following tags will be purged:
use EzSystems\PlatformHttpCacheBundle\Handler\ContentTagInterface;
/**
* @param \eZ\Publish\Core\SignalSlot\Signal\LocationService\MoveSubtreeSignal $signal
*/
protected function generateTags(Signal $signal)
{
return [
// The tree itself being moved (all children will have this tag)
ContentTagInterface::PATH_PREFIX . $signal->locationId,
// old parent
ContentTagInterface::LOCATION_PREFIX . $signal->oldParentLocationId,
// old siblings
ContentTagInterface::PARENT_LOCATION_PREFIX . $signal->oldParentLocationId,
// new parent
ContentTagInterface::LOCATION_PREFIX . $signal->newParentLocationId,
// new siblings
ContentTagInterface::PARENT_LOCATION_PREFIX . $signal->newParentLocationId,
];
}
All slots can be found in src/SignalSlot
Given a header like Xkey: 0c22 0ct2 (...)
, the following can be understood:
Xkey:
Configured tag header, by this bundle config. Understood by Varnish (with xkey module), and by our enhanced version of Symfony HttpCache Proxy "AppCache".0..
: Current repository index, set for you by this bundle. Done in order to support multi repository setups, it's the array key of the current repository is used (and not the full identifier)...c22
: Content id 22..ct2
: Content Type id 2
Even if the tags are kept as short as possible, you might still encounter issue with tag header exceeding limits in Varnis/Apache/Nginx/Fastly, stopping either caching or invalidation from working as expected.
For handling these cases it's simplest to increase the limits on the service side if possible, and if not look at the code involved to see if amount of tags can be reduced.
Varnish config:
- http_resp_hdr_len (default 8k, change to i.e. 32k)
- http_max_hdr (default 64, change to i.e. 128)
- http_resp_size (default 23k, change to i.e. 96k)
- workspace_backend (default 64k, change to i.e. 128k)
Fastly has a Surrogate-Key
header limit of 16kb, this can not be changed.
Apache has a hard coded limit of 8kb, so if you face this issue consider using nginx instead.
Nginx has a default limit of 4k/8k when buffering responses from PHP-fpm/fast-cgi:
- https://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_buffer_size
- https://nginx.org/en/docs/http/ngx_http_fastcgi_module.html#fastcgi_buffer_size
Typical case with too many tags would be when inline rendering a lot of embed content object. Normally the system will add all the tags for this content, to handle every possible scenario of updates to them.
So if you embed hundreds of content on the same page (i.e. in richtext, using relations, or using page builder), it will explode the tag usage.
However if for instance you just display the content name, image attribute, and/or link, then it would be enough to:
- Just use
r<id>
tag, or preferably the abstractions for it. - Optionally: Set reduced cache TTL for the given view in order to reduce remote risk of subtree operations affecting the cached page without correctly purging the view.
If that is not an option, you can opt-in to set a max length parameter (in bytes) and corresponding ttl (in seconds):
parameters:
# Warning, setting this means you risk losing tag information, risking stale cache. Here set below 8k:
ezplatform.http_cache.tags.header_max_length: 7900
# In order to reduce risk of stale cache issues, you should set a lower TTL here then globally (here set as 2h)
ezplatform.http_cache.tags.header_reduced_ttl: 7200