Updated: Comment #175
Drupal 8 uses "cache tags" to help determining whether a cached item is valid or not. If a piece of rendered HTML contains node 1 then the change of node 1 (tag:
node:1 will need to invalidate that piece of HTML -- but also the author of the node (
user:123) and any taxonomy terms on the node. On the other hand, changing node 1 also needs to invalidate the path alias cache for that etc -- so cache tag invalidations are not restricted to a single bin.
Currently, cache bins and cache tags are tightly coupled. In order to delete or invalidate cache items based on tags you need to do that via cache bin. Since there can be different cache backends for different bins one needs to be aware of that and delete/invalidate tags on both backends. Drupal 8 has had
Cache::invalidateTags() for many months now, which automatically iterates over all cache back-ends, but the problem is that it's still possible to only invalidate tags on a single cache back-end. It should not be possible at all.
The mismatch between the fact that cache tags are a global concept but every cache backend/bin must provide it's own implementation results in more complexity and duplicated and/or inconsistent code. Some backends then have a global implemenation like the database but are still called per-bin, so there is a lot of code to avoid invalidating cache tags multiple times in a row in the database backend, but the ApcuBackend does not do that. And the PhpBackend loads all files, checks them for cache tags and invalidates them.
The common way to implement cache tag based invalidation is to use checksums and compare them when loading cache entries. When they don't match, the cache item is invalid. However, some backends implement different concepts. The fast chained backend does not propagate cache tags to the fast backend but invalidates it in case a cache tag is invalidated as that must be distributed to other servers anyway. The memory backend can simply loop over all entries and invalidate them and a redis implementation can use server-side set operations to delete cache tags.
Two new concepts are introduces:
Cache tags invalidation storage/checksum provider
Currently named invalidation storage, but will probably be renamed to
CacheTagsChecksumProvider (cache_tags.checksums?), so that it is named after what it provides and not how it does it. Is responsible for providing the checksum for a set of tags, the default implementation is what was in DatabaseBackend, but cleaned up (no drupal_static() anymore) and simplified.
Cache tags invalidators
Services tagged with cache_tags_invalidator, they react to cache tag invalidations. There is a central service (currently cache_tags_invalidator, possibly cache_tags.invalidator) that notifies all other services. Those include the checksum provider, other use cases are varnish/nginx integrations that ban cached pages based on their cache tags, or a logger that tracks cache tag invalidations.
Additionally to explicitly registered services, all cache bins where the backend implementation implements the CacheTagsInvalidatorInterface are also notified about any invalidations, to support the mentioned use cases.
Backends that use the checksum implementation can just inject the central checksum provider and all have to do is is:
1. On cache set, get the current checksum for the provided cache tags and store them together with the list of cache tags.
2. On a cache get, get the current checksum for the stored tags and compare it, on mismatch, invalidate the cache item.
3. Notify the service about cache tags so that optimized implementations (like the one we have now) that do not repeatedly invalidate the same cache tag know that they have to increment the invalidations counter once more.
Right now, 1. and 2. are the same API, if we were to split that up, we could make 3. part of 1.
The following backends do that now: database, apcu, php.
Additionally, the confusing method "deleteTags()" is completely removed. It does not "delete tags", it just invalidates tags without the possibility to get the the stale cache item with $allow_invalid = TRUE. Only special cache backends could hope to have a better implementation that use the same as for cache tag invalidations, for others, it adds more complexity and developers do not understand when to use deleteTags() and invalidateTags()
- Agree on names for concepts/interfaces/class/service.
- Update documentation
User interface changes
- deleteTags() is completely removed
- invalidateTags() removed from CacheBackendInterface (although specific implementations may still have it)
- A new cache tags invalidator service, Cache::invalidateTags() still works but is now a simple wrapper around the service, so it is possible to inject this into services if they want. Cache::invalidateTags() will not be deprecated/removed in Drupal 8.
- A new cache tags storage/checksums service that cache backends can use.
[Original report removed, as it had nothing left in common with current goals of this issue, it was started *before* cache tags existed and as alternative implementation]
PASSED: [[SimpleTest]]: [PHP 5.4 MySQL] 83,390 pass(es). View
PASSED: [[SimpleTest]]: [PHP 5.4 MySQL] 83,361 pass(es). View