Skip to content

[Cache] Improve the docs about cache namespaces #20966

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
May 16, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions cache.rst
Original file line number Diff line number Diff line change
Expand Up @@ -539,6 +539,8 @@ Symfony stores the item automatically in all the missing pools.
;
};
.. _cache-using-cache-tags:

Using Cache Tags
----------------

Expand Down
59 changes: 45 additions & 14 deletions components/cache.rst
Original file line number Diff line number Diff line change
Expand Up @@ -91,30 +91,61 @@ Creating Sub-Namespaces

Cache sub-namespaces were introduced in Symfony 7.3.

All cache adapters provided by the component implement the
:class:`Symfony\\Contracts\\Cache\\NamespacedPoolInterface` to provide the
:method:`Symfony\\Contracts\\Cache\\NamespacedPoolInterface::withSubNamespace` method.
This method allows namespacing cached items by transparently prefixing their keys::
Sometimes you need to create context-dependent variations of data that should be
cached. For example, the data used to render a dashboard page may be expensive
to generate and unique per user, so you can't cache the same data for everyone.

$subCache = $cache->withSubNamespace('foo');
In such cases, Symfony allows you to create different cache contexts using
namespaces. A cache namespace is an arbitrary string that identifies a set of
related cache items. All cache adapters provided by the component implement the
:class:`Symfony\\Contracts\\Cache\\NamespacedPoolInterface`, which provides the
:method:`Symfony\\Contracts\\Cache\\NamespacedPoolInterface::withSubNamespace`
method.

$subCache->get('my_cache_key', function (ItemInterface $item): string {
This method allows you to namespace cached items by transparently prefixing their keys::

$userCache = $cache->withSubNamespace(sprintf('user-%d', $user->getId()));

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

return '...';
});

In this example, the cache item will use the ``my_cache_key`` key, but it will be
stored internally under the ``foo`` namespace. This is handled transparently for
you, so you **don't** need to manually prefix keys like ``foo.my_cache_key``.
In this example, the cache item uses the ``dashboard_data`` key, but it will be
stored internally under a namespace based on the current user ID. This is handled
automatically, so you **dont** need to manually prefix keys like ``user-27.dashboard_data``.

This is useful when using namespace-based cache invalidation to isolate or
invalidate a subset of cached data based on some context. Typical examples
include namespacing by user ID, locale, or entity ID and hash::
There are no guidelines or restrictions on how to define cache namespaces.
You can make them as granular or as generic as your application requires:

$userCache = $cache->withSubNamespace((string) $userId);
$localeCache = $cache->withSubNamespace($request->getLocale());
$productCache = $cache->withSubNamespace($productId.'_'.$productChecksum);

$flagCache = $cache->withSubNamespace(
$featureToggle->isEnabled('new_checkout') ? 'checkout-v2' : 'checkout-v1'
);

$channel = $request->attributes->get('_route')?->startsWith('api_') ? 'api' : 'web';
$channelCache = $cache->withSubNamespace($channel);

.. tip::

You can even combine cache namespaces with :ref:`cache tags <cache-using-cache-tags>`
for more advanced needs.

There is no built-in way to invalidate caches by namespace. Instead, the recommended
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Couldn't this be achieved with $cache->withSubNamespace('ns')->clear()?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It depends on the implementation: some support this, others not (they might return false or clear the full pool depending on the implementation.)
Not all backends support clearing by prefix...

approach is to change the namespace itself. For this reason, it's common to include
static or dynamic versioning data in the cache namespace::

// for simple applications, an incrementing static version number may be enough
$userCache = $cache->withSubNamespace(sprintf('v1-user-%d', $user->getId()));

// other applications may use dynamic versioning based on the date (e.g. monthly)
$userCache = $cache->withSubNamespace(sprintf('%s-user-%d', date('Ym'), $user->getId()));

// or even invalidate the cache when the user data changes
$checksum = hash('xxh128', $user->getUpdatedAt()->format(DATE_ATOM));
$userCache = $cache->withSubNamespace(sprintf('user-%d-%s', $user->getId(), $checksum));

.. _cache_stampede-prevention:

Expand Down