Updated: Comment #175
Problem/Motivation
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.
Proposed resolution
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()
Remaining tasks
- Agree on names for concepts/interfaces/class/service.
- Update documentation
- Reviews
User interface changes
-
API 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]
Comment | File | Size | Author |
---|---|---|---|
#213 | cache-tags-918538-213-interdiff.txt | 1.22 KB | Berdir |
#213 | cache-tags-918538-213.patch | 121.03 KB | Berdir |
#205 | cache-tags-918538-205-interdiff.txt | 1.28 KB | Berdir |
#205 | cache-tags-918538-205.patch | 120.86 KB | Berdir |
#201 | cache-tags-918538-201-interdiff.txt | 664 bytes | Berdir |
Comments
Comment #2
sunThe installer is really the last thing I care for here.
Comment #4
tobiasbComment #6
Damien Tournoud CreditAttribution: Damien Tournoud commentedI'm reluctant to add a dependency between the cache system and the module system, which will quickly be circular thanks to the various caches leveraged by the module system.
Can you elaborate on the use case?
Comment #7
sunFirst example: There are modules (like Mollom module) that invoke info hooks in other modules to collect available data of them, which may depend on information that is cached. Now, the collected and processed data may be cached by the module on its own, which quickly leads to a stale cache.
Taking #898360: Cache node types as a simple example, I'd like to be able to just do:
Of course, Mollom would probably rather expose that cache clear condition in its own info hook, so it does not hard-code some special caches, but anyway, doesn't matter for this issue.
Second example: Administration menu module implements client-side caching, but unless menu_rebuild() is invoked to entirely rebuild the menu, it's close to impossible to take all hooks and forms into account that should lead to an invalidation of the client-side cache. For instance, whenever 'cache_views' is cleared, a client should retrieve a new copy of the menu.
Comment #8
Damien Tournoud CreditAttribution: Damien Tournoud commentedReassigning.
Comment #9
sunDo the use-cases I outlined in #7 make sense? Or am I completely nuts? As explained in #924616: Make database cache leverage static cache by default, I'm really struggling very hard to make any sense of the caching nightmare we introduced for D7.
Comment #10
sunAlthough badly needed, this is D8 material according to the rules (I had to learn today). It may be backported at a later point in time (though that's unlikely).
Comment #11
carlos8f CreditAttribution: carlos8f commentedsubscribing
Comment #12
Anonymous (not verified) CreditAttribution: Anonymous commentedsubscribing
Comment #13
xjmTagging issues not yet using summary template.
Comment #14
catchI'm demoting this to normal, although I think it is probably won't fix for D8 (at least I hope we can get cache tags in and I plan to work on that). Introducing a hook in the cache system would introduce another circular dependency.
Comment #15
sunThe only way to resolve this issue without a hook would be to ditch the info hook pattern entirely.
It is the info hook pattern that allows modules to "register" arbitrary new stuff and drop old stuff without any kind of notice.
There's no way for another module that implements its own caching to track these registration, configuration and data changes in a reliably way, except for attempting to listen to cache clears currently.
To clarify what that would mean in code for one of the examples in #7:
The essential change: There is no hook_node_info() and also no hook_forms() anymore. There is also no hook_entity_info() anymore.
Somewhat related to this is #597280: Introduce form registry to fix various problems and increase security, in which I already proposed to introduce a proper form registry. But that still assumed an info hook based approach; whereas to also resolve this issue, it would have to be a CRUD based approach.
Comment #16
sunWon't fix based on that argument...
Although I still don't have any idea how to resolve my use-cases without it.
A clarification on the second use-case might help to understand the scenario better:
For example, whenever the Views cache is cleared, this may mean that a view was created, edited, or deleted. The view itself does not have a menu link. But admin_menu exposes a direct link to it in order to access it faster. Thus, its cache needs to be flushed whenever any of the dynamic items in the menu may have changed.
The immediate response to that is: Listen to CUD hooks instead of cache hooks.
However, admin_menu tries to provide a simple way for other modules to integrate with it; i.e., a single hook. Thus, asking for CRUD involvement immediately means a step from 1 hook to 4 hooks, as it either means:
or respectively, it means to put module-specific code into other modules:
Thus, the most simple solution, without enforcing additional functions, would be to enhance the already existing info-alike hook to additionally return cache IDs that are sort-of treated like "triggers"; e.g.:
Comment #17
Anonymous (not verified) CreditAttribution: Anonymous commentedsun,
would #636454: Cache tag support solve this issue in your use case?
Comment #18
sunCache tags would only really make a difference if a tag would be invalidated across all known cache bins.
However, that's not the case currently.
If that was the case, then it sounds like it might resolve the problem, but I'm not 100% sure. If I get it right, then admin_menu would have to add all the cumulative cache tags from all affected data structures to its own cache items. Thus, if any of the tags was invalidated, admin_menu's affected cache items would be invalidated, too.
But to make that possible, cache tags and their invalidation counts would have to be managed in a cache bin-agnostic storage, which would inherently mean that cache tags would have to be their own key/value store service.
...something like that.
Comment #19
sunTrying to resurrect this. And clarifying the issue title.
By now, cache tags turned out to be the answer for invalidating caches in a multidimensional modular system. This allows modules to do two discrete things:
1) Cache data that depends on or is related to some other data, by adding/using the same tags that the original/originating module is using for its own cache entries.
2) Cache an aggregate of other data and auto-invalidate it when required, by cumulating all relevant cache tags for the aggregate cache item.
I'm very grateful for them to exist.
The only remaining problem right now is that cache tags are still bound to cache bins. (At minimum, storage-wise.)
I.e., as a module, I'm using cache bin X. But:
i) The data of the original/other module is potentially cached in another bin.
ii) The other bin may not use the same storage as mine.
iii) I only know about my own cache bin.
iv) Tagged cache items are only invalidated/deleted within a single cache bin.
In turn, what I'd like to see to happen for D8 is to decouple the cache_tags bin from the "actual" cache bins, so the cache tags are managed across all cache bins. The storage of cache_tags can still be swappable and thus be replaced with something else than SQL. The only part that matters is that there is a single + reliable list of tags that is central for all cache bins.
So, e.g., if I do
cache()->invalidateTags(array('filter_formats' => TRUE));
then I should not have to worry about the actual cache bins that might be affected. Instead, the expectation is that invalidating or deleting a tag has an impact on all currently actively/registered cache bins/backends.As a result, I think we should move the cache tags functionality from CacheBackendInterface to the CacheFactory.
What do you think?
Comment #20
carlos8f CreditAttribution: carlos8f commentedIn my original implementation there was a new class for cache tags which was not coupled with bins, and I made a point of being able to invalidate across all bins, with pluggable (non-bin) storage. I'm not sure why that got shot down. I quit Drupal a while back partly because I got tired of the epic ego battles necessary to get anything done.
If it helps at all, here's the original:
http://drupal.org/node/636454#comment-5157150
Comment #21
catchHaving a single cache tag manager across all bins makes it harder to write an implementation that clears tags directly rather than using checksum (i.e. MongoDB could do that efficiently enough). However the current situation isn't great either so maybe we should just switch to that.
I'm confused by this issue though since tag invalidation already happens for all cache backends: http://api.drupal.org/api/drupal/core%21includes%21cache.inc/function/ca...
Comment #22
catchOK I think I get what sun is after, it's just that this misses the current implementation in the SQL backend already works across all bins.
Trying to remember the various arguments from the original issue:
- the current architecture where the tags are coupled to the bin is bad. What we currently do in the SQL backend is make the tags coupled to the backend rather than the bins via using a single table for invalidation and static class properties, but it's messy.
- switching from that to a single location for all cache bins looks much better architecturally, however limiting a site to a single cache tags implementation with potentially multiple cache implementations has real practical limitations:
- Cache tags can be implemented in Varnish using custom headers - this means that clearing the page (or blocks if they're cached with ESI) cache bin would make a call to the varnish backend to clear the tags via regexp. We need to allow for that implementation to co-exist with database/memcache/redis for everything else.
- Some cache backends such as Redis or MongoDB are able to store cache tags with the cache objects and clear them with a DELETE query efficiently, as opposed to relying on the checksum implementation that core ships with. Contrib backends should be free to experiment with the implementation and site admins will still want to mix and match backends in some cases (say APC for cache_bootstrap and Redis for everything else).
So...
What about if we just allow for multiple cache tag implementations - a separate interface to the backends, and each one is fired on every tag clear? Then if you have database + varnish both get invoked and there's no problem compared to now, but we get rid of the bin coupling.
The only issue then is that we have a cache interface which allows tags to be set, but doesn't allow them to be cleared, so we'll need to document the interdependency of the two somewhere, but that's not such a horrible problem to have.
Comment #23
catchAnd bumping to major, this is quite confusing https://drupal.org/node/1971158#comment-7618133
Will be an API change but 1. People should only call Cache::deleteTags() anyway, otherwise there's a bug 2. The real API change is for cache backend authors and there's only a handful of those (and it'll make it easier rather than harder for them).
Comment #24
damiankloip CreditAttribution: damiankloip commentedOk, here is a start on this. I've added:
- A CacheTagInterface containing invalidateTags and deleteTags methods. We might want to add something for actually getting whether a tag is invalidted or not but I haven't got there yet (the db cache bin currently just calls checksumTags() on the cache tag service atm).
- A CacheTagBase class, Mainly for the flattenTags method at this stage. We might want to just add this to our interface too? I think most implementations can share the current flattenTags, but should be on the interface I think.
- A DatabaseTag service; this basically just moved the invalidate/deleteTag logic from the DatabaseBackend to here.
Comment #26
catchAdding a method to check if tags are valid seems sensible - for some cache tag implementation that invalidate tags directly it'll be a null op so would have to always return TRUE (if there were mixed backends in use on a site) but I don't see a way around that.
We should have a factory similar to cache backends and pass that to the database cache backend as an argument rather than the specific database service. There's no hard dependency between the database cache + tags - you could use database tags with memcache cache, or memcache tags with database cache (not that you'd want to, but it'd work). For the varnish use case it'll be necessary to have two tags implementations running simultaneously - one for the 'real' cache and one for Varnish. If it's simple to have multiple tags services all called we could do that here, but potentially contrib could implement a chained backend that combines two or more as well.
Comment #27
damiankloip CreditAttribution: damiankloip commentedAdded the simplest factory I think is possible.
Not sure how we want to handle tests though? The generic cache backend test obviously tests tag logic, should we just keep that there? add unit tests only for the new tag classes?
Comment #29
damiankloip CreditAttribution: damiankloip commented#27: 918538-27.patch queued for re-testing.
Comment #31
damiankloip CreditAttribution: damiankloip commented#27: 918538-27.patch queued for re-testing.
Comment #33
damiankloip CreditAttribution: damiankloip commentedTrying to change the Cache class, let's see how this goes.
Comment #35
slashrsm CreditAttribution: slashrsm commentedRe-roll. It installs OK locally. Let's see what bot thinks about that and proceed from there.
Comment #37
slashrsm CreditAttribution: slashrsm commentedIt looks that change in Cache::deleteTags() and Cache:invalidateTags() causes this fails. I'm guessing that it is related to MemoryBackend, which implements tags in the backend class instead of using separate cache tags service.
For now I just reverted Cache::deleteTags() and Cache::invalidateTags() as it seem to work. I was thinking about implementing MemoryTag service, but I was unable to do it with current implementation. Since cache items are stored in MemoryBackend class tags service would need to get access to it. This results in a circular dependency. We could decouple "memory cache storage" in a new service which would be accessed by both cache bachend service and tags service. This seemed a bit of overengineering to me and I didn't go with that solution.
Comment #39
damiankloip CreditAttribution: damiankloip commentedYeah, this is the issue. Me and catch have spoken about this a couple of times. This issue is that we have cache tags that invalidate and cache tags, like memory that just remove the item directly.
I have a couple of ideas around this which I will do when I have time.
Either way, I think reverting the cache class Stuff in #37 doesn't really help us I'm afraid. As we just need to fix the current implementation anyway. Getting a patch green doesn't fix our problem :)
Comment #40
slashrsm CreditAttribution: slashrsm commented@damiankloip: Isn't it the same whether we clear tags via bins or directly via cache tags? The result should be the same, right? What do you think about the MemoryBackend change I proposed in #37? What other solutions do you see here?
Anyway... I found the source of that fail in #37. There are two drupal_static_reset() calls in test classes, which are clearing statically cached tags. Since we changed that with a variable on class it was not working correctly any more. I was trying to implement clear function on class itself and call it on the same places via service container, but it was still failing. Reverting back to drupal_static, just to confirm it really fixes this problem. Hopefully someone with better understanding of testing framework figures out a better solution.
This would fail if we'd revert in #37 or not, as the problem stays there in both implementations.
Comment #42
damiankloip CreditAttribution: damiankloip commentedYes, this is why we need a slightly different/better solution for this, this stuff I had so far is not really adequate. I think we can maybe modify the memory backend slightly instead by injecting a new memory tag service (still not sure about this though). We really need to pass the cache item (I think) to the tag service to also get a chance to prepare the item. So some of that logic will just move there.
No, the reason I was saying about the reverting back to looping through cache bins is that this is one of the main things we want to get away from (Same goes for the drupal_static_reset() that was added back in). So the fact that it does not pass either way doesn't matter really, as the whole patch needs a make over. So what we had before was a better base to work from IMO, as we'll just need to revert the reverted changes anyway...
#37 is potentially a way out, how about we just use the key value store for that? So we could inject the keyvalue factory for tests, and this could be used as this decoupled storage mechanism? Then the memory cache tag service would get passed the key value store too.
Comment #44
catchBumping this to critical. Right now we have a method on the cache backend interface which is deleteTags(), but you should never call that directly. Code that does is not clearing the cache tag, but only for the bin (which might or might not delete it), since it's an API change needs to happen before release. Don't think it blocks beta.
Comment #45
slashrsm CreditAttribution: slashrsm commentedWorking on this...
Comment #46
damiankloip CreditAttribution: damiankloip commented@slashrsm, any joy with this?
Comment #47
slashrsm CreditAttribution: slashrsm commentedSome joy and some pain. I've achieved some progress (created MemoryTag, implemented prepare function approach for cache items, ...).
I'm still trying to figure out how to fix installation problems, that were there in #35. I'm currently debugging my way through it.
Comment #48
slashrsm CreditAttribution: slashrsm commentedThis is based on #35. Added MemoryTag service and cache item prepareGet() function that is responsible for tags-related validation of cache item.
Comment #50
slashrsm CreditAttribution: slashrsm commentedLet's try again....
Comment #53
slashrsm CreditAttribution: slashrsm commented50: 918538_50.patch queued for re-testing.
Comment #55
slashrsm CreditAttribution: slashrsm commentedFixed some failing tests. I removed tests that check deleteTags() and invalidateTags() on chain backend as cache backends don't implement those any more. We will need to add some tags test coverage anyway, which should cover that also. I'd be happy to write those tests, but first I'd like to see existing tests passing and some level of consensus about this patch.
Comment #57
slashrsm CreditAttribution: slashrsm commentedReroll after #2194273: Avoid repeated cache tag deletions.
Comment #58
slashrsm CreditAttribution: slashrsm commentedForgot to attach file.
Comment #59
Wim LeersIs this a straight reroll, or did you make functional changes?
Comment #62
slashrsm CreditAttribution: slashrsm commentedNo real functional changes. Only moved dupl. tag deleted tracking from cache backed service to tag service.
Comment #63
slashrsm CreditAttribution: slashrsm commentedComment #64
BerdirLooking at the test fails, it looks like all DUBT tests are broken which use the memory cache backend.
Comment #65
slashrsm CreditAttribution: slashrsm commentedComment #68
slashrsm CreditAttribution: slashrsm commented@Berdir: DUBT stands for?
Comment #69
tim.plunketthttp://api.drupal.org/api/search/8/DrupalUnitTestBase
Comment #70
slashrsm CreditAttribution: slashrsm commentedWith some other tests I saw problem with two instances of MemoryTag class being initiated. This way some tag delete requests went to one and others to the other. This caused cache items not being correctly invalidated/deleted. I am suspecting same problem also with this tests, but I was not able to find the actual source of the problem.
Does this seems to be reasonable explanation? Any idea how to debug/fix it?
Comment #71
slashrsm CreditAttribution: slashrsm commentedComment #72
Damien Tournoud CreditAttribution: Damien Tournoud commentedI understand what this is after (making it more efficient to clear tags across bins), but the current implementation doesn't make much sense to me.
It is clear from the code and the interfaces that bins and tags are strongly bound together. Arbitrarily splitting them doesn't make much sense.
What would make sense, on the other hand is to split bins and *backend*. When you ask for a cache bin, you currently get a CacheBackendInterface... and that clearly doesn't make much sense.
Comment #73
Damien Tournoud CreditAttribution: Damien Tournoud commentedActually, there is already some sense of a weird separation between backend and bin, it's the
CacheFactoryInterface
. So one easy way to move this patch forward is to just:CacheBackendInterface
intoCacheBinInterface
CacheBackendInterface
extendsCacheFactory
, and move thedeleteTags
andinvalidateTags
methods theredeleteTags
andinvalidateTags
to their corresponding factory classes (ie.CacheBackendFactory
,MemoryBackendFactory
)BackendChainFactory
, given the current state of the code, it doesn't seem like the backend chain is actually usable because it is missing a factoryComment #75
slashrsm CreditAttribution: slashrsm commentedI can understand arguments that were pointed out in #72, but I still thing it would make sense to have tags storage and bin storage separated.
Posting another patch to see if I can get it closer to green.
Comment #77
slashrsm CreditAttribution: slashrsm commentedComment #78
slashrsm CreditAttribution: slashrsm commentedComment #79
Berdir77: 918538_76.patch queued for re-testing.
Comment #81
Wim LeersThis has been brought up in many discussions, and I think it's relevant here: cache tag invalidation/deletion should be triggered not via method invocation, but via events. Having read the issue summary and the last few comments, it sounds like that could help with untangling architectural problems here.
Comment #82
damiankloip CreditAttribution: damiankloip commentedBut to be able to do that, we need to untangle this mess first. It is more a case of being able to clear tags and not have the whole static tags mess on the bins, which is actually the same thing but present in all these cache bin instances. That is basically a higher level than this is tackling IMO. Also from an API level, people are not sure (and frequently get wrong) what the difference is between Cache::deleteTags and $cache_bin->deleteTags(). Here we need to decouple the logic and functionality as much as we can to manage that.
How we actually implement tag clearing is another discussion I think.
Comment #83
BerdirI added a @todo to #2216543: Fill in topic/@defgroup docs for Cache overview to update the cache tag related documentation in here, I hope that will land asap (reviews welcome). The cache API really deserves some accurate and up to date documentation :)
Comment #84
Wim LeersOk, was just trying to help :)
Comment #85
damiankloip CreditAttribution: damiankloip commentedok, thanks :) It is certainly some I would like to see. Hopefully this can happen in this release cycle!
Comment #86
slashrsm CreditAttribution: slashrsm commentedReroll.
Comment #88
BerdirMissing use.
Comment #90
slashrsm CreditAttribution: slashrsm commentedComment #92
Wim LeersThis is at least a beta target, potentially a beta blocker.
Comment #93
martin107 CreditAttribution: martin107 commentedComment #94
BerdirWorking on a re-roll, which is going to take some time, so assigning to me.
Comment #95
BerdirInitial re-roll, fixed unit tests and tested the installer with drush, let's see how this goes.
Comment #97
Berdir95: 918538_95.patch queued for re-testing.
Comment #99
BerdirJust trying to get the installer to pass, have some ideas to simplify this.
Comment #101
BerdirWorking on clean-up and simplifying things. Only one cache tag backend is now supported, so removed the factory and directly registering the service, removed the cache.tags tag, directly calling deleteTags() and invalidateTags() on the service now. Based on what @slashrsm said, this might result in same new test fails but those will hopefully be fixable.
Also fixed some tests.
Comment #103
BerdirUps, this will hopefully work a bit better, some more clean-up, removed drupal_static and cleaned up usage of cache.tag.database.
Comment #105
damiankloip CreditAttribution: damiankloip commentedThanks for picking this up again - totally forgot about this issue!
I think we were using a single cache tag implementation way back. I just don't think this is an option though :/ Passing the DB tag implementation into all backends...
Comment #106
BerdirThis is exactly what the patch already did, the factory wasn't a factory, it was just a service locator for another service that was globally configured and passed the same implementation into all cache backends.
Yeah, I'm not 100% sure this will work, but the ability to provide an optimized cache tag implementation for your specific backend is more or less gone anyway when you get another service injected with a common API. I'll ask @znerol, he's working on a Redis cache backend implementation, which is one that is able to do better.
Now web tests should basically pass again.
Comment #107
BerdirThe cache tag factory that this issue had was certainly flawed, that didn't make sense.
What *might* be an option is to limit the cache tag interface to deleting and invalidating cache tags (and clearCache for static caches), then each cache backend is free to implement the actual checking and implementation they use however they want (they could even keep it in the same class and only service-tag one cache backend, for example). They would all have to provide their own implementation and extend it in whatever way they see fit for their specific backend.
Comment #108
znerol CreditAttribution: znerol commentedI feel that we first should explore the ideas from #72. I think that the tight coupling of tag and backend implementation is not much of a problem, but the lack of separation between cache-bin and cache-backend.
Comment #110
BerdirMy suggestion is actually very similar to that, but it's not enough, not with the database cache backend implementation that we have now with the deletion/invalidation optimizations, cache backends need to tell the whatever class contains deletedTags/invalidatedTags arrays if they write something with it and I do not want to keep the current drupal_static() approach ;)
Comment #111
znerol CreditAttribution: znerol commentedIndeed. So the generic approach seems sane to me. I think we may want to introduce a
cache_tag
service capable of dispatching the calls to the appropriate backend at some point.It will not be a surprise for you that I do not like this. The checksuming is an implementation detail. In fact the implementation in #2233413: Port Redis to Drupal 8 does not use tag-checksums but server-side set operations. I have plans to extend it even further and only use server-side lua-script for the more complex operations. What about introducing
CacheTagBackendInterface::prepareSet($item)
, this also would correspond better withCacheTagBackendInterface::prepareGet($item)
?Comment #112
BerdirYeah, the suggested approach would remove flattenTags, prepareItem() and checksumTags() from the public interface completely. The database implementation could then keep using that internally but others are free to do it differently.
Comment #113
catch#111 sounds great.
Comment #114
damiankloip CreditAttribution: damiankloip commentedGoing to do some work on this one.
Comment #115
slashrsm CreditAttribution: slashrsm commentedDiscussed this with @Berdir last week and finally got some time to look into this. I really like the direction we're going and really hope we can push this forward.
Comment #116
andypost@slashrsm Please update summary with #111 and what you discuss with @Berdir
Comment #117
slashrsm CreditAttribution: slashrsm commentedIt turns out summary already describes cache tag service approach.
Comment #118
Wim LeersThe IS really needs to be updated, because this hasn't been true in a long, long time:
In HEAD, you call
Cache::invalidateTags()
, not$cache_bin->invalidateTags()
.Cache::invalidateTags()
iterates over all cache bins.This does mean that it's still possible to not use
Cache::invalidateTags()
and just call$cache_bin->invalidateTags()
directly, and that is definitely problematic, because it can easily cause very hard to debug problems.Comment #119
slashrsm CreditAttribution: slashrsm commentedI was referring to the approach discussed in last set of comments not the other things.
Comment #120
BerdirJust a note that despite what I said earlier, I actually found a perfectly valid reason for having multiple listeners/handlers/whatever on cache tag deletions/invalidations: Sending them to varnish.
I was able to do that pretty easily with the current API, by registering an arbitrary service that is tagged with cache.bin. That is then called automatically and is a null/no-op backend except deleteTags() and invalidateTags(), which send a http BAN request to varnish.
Whatever we end up doing here, we need to be able to support something like that.
Comment #121
catchTagging this with D8 upgrade path. Not because we'll need an upgrade path, but it's going to at least require a rebuild.php after updating the code base due to service definitions being different.
Comment #122
chx CreditAttribution: chx commentedComment #123
ksenzeeWorking on this for now.
Comment #124
catchWe only need to interact with services implementing CacheTagInvalidationInterface generically.
The database cache backend can inject the database implementation of CacheTagInvalidationStorageInterface as part of the services.yml definition. We never need to call all CacheTagInvalidationStorageInterface at once since the return values of those should be identical.
Otherwise issue summary looks spot on now.
Comment #125
damiankloip CreditAttribution: damiankloip commentedksenzee, is anything going on with this?
Comment #126
Fabianx CreditAttribution: Fabianx commentedksenzee has working code in a sandbox. The approach looks good so far.
Comment #127
damiankloip CreditAttribution: damiankloip commentedDo we have any links etc... to this sandbox? A patch here would be cool too :)
Comment #128
Fabianx CreditAttribution: Fabianx commentedhttp://cgit.drupalcode.org/sandbox-ksenzee-1134904/log/ is the sandbox :).
Comment #129
xjmComment #130
catchComment #131
BerdirWorking a bit on this.
Comment #132
BerdirOk, rerolled what @ksenzee was working on and copied the old cache tag implementation for the database backend, this installs again for me with that. (Did not without that).
Here's a patch for that, still needs a lot of work, obviously.
Comment #134
BerdirSmall steps, tags storage backend really needs both interfaces, database factory does not need the invalidation. Did not touch the other backends yet.
Comment #136
BerdirNow the statics were different, trying again. Will have to change, but I honestly don't know how yet, given the implementation here.
Comment #138
BerdirFixing the unit test fatals to get more test feedback.
Still very unsure about a lot of stuff.
Comment #140
BerdirI might need to switch to a test issue soon.
Comment #142
BerdirUpdated the crazy drupal statics (they will go away, but first I want to make it pass) everywhere and fixed some kernel tests
Comment #144
BerdirThis might be green....
Which doesn't mean it's ready for review, but now I can start refactoring the internals for the database cache backend and tags storage.
Still many open questions, but I might be getting there...
Comment #146
BerdirThis should fix the cache tests, real work starting now :)
Comment #147
BerdirNow with 100% more patches.
Comment #148
Berdirdrupal_static()'s, begone!
Comment #149
Wim LeersClarification in the IS.
Comment #150
Wim LeersI think this tag is not descriptive enough, makes it harder to understand this patch than necessary.
Perhaps something like
cache_tags_invalidator
would be better?These are the services tagged with
cache_tags
.Isn't it a problem that this still keeps a (public)
invalidateTags()
method onCacheBackendInterface
? Why doesCacheBackendInterface
even have to implementCacheTagInvalidationInterface
? Why can't it just let the cache back-end factories take care of this? Because an individual cache back-end implementation might react differently? If that's why, then I think we need to rename the methods. In fact, I think we need to rename them anyway.This says that implementations of this method react to changes. But the method names pretend they make the changes:
What about this instead?
Nice :)
Do we want to get rid of
deleteTags()
and keep onlyinvalidateTags()
in this issue as well? It'd make the patch more elegant/easier to review also IMHO, because currently this:makes "tags deleted" sound like something that doesn't belong on that interface at all.
Comment #151
catchAgreed on removing cache tag deletions.
It adds a lot of complexity to the implementation - have to keep track of two things (or ignore invalidations and just delete).
Also it's up to code getting cache items whether they use $allow_invalid or not, when invalidating tags it's either valid or not.
I committed the patch that added this, but didn't properly think it through at the time.
Comment #152
BerdirThis removes deleteTags(), @catch, @WimLeers, @Fabianx and myself all agree that there is no use case for this. Further work should be easier with that gone, so doing it here, as we have only half the amount of methods to write/test/rewrite and simpler API's.
Comment #153
Wim LeersAs of #151/#152, this now also greatly enhances DX, by removing the extremely confusing difference between "deleting tags" and "invalidating tags".
Yay!
The #152 interdiff looks awesome.
Now that we have that, it's time to start cleaning this patch up. Berdir and I discussed my review in #150, he's going to use a more meaningful service tag, going to remove the need for
CacheBackendInterface
implementations to still implement aninvalidateTags()
method (but still keep that possibility open for the "special snowflake" cache back-ends that need it, likeChainedFastBackend
andMemoryBackend
). That should make this patch a lot cleaner and easier to review.Marking NW for that :)
Comment #154
BerdirOk....
A few changes :)
- Renamed CacheTagHandler to CacheTagsInvalidator (+ interface)
- Removed the null implementation and install override for that
- Removed CacheTagInvalidationInterface, now using the above interface also for the listeners. This needs to be discussed, we might want to have a different name of the two methods. I quite like it, only one interface means you can't accidentally implement the wrong one.
- Changed the Invalidator to also call the method on all bins that implement the interface
- Removed it from all backends that don't need it
- Using the cache tag storage in the apcu and php backends
- Updated unit and generic cache backend tests
- Moved the cache tag invalidation into the Invalidator, moved the unit test for it
Go testbot
Comment #156
BerdirOk, so we need a null implementation but only when there is no settings.php already.
I decided to make a null implementation of the tags storage instead, that makes a bit more sense to me.
Comment #157
Wim LeersWow, huge progress! Will review.
Comment #159
BerdirActually, we don't need the null implementation, we can just be sneaky and remove the tag. The failing tests gave me that idea, as we didn't even register the null implementation. Fixed that now, also fixed two invalidateTags() calls that still called the backend directly.
Comment #160
Wim LeersThe patch is looking better and better :)
string[]
(It's correct everywhere else.)
s/validator/invalidators/
Extraneous newline.
@return statement needs a trailing "[]" to indciate it's an array.
The doc line needs one extra leading space.
Needs to be fixed still (I know this is just temporary, to see if the overall approach is viable).
s/The object that stores/Storage/
Perhaps it makes sense to rename 'checksum_invalidations' to just 'checksum' or just 'invalidations'?
80 col rule.
s/table/tables/
Missing leading backslash.
DatabaseCacheTagsStorage
Copy/pasted comment is wrong.
s#request/#request#
80 cols.
Comment seems in the wrong place too?
Not related to "the" cache bin, but to all cache bins?
Needs newline in between.
s/entry/item/
This doesn't seem to belong in this patch? :)
s/Delete or invalidate/Invalidate/
s/tag/tags/
Missing the "OR cache tags invalidator interface" part.
This probably needs more test coverage.
I don't think we actually want to remove this?
s/backend/tags invalidator/
Requires an update of the property docs.
The docs for this function are now wrong/outdated. And probably even the name is now wrong-ish?
Comment #161
BerdirFixing this, when I don't comment on some points then it is fixed.
5. Oh, I forgot about that, let's see if it is still working :) See also the patch I posted in the fast backend & cache tags issue, that would help a lot with the (hopefully small) regression introduced here.
7. Not sure about the naming around checksum/invalidations, also the method.
9. @xjm agreed, changed.
10. No, use should never have \, the problem was that the one above had, not that this didn't :)
14. Moved down, same as DatabaseBackend now, also changed to cachetags, then it's exactly 80 characters.
18. Ha, no, it doesn't belong in any patch, was just trying some for a discussion here
20. Fixed all of those that I could find.
22. Yes, added another test, not perfect unit test as we call multiple methods but IMHO fine.
23. Yes, we do, because the validation is now in CacheTagsInvalidator, so I moved it to CacheTagsInvalidatorTest that you reviewed in 22 :)
25. not sure what you mean with property docs it is not a propery, I did rename the local variable name
26. Yes, I think we should just drop that method anyway and use a real Container like we do in many other tests. Updated/renamed.
Comment #163
BerdirI thought you reviewed this :p
I think we should switch the generic tests to use their factories, at least were we can.
Comment #164
BerdirTime to go home if I'm doing embarrassing stuff like that.. Re-uploaded the old patches.
Comment #166
damiankloip CreditAttribution: damiankloip commentedGenerally, this is looking fantastic! Thanks for picking this up again berdir.
I didn't have a chance to actually test yet (will do a bit later) but here are a few points:
I think this should have database in the name too?
Similar here, diff context might be lacking, but do we need the $context variable?
Is it worth a short comment here that this is first, so checksums below are updated from this?
This can just be assigned directly.
Now we're talking, we've wanted to get to something like this for a WHILE :)
Howcome we want to do this, then we are still allowing invalidateTags() on a specific bin, which is a big reason to separate this stuff in the first place (even though this will just proxy to the tag storage) :/
I thought this would be the other way round maybe - if it is some self container invalidation bin, call it?
This will then still loop over all cache tag invalidators and bins when we invalidate tags?
Again, can this var be removed?
Is this file meant to be here?
We should use \Drupal::service or $this->container
Comment #167
Wim Leers#161:
10. oops :)
18. :)
25. oops.
damiankloip already did a more architecture-level review. So here's another review with only nitpicks, that I already started, but meant to expand to do the review that Damian now already did :)
80 cols.
s/nvalidate/Invalidate/
Misplaced comma.
Hah, creative :)
s/$cache_tags_handler/$cache_tags_invalidator/
Comment #168
Fabianx CreditAttribution: Fabianx commentedOverall looks great.
From a clean state architecture this is what I would expect:
- There are n cache tags storages in the system and m cache tags invalidators. [YES, in addition any cache_tags.storage can subscribe to invalidation 'events']
- Cache Backends get the cache tags storage injected. [YES]
- There is a cache_tags.storage default cache tags storage [YES, here called cache_tags_storage]
- Both Cache Tags storages and cache tags invalidators are "subscribed" to cache tag invalidation 'events'. [YES]
- Cache Tags storages in addition can be queried for cache tags (cache tag checksums) [YES]
I would love from a naming to be more like:
- cache_tags (central service used for any operations, like invalidations)
- cache_tags.storage (the default storage injected in cache backends)
e.g.
\Drupal::service('cache_tags')->invalidateTags();
In addition to this the patch does:
- Allow any cache bin to subscribe to invalidation events and while that is for now the simplest implementation (with the least diff), it also leaves a lot to be desired in terms of de-coupling:
* For ACPU backend, there is no need to get the bin instance, the bin name is usually enough to invalidate the cache centrally.
* For redis backend the name might be enough or it might need to get the actual bins via container itself.
I am not convinced making the default implementation container aware is good here and giving bins / backends this capability.
I can see however that given the size of the patch, this might need to be a major follow-up instead.
OPEN QUESTIONS:
Operations like checksums should be done by the cache_tags service and not the storage service, so that storage can be exchanged.
The storage should IMHO just be responsible for getting and retrieving tag data (e.g. timestamps, increment counters, whatever) - nothing else.
Comment #169
Fabianx CreditAttribution: Fabianx commentedAfter speaking with berdir here comes the hard part: Naming things! :)
Proposal was to use:
CacheTagsChecksumProvider instead of CacheTagsInvalidationStorage.
That it is stored somewhere is an implementation detail.
Use cache_tags and just have to learn that \Drupal::service('cache_tags')->invalidateTags() won't work (Exception: Method not found), but have to use \Drupal::service('cache_tags_invalidator')->invalidateTags() instead. (or Cache::invalidateTags).
Also do use:
$this->cacheTags->checksum($tags);
to have less redundant tags in there.
Comment #170
Fabianx CreditAttribution: Fabianx commentedAlso and I am not sure how others feel about it, I think we could move that to a cache_tags_invalidator.cache_bin service.
This would enable us to make this service not container aware, but only the one using the cache bins.
And such remove the special case, but just have cache_bin service be one invalidation service in addition to the others that is called in the invalidateTags above.
Comment #171
znerol CreditAttribution: znerol commentedOr maybe implement a compiler pass which collects all cache bins implementing the
CacheTagsInvalidatorInterface
and registers it with thecache_tags_invalidator
service?Comment #172
BerdirCatching up with reviews (only commenting if there is something relevant to say):
#166:
1. As discussed, there's only one storage, so no, I don't think so.
2. Removed where possible, also renamed checksum_invalidations to just checksum
3. I just tried to put all the $tags "massaging" together. After thinking it through, the order does not matter. The count will be the same, unless a different server triggered a cache tag invalidation in the meantime. And we also don't invalidate the cache of that, we just remove the flag that we already invalidated that specific cache tag in this request (if we did), so that if there is another invalidation later in the request, we make sure that it counts. And this is documented inside onCacheTagsWrite() and not outside, as it is an implementation detail.
6. As discussed. We both have explicit invalidator registrations, those are not cache bins. And we also check all bins if they implement the interface and notify them as well. We need to do it, backends can separate out the invalidation to separate services if they are capable, but some implemenations will never be able to do that and the chains also need to account for that.
8. Uhm, I thought I already removed that above.
#167: Fixed.
#168-168: Not changed anything yet, need to think more about it. Would like to hear what others think about the storage name change and how we should call the services. I don't like the idea of a cache_tags service that both publishes cache tag invalidations and manages checksums (with a storage backing it), which means it is an invalidation subscriber as well. That seems to contradict your own argument about one service should only do one thing ;) I'm fine with moving the bin notifications to a separate service if others agree with #170, but for me, that belongs together.
#171: No, that is not possible. Cache backend settings are in $settings, so that they can be changed without rebuilding the container, and then what we expect it to be might no longer be true. It could also depend on the environment, for example, if you do a cache clear through apache, you get a fast chained backend that implements the service, if you do it with drush, you directly get the database backend which doesn't implement the service. And it would mean that we have to instantiate all bins during a container rebuild, which is something we should try to avoid if possible I think.
This also needs more code documentatio, I will try to update the chapter in core.api.php among other things.
Comment #174
BerdirMore stuff that doesn't belong in there...
Comment #175
Berdir#2392235: ChainedFastBackend shouldn't write cache tags to the fast cache back-end got in, that solves the possible regression when the apcu cache backend uses the common cache tags storage and therefore the need for profiling IMHO. It would still be a problem if apcubackend is used directly, but that is not common as then not even drush can be used as the shared memory can not be shared with it.
Updated the issue summary, please review.
Comment #176
catchOn the naming issue I'd go for checksum.
I also don't love the idea of still invalidating the bins sometimes, but I'm not sure there's a better alternative.
The main bug that the interface encouraged direct invalidations at bin level is gone.
If we wanted to not allow the bin storage to implement that interface, backends like memory would need an additional service that handles storage and invalidations, injected into each bin instance. That doesn't really feel like an improvement.
This is tempting.
Comment #177
BerdirOk, cache_tags.checksums or cache_tags.checksum_provider? And CacheTagsChecksumProviderInterface or without Provider, or something else?
On 3., any suggestions for method names for 1. and 2. ? right now it is checksumTags(). I have no idea what would make sense.. checksumTagsForSet/Write() for 1? Should the method for 2. then just return TRUE/FALSE, something like
$this->checksumProvider->isValid($tags, $checksum)?
Comment #178
BerdirOnly partially related to this issue, but I noticed in #2395143: YAML parsing is very slow, cache it with FileCache that we do thousands and thousands of markAsOutdated() queries and most of them due to deleteTags()/invalidateTags(). 6100 calls in total in standard install, 3800 of those from delete/invalidate tags.
I think we should look into not using chained fast during the installer and maybe even just using a memory implementation. That makes up 10% of the whole install runtime.
Comment #179
pounardOr a null implementation: don't cache anything during install, would be even more consistent I think.Depends on the cache items if it is being reused during the same request or not I guess.Comment #180
catchIf we do
$this->checksumProvider->isValid($tags, $checksum)?
then can we just leave checkSumTags() as the other method name?On the class/interface naming I'd skip provider I think.
Comment #181
BerdirOk, renamed to CacheTagsChecksumInterface, DatabaseCacheTagsChecksum, @cache_tags.checksum and checksumProvider in the backends.
Comment #183
BerdirWould help if I wouldn't upload a completely broken implementation.
Comment #185
BerdirFixing those test fails and renamed the service to cache_tags.invalidator.
Comment #187
BerdirGrml.
Comment #188
BerdirGreen, needs reviews, unassigning.
Comment #189
dawehnerWe could use the container.trait parent here ...
In case we would prefix the service name with 'validator' it would be a bit more clear, to what this belongs to, so what about using cache_tags.invalidator.checksum ?
While reading the code I was wondering why we have code in ApcuBackend::invalidateTags() but never access the stored counter?
Is there a particular reason we don't a) skip the check and b) do the unique call in validateTags itself? It is just a little bit easier to understand what is going on when you have 1 vs. 4 lines.
This could be for sure done in a follow up as well.
Did we also thought about including the sort() which is part of the db backend in to the service itself.
I try to be curious here ... do we recommend people to inject the cache_tags.invalidator service on the longrun? Maybe we can open up a new follow up to clear up those usecases?
This sentence sounds like someone waiting on simpletest, went onto reddit on accident, and forgot about what was actually the main topic at the moment :) I'll assume that this will document how to use it. Did you considered adapting the name to something like CacheTagsChecksumProviderInterface maybe? The current name does not tell enough maybe, what this is doing.
Maybe a bit of a provocative question (and I know we used to do that already in many places): When we know its only used for tests purposes, does it have to be part of the interface? You can still add the method in case you need to write tests against your congrete implementation. This is not a blocker at all, just wanted to throw out some thoughts.
Do you think it would help if we explain here, that cache backends can implement the interface in case they want to deal with invalidation for themselves.
You probably went through this already, but can we document why we have to allow multiple invalidations per request? Note: We can drop the notion of 'deletion' here.
should we use === here?
Unrelated change.
Feel free to get rid of the assignment.
Nitpick: 2 empty lines.
Nice numbers!
... is it really intented that we point to a non-existing class? the "nterface" of "Interface" is missing here.
Oh interesting: I always thought you have to use the ContainerBuilder, but it fact you don't have to.
Next time you have to do something like this: Use
->withConsecutive([['entity_types'], ['entity_bundles'], ['entity_field_info']])
Feel free to create a follow up: For readability reasons it would be great to move the cacheBackend up to the other cacheBackend once.
Comment #190
BerdirThanks for the review.
1. We could, changed ;)
2. Works for me, renamed.
3. Yeah, that's left-over code, removed.
4. There used to be an additional call there. I reverted the changes that I could so they don't distract :) I think the suggested changes don't really belong here.
5. I'm not sure on that part. We could, but for example, injecting it into plugin manager would break all subclasses/contrib plugin managers again for no good reason.. 9.x I guess, or new/standalone services...
6. It probably was netflix but close ;) Updated the documentation. Yes, I considered including Provider, read from comment #168, @catch in #180 preferred to not include Provider.
7. I'm not sure, it is not something we use for testing the implementation, it is used for web tests, like a bunch of other reset methods too, see WebTestBase::refreshVariables(), I'm replacing drupal_static_reset()'s with a reset() method.
8. Like this.
9. I tried, is this any clearer?
10. I'm not sure. At least right now, e.g. the database backend is passing in strings because it is loaded from the database, we would have to explicitly cast to (int) in the caller (because doing a type safe check and cast it in the implementation would be pointless).
11. Grrrr. Yet another left-over from Ghent discussion :p
12. Removed, was needed in an older version.
13. Fixed.
15. No idea why that doesn't throw errors. Fixed, should be the interface.
16. Yes, using the builder is actually pointless :)
Comment #191
dawehnerAgreed
That is indeed better.
Ah okay no big deal here.
In other words, +1
Comment #192
jhodgdon@Berdir asked me to take a look at this patch and its documentation. Overall I think it looks great, and I think the issue summary makes total sense. The code and documentation seem very clear, and I think the separation between the various classes/services is very clean.
Just to be clear, all I read on this issue is the summary and ... well at least most of the latest patch (I skipped reading the tests and may not have read the code too carefully in a few places), so apologies if I've missed discussions that weren't part of the summary.
I do have a few questions:
(a) In CacheTagsInvalidator::getInvalidatorCacheBins(), I don't think I understand why this is not done once and saved in a member variable the first call, then retrieved from the member variable after that for subsequent calls? It seems like this is being calculated every time there is an invalidateTags() call, which seems inefficient. I may not be understanding what this class is actually used for though?
(b) In the existing core.api.php in the cache tags section:
This needs to be removed.
(c) Also in core.api.php in the cache tags section, there is text where it talks about deleting tags, and I think now all you can do is invalidate them, right? Here's the text:
I think the rest of core.api.php text is fine as it is.
Comment #194
BerdirThanks for the review.
a) I'm not sure about storing that information. This shouldn't be called tha often (except from the installer), and theoretically, something could change e.g. during tests. It would probably be ok, but I don't think it would make much of a difference.
b) & c) should be fixed.
Also fixed the (again) broken installer.
Comment #196
BerdirThat is a random fail: #2398259: Random test fail in AccessRoleTest
Prepared a change record for this issue: https://www.drupal.org/node/2398255
Comment #198
jhodgdonRegarding the cache bins calculation, it's called for each call to invalidateTags()... Does invalidateTags() typically only happen once during an entity save operation (or other requests?), or are there multiple calls that typically need to be invalidated, during different steps in a request?
It just seems like a fairly expensive calculation... and is the 'cache_bins' parameter of the Container generally changed during a request (or ever)? Because that's what it's iterating over:
Docs changes look good!
Comment #199
BerdirYes, it usually only happens on write operations that change things that are cached, typically config or entities. The parameter usually doesn't change as that is based on services, but it is theoretically possible that difference backends are returned.
And no, it's not that expensive. If anything,then it is only expensive once, after that, the services are initialized and 10 calls to the container aren't going to make any difference at all.
Comment #200
catchOverall this looks great.
Two nitpicks was all I found so far:
s/requested/request.
catchException() contains the check that's already in ensureTableExists(), however we also currently can't rethrow this exception on ensureTableExists() in the case that the table already does exist (right now it just returns FALSE). Doesn't matter that much and not sure how to improve it, but it shows up as a bit odd.
Comment #201
BerdirThanks, rerolled, 1. fixed, not sure I understand the second point.
Comment #202
catchSo in ensureTableExists() we do this check:
Then again in catchException:
So if there's an exception, and the table already exists, then we have to check the existence of the table twice before rethrowing the exception. It'd be more efficient for ensureTableExists() to flag that yes, the table does actually exist already, so that catchException() becomes unnecessary. But I can't immediately see a clean way to do that, and wouldn't hold the patch up on it.
Comment #203
Wim LeersPatch looks great, has undergone a nice amount of scrutiny, CR looks great, docs have been reviewed, and I also can't see a clean way to do #202. Hence: RTBC.
Comment #204
alexpottCouple of minor things - looks great. Very happy to see the statics introduced to makes tests pass be replaced by a service.
Not needed anymore afaics
Can we can a followup created to inject the service instead of calling out to the static method. No need to make this patch any bigger though.
Comment #205
BerdirRemoved dead code from ApcuBackend.
The follow-up for injecting is #2398085: Inject cache_tags.invalidator instead of using Cache::invalidateTags(), we forgot to link that back to here.
Comment #206
alexpottAssigning to catch since he's been very involved with this topic.
Comment #207
catchOnly thing here is I'm not sure we should restrict this to an int - we probably want to allow it as a string, so that we can check hashes of invalidations rather than a numeric sum. This came up a couple of times on irc but don't think there's an issue, so opened #2401929: Cache tag checksums can be equal for different sets of checksums.
Don't have anything else though.
Comment #208
webchickComment #209
BerdirHm, interesting point, but we have to restrict it to *something*? This then needs to be stored, which e.g. for the database backend, is a separate column, so we can't just define it as mixed and then in 8.1 decide to use a string instead of an integer?
I think changing it to anything else than integer is not in the scope of this issue, so we should document it as that and change it if we decide to return string hashes or something else?
Comment #210
Wim Leers+1 to #209. It has served us fine so far, no need to start changing that detail (relatively speaking) in this already big issue/discussion/patch. To continue that discussion, catch already opened #2401929: Cache tag checksums can be equal for different sets of checksums.
Moving this back to RTBC.
Comment #211
catchHmm so the reason I brought it up here was because previously what gets stored hasn't been part of the API at all, but was just an implementation detail.
This patch brings the fact it's a string or integer into the API (only as a return value but still). I was wondering if we just define it as a string, then it'd be a bug that the database uses an int as storage compared to varchar, but no API change later if we change it.
Comment #212
BerdirWe discussed to change the API and storage to string but not doing anything about the implementation. With the idea that we could change the implementation without having to change the API/storage.
But after having a look, I'm not sure that makes sense. The checksum provider stores 'invalidations', changing that to a string would not make sense, nor we would we actually store a string there, we would store a 'last_invalidated' timestamp...
Comment #213
BerdirOk, changed just the API and the storage of database cache bins. Then we just have to change the cache_tags table if we want to change this...
Comment #214
BerdirDidn't want to keep this at RTBC.
Comment #215
catchThanks. Sorry that was so last minute, I didn't think about the API change until the very last look through.
Back to RTBC, will commit today.
Comment #216
catchCommitted/pushed to 8.0.x, thanks!
Comment #219
podarokThis patch brakes upgrade path from beta4
Unknown column 'checksum' in 'field list': INSERT INTO {cache_config}
Here is a manual upgrade path
Comment #220
BerdirYes, there is no upgrade path yet.
You can just drop the cache tables, but there are many other changes as well, this is just the first one that you'll notice.