Nicolas Grekas
Contributed by Nicolas Grekas in #59813

Namespace-based cache invalidation is a technique where cache keys are grouped under a logical namespace (e.g. per user, locale, or entity). Instead of deleting individual keys, you invalidate the entire namespace, effectively removing all related entries at once.

In Symfony 7.3, we're adding namespace-based cache invalidation to all cache adapters in the Cache component. This is implemented via the new Symfony\Contracts\Cache\NamespacedPoolInterface, which defines the withSubNamespace() method:

1
2
3
4
5
6
7
$subCache = $cache->withSubNamespace('foo');

$subCache->get('my_cache_key', function (ItemInterface $item): string {
    $item->expiresAfter(3600);

    return '...';
});

In this example, the cache item uses the my_cache_key key, but it's stored internally under the foo namespace. This is handled transparently, so you don't need to manually prefix keys like foo.my_cache_key. In practice, this groups cache items logically into different sets:

1
2
3
4
5
6
7
8
9
10
11
12
13
$subCache = $cache->withSubNamespace('foo');

$bar1 = $cache->getItem('bar1');
$bar1->set(...); $cache->save();

$bar2 = $subCache->getItem('bar2');
$bar2->set(...); $subCache->save();

$cache->getItem('bar1')->isHit();        // true
$cache->getItem('bar2')->isHit();        // false
$subCache->getItem('bar1')->isHit();     // false
$subCache->getItem('bar2')->isHit();     // true
$subCache->getItem('foo.bar2')->isHit(); // false

Namespaces can be anything that makes sense in your application. For example, you can cache data per user, per locale, or per entity:

1
2
3
$userCache = $cache->withSubNamespace((string) $userId);
$localeCache = $cache->withSubNamespace($request->getLocale());
$productCache = $cache->withSubNamespace($productId);
Published in #Living on the edge