Change record status: 
Project: 
Introduced in branch: 
10.2.x
Introduced in version: 
10.2.0
Description: 

Up until now, the only cache able to benefit from cache contexts was the render cache. If you wanted to cache anything having multiple variations other than a render array, you'd have to somehow put your cacheable data into a render array and cache it with the render cache. This used to look something like this (taken from dynamic page cache):

// Embed the response object in a render array so that RenderCache is able
// to cache it, handling cache redirection for us.
$response_as_render_array = $this->responseToRenderArray($response);
$this->renderCache->set($response_as_render_array, $this->dynamicPageCacheRedirectRenderArray);

The first part of that set call would consist of a render array with the response and its associated cache contexts, whereas the latter would contain a render array with the initial cache contexts (in this case 'route' and 'request_format'). This is important because the latter set is used as a base of contexts and the final set may contain more, at which point so-called "cache redirects" needed to be introduced.

With the new API, you need to get a VariationCache from the VariationCacheFactory (more on that below) and pass that to your code rather than the render cache. Then your code will look something like this:

// Embed the response object in a render array so that RenderCache is able
// to cache it, handling cache redirection for us.
$cacheable_metadata = CacheableMetadata::createFromObject($response->getCacheableMetadata());
$this->cache->set(
  ['response'],
  $response,
  $cacheable_metadata->addCacheContexts($this->cacheContexts),
  (new CacheableMetadata())->setCacheContexts($this->cacheContexts)
);

As you can see, this API also has a notion of initial cache contexts (the last argument) and the final or actual cache contexts (the second to last argument). But instead of having to pass a render array, we can now directly pass the cache keys and response, just like we would with a regular cache. For completeness: $this->cacheContexts contains 'route' and 'request_format', just like the old code.

So how do we add this variation cache to our service? Well let's look at the dynamic page cache's service declaration before and after:
Before:

  dynamic_page_cache_subscriber:
    class: Drupal\dynamic_page_cache\EventSubscriber\DynamicPageCacheSubscriber
    arguments: ['@dynamic_page_cache_request_policy', '@dynamic_page_cache_response_policy', '@render_cache', '%renderer.config%']
    tags:
      - { name: event_subscriber }

After:

  variation_cache.dynamic_page_cache:
    class: Drupal\Core\Cache\VariationCacheInterface
    factory: variation_cache_factory:get
    arguments: [dynamic_page_cache]
  dynamic_page_cache_subscriber:
    class: Drupal\dynamic_page_cache\EventSubscriber\DynamicPageCacheSubscriber
    arguments: ['@dynamic_page_cache_request_policy', '@dynamic_page_cache_response_policy', '@variation_cache.dynamic_page_cache', '@cache_contexts_manager', '%renderer.config%']
    tags:
      - { name: event_subscriber }

One line to focus on in particular is arguments: [dynamic_page_cache], where 'dynamic_page_cache' is the name of a regular cache backend. The variation cache is a wrapper around a regular cache, so you need to feed it the name of a regular cache backend. In this case, we pointed to the 'cache.dynamic_page_cache' cache backend.

For more examples, you can also look into RenderCache as that class's inner code has been replaced with a mere call to the new VariationCache.

Impacts: 
Module developers