2

I have a render array I'm caching as follows:

$build['friend_link'] = [
  '#type' => 'item',
  '#markup' => $link,
  '#cache' => [
    'keys' => ['friendship', $user->id()],
  ],
];

I need to delete this cache when the friendship is invalidated. However, I'm having a lot of troubles with retrieving the cache in order to invalidate it. I'm attempting to use the following code to retrieve the cache item:

$render_cache = \Drupal::cache('render');
$render_cache->get('friendship:' . $friend->id()); // id = 4

However, this doesn't work, because the actual cache key in the DB is:

friendship:4:[languages:language_interface]=en:[theme]=bartik:[user.permissions]=80e4ddd871aca07e035f618b532bc75cb7c155caaaedd08a4ae17e5e137ca30f

I tried setting the cache contexts to an empty array:

'#cache' => [
  'keys' => ['friendship', $user->id()],
  'contexts' => [],
],

But this didn't change the key. So I set the contexts to a value:

'#cache' => [
  'keys' => ['friendship', $user->id()],
  'contexts' => ['user'],
],

This changed the key to:

friendship:4:[languages:language_interface]=en:[theme]=bartik:[user]=1

So while this does make it potentially easier to build the key manually, it seems like I'm missing something.

I did find RenderCache::createCacheID(), however it's protected so I can't call it.

I'm at a bit of a loss here, is there an API function I'm missing somewhere to retrieve the cache?

1 Answer 1

4

What exactly is a "friendship"? You most likely want to use cache tags for invalidation.

One way to do that would be to add the cache tag for the user, then you just have to make sure that you are invalidating that user cache tag. So you add 'tags' => $user->getCacheTags(), and when the friendships of a user change, you use Cache::invalidateTags($user->getCacheTags()). The downside is that this will invalidate too much: It will invalidate all content where the user is displayed, e.g. if he's the author of an article.

You could define your own cache tag; they're really just strings and invalidate just that, e.g. 'tags' => ['friendships:' . $user->uid] and then invalidate that.

Cache contexts are for variations of the same cache entry; it's exactly those things you see in the cache key. Having $user->id() in the cache keys and the user context is practically the same thing. You can do either or the other; you don't need both. (Cache context user and keys just 'friendships' would be the same as what you have now.)

5
  • Thanks for the suggestions - I've already gone the route of using cache tags and invalidating them all - that's my backup plan. But it's overkill. I'd much rather just delete the cache item in question. I feel like I must be missing something though - they provide public methods to delete caches by CID, but there is no public method to generate the CID from the array that was used to create it. If I'm not missing something, then I think I need to submit a patch to DO, as right now the get() method is entirely useless.
    – Jaypan
    Nov 27, 2016 at 15:33
  • 2
    If you make your cache tag specific enough then you don't have to "invalidate them all". It will be the same as deleting a single entry. get()/set() is not useless, but the render cache API sits on top of it, you interact with that, not with get/set. It's not designed to be used like that. It is designed to deal with the extensible render system. Your case which is per-user is a bit a special case, but even then, it is theoretically possible that there is more than just one cache entry.. for example for two different languages. Cache tags are the way this is meant to work.
    – Berdir
    Nov 27, 2016 at 15:40
  • Drupal 7 had a wildcard argument allowing you to delete all tags starting with a given string - that would work perfectly in this case, as all tags for all environments could be deleted.
    – Jaypan
    Nov 27, 2016 at 15:45
  • 3
    Yes, cache tags are the replacement for exactly that wildcard API, they are much more flexible. Redis/Memache have no way of supporting a wildcard delete, they had to invent a system that was at least as complex as cache tags in 7.x. The only way to have a single cache entry that you can get/delete is to use the cache API directly and then through the render cache, for example to cache a list of friendships per user and no longer have your own render cache entry. Note that render caches can be nested, so your part might be part of a block, and that is cache too. Cache tags solve that as well
    – Berdir
    Nov 27, 2016 at 15:51
  • Thanks, your comments put me right in the right direction. I was had a fundamental misunderstanding about cache tags that your post made me realize, and I was able to come up with a solution by setting a string for the tag, and invalidating that tag.
    – Jaypan
    Nov 27, 2016 at 16:25

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.