{"action":"created","issue":{"url":"https://api.github.com/repos/symfony/symfony/issues/18646","repository_url":"https://api.github.com/repos/symfony/symfony","labels_url":"https://api.github.com/repos/symfony/symfony/issues/18646/labels{/name}","comments_url":"https://api.github.com/repos/symfony/symfony/issues/18646/comments","events_url":"https://api.github.com/repos/symfony/symfony/issues/18646/events","html_url":"https://github.com/symfony/symfony/pull/18646","id":151216644,"number":18646,"title":"[Cache] Add hierachical tags based invalidation","user":{"login":"nicolas-grekas","id":243674,"avatar_url":"https://avatars.githubusercontent.com/u/243674?v=3","gravatar_id":"","url":"https://api.github.com/users/nicolas-grekas","html_url":"https://github.com/nicolas-grekas","followers_url":"https://api.github.com/users/nicolas-grekas/followers","following_url":"https://api.github.com/users/nicolas-grekas/following{/other_user}","gists_url":"https://api.github.com/users/nicolas-grekas/gists{/gist_id}","starred_url":"https://api.github.com/users/nicolas-grekas/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/nicolas-grekas/subscriptions","organizations_url":"https://api.github.com/users/nicolas-grekas/orgs","repos_url":"https://api.github.com/users/nicolas-grekas/repos","events_url":"https://api.github.com/users/nicolas-grekas/events{/privacy}","received_events_url":"https://api.github.com/users/nicolas-grekas/received_events","type":"User","site_admin":false},"labels":[{"url":"https://api.github.com/repos/symfony/symfony/labels/Cache","name":"Cache","color":"dddddd"},{"url":"https://api.github.com/repos/symfony/symfony/labels/Feature","name":"Feature","color":"02e10c"},{"url":"https://api.github.com/repos/symfony/symfony/labels/Status:%20Needs%20Review","name":"Status: Needs Review","color":"ededed"}],"state":"open","locked":false,"assignee":null,"milestone":null,"comments":13,"created_at":"2016-04-26T19:15:13Z","updated_at":"2016-05-03T13:56:12Z","closed_at":null,"pull_request":{"url":"https://api.github.com/repos/symfony/symfony/pulls/18646","html_url":"https://github.com/symfony/symfony/pull/18646","diff_url":"https://github.com/symfony/symfony/pull/18646.diff","patch_url":"https://github.com/symfony/symfony/pull/18646.patch"},"body":"| Q             | A\r\n| ------------- | ---\r\n| Branch?       | 3.2\r\n| Bug fix?      | no\r\n| New feature?  | no\r\n| BC breaks?    | no\r\n| Deprecations? | no\r\n| Tests pass?   | yes\r\n| Fixed tickets | -\r\n| License       | MIT\r\n| Doc PR        | -\r\n\r\nThe implemented cache invalidation strategy is not inspired from Stash nor php-cache (but from my own good-old patchwork as stated above), so please be careful when doing direct comparisons: the concepts might look similar but aren't the same in the end. AFAIK, Stashphp and php-cache, both split the hierarchical vs tag concepts: hierarchical invalidation is implemented using special conventions when naming cache keys, and tags are plain labels used to define sets of invalidation.\r\n\r\nHere, we have a single concept of hierarchical tags to invalidate cached items. Basically, hierarchical tags do not mangle with cache keys but add on top of PSR-6, in a powerful way for advanced cache invalidation needs (which almost everyone needs when generating web pages with excerpts from everywhere in the app's storage).\r\n\r\nA hierarchical tag is a slash separated string, e.g. `foo/bar`. One can apply as many tags as she want on a cache item before saving it by using the new `CacheItem::tag($tag)` method, formalized in `TaggedCacheItemInterface`. Invalidation is provided by the `TagInvalidationInterface::invalidate($tag)` method.\r\n\r\nTags are called hierarchical because invalidation is propagated to sub-tags: invalidating the `foo` tag is going to invalidate all items linked to tags that are in the `foo/**` hierarchy. And that's all folks :) I've been using this strategy for years on my own apps with great success so far. It allows to very easily invalidate related contents.\r\n\r\nA working implementation is provided for the Redis and Filesystem adapters.\r\nI suggest looking at the test case to see how it can be used.\r\n\r\nFurther work could be done later to define internal interfaces that would allow having a separate storage for cached items and cache tags (php-cache already has such similar thing). But this is not required to get this very PR included IMO.\r\n\r\nWDYT?"},"comment":{"url":"https://api.github.com/repos/symfony/symfony/issues/comments/216535355","html_url":"https://github.com/symfony/symfony/pull/18646#issuecomment-216535355","issue_url":"https://api.github.com/repos/symfony/symfony/issues/18646","id":216535355,"user":{"login":"nicolas-grekas","id":243674,"avatar_url":"https://avatars.githubusercontent.com/u/243674?v=3","gravatar_id":"","url":"https://api.github.com/users/nicolas-grekas","html_url":"https://github.com/nicolas-grekas","followers_url":"https://api.github.com/users/nicolas-grekas/followers","following_url":"https://api.github.com/users/nicolas-grekas/following{/other_user}","gists_url":"https://api.github.com/users/nicolas-grekas/gists{/gist_id}","starred_url":"https://api.github.com/users/nicolas-grekas/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/nicolas-grekas/subscriptions","organizations_url":"https://api.github.com/users/nicolas-grekas/orgs","repos_url":"https://api.github.com/users/nicolas-grekas/repos","events_url":"https://api.github.com/users/nicolas-grekas/events{/privacy}","received_events_url":"https://api.github.com/users/nicolas-grekas/received_events","type":"User","site_admin":false},"created_at":"2016-05-03T13:56:12Z","updated_at":"2016-05-03T13:56:12Z","body":"Invalidation is now done using a Redis cursor (SSCAN) so that the process is not blocking concurrent accesses (which was the case when using SMEMBERS on large sets).\r\nAs @andrerom noted, the invalidation loop can grow huge when the list of items and related tags is.\r\nTo me, this means at some point, for serious setups, it should be done asynchronously in a worker dedicated to invalidation."}}