You are here

Cache API in Drupal 8

Same name and namespace in other branches
  1. 9 core/core.api.php \cache

Information about the Drupal Cache API

Basics

Note: If not specified, all of the methods mentioned here belong to \Drupal\Core\Cache\CacheBackendInterface.

The Cache API is used to store data that takes a long time to compute. Caching can either be permanent or valid only for a certain time span, and the cache can contain any type of data.

To use the Cache API:

  • Request a cache object through \Drupal::cache() or by injecting a cache service.
  • Define a Cache ID (cid) value for your data. A cid is a string, which must contain enough information to uniquely identify the data. For example, if your data contains translated strings, then your cid value must include the interface text language selected for page.
  • Call the get() method to attempt a cache read, to see if the cache already contains your data.
  • If your data is not already in the cache, compute it and add it to the cache using the set() method. The third argument of set() can be used to control the lifetime of your cache item.

Example:

$cid = 'mymodule_example:' . \Drupal::languageManager()
  ->getCurrentLanguage()
  ->getId();
$data = NULL;
if ($cache = \Drupal::cache()
  ->get($cid)) {
  $data = $cache->data;
}
else {
  $data = my_module_complicated_calculation();
  \Drupal::cache()
    ->set($cid, $data);
}

Note the use of $data and $cache->data in the above example. Calls to \Drupal::cache()->get() return a record that contains the information stored by \Drupal::cache()->set() in the data property as well as additional meta information about the cached data. In order to make use of the cached data you can access it via $cache->data.

Cache bins

Cache storage is separated into "bins", each containing various cache items. Each bin can be configured separately; see Configuration.

When you request a cache object, you can specify the bin name in your call to \Drupal::cache(). Alternatively, you can request a bin by getting service "cache.nameofbin" from the container. The default bin is called "default", with service name "cache.default", it is used to store common and frequently used caches.

Other common cache bins are the following:

  • bootstrap: Data needed from the beginning to the end of most requests, that has a very strict limit on variations and is invalidated rarely.
  • render: Contains cached HTML strings like cached pages and blocks, can grow to large size.
  • data: Contains data that can vary by path or similar context.
  • discovery: Contains cached discovery data for things such as plugins, views_data, or YAML discovered data such as library info.

A module can define a cache bin by defining a service in its modulename.services.yml file as follows (substituting the desired name for "nameofbin"):


cache.nameofbin:
  class: Drupal\Core\Cache\CacheBackendInterface
  tags:
    - { name: cache.bin }
  factory: cache_factory:get
  arguments: [nameofbin]

See the Services topic for more on defining services.

Deletion

There are two ways to remove an item from the cache:

  • Deletion (using delete(), deleteMultiple() or deleteAll()) permanently removes the item from the cache.
  • Invalidation (using invalidate(), invalidateMultiple() or invalidateAll()) is a "soft" delete that only marks items as "invalid", meaning "not fresh" or "not fresh enough". Invalid items are not usually returned from the cache, so in most ways they behave as if they have been deleted. However, it is possible to retrieve invalid items, if they have not yet been permanently removed by the garbage collector, by passing TRUE as the second argument for get($cid, $allow_invalid).

Use deletion if a cache item is no longer useful; for instance, if the item contains references to data that has been deleted. Use invalidation if the cached item may still be useful to some callers until it has been updated with fresh data. The fact that it was fresh a short while ago may often be sufficient.

Invalidation is particularly useful to protect against stampedes. Rather than having multiple concurrent requests updating the same cache item when it expires or is deleted, there can be one request updating the cache, while the other requests can proceed using the stale value. As soon as the cache item has been updated, all future requests will use the updated value.

Cache Tags

The fourth argument of the set() method can be used to specify cache tags, which are used to identify which data is included in each cache item. A cache item can have multiple cache tags (an array of cache tags), and each cache tag is a string. The convention is to generate cache tags of the form [prefix]:[suffix]. Usually, you'll want to associate the cache tags of entities, or entity listings. You won't have to manually construct cache tags for them — just get their cache tags via \Drupal\Core\Cache\CacheableDependencyInterface::getCacheTags() and \Drupal\Core\Entity\EntityTypeInterface::getListCacheTags(). Data that has been tagged can be invalidated as a group: no matter the Cache ID (cid) of the cache item, no matter in which cache bin a cache item lives; as long as it is tagged with a certain cache tag, it will be invalidated.

Because of that, cache tags are a solution to the cache invalidation problem:

  • For caching to be effective, each cache item must only be invalidated when absolutely necessary. (i.e. maximizing the cache hit ratio.)
  • For caching to be correct, each cache item that depends on a certain thing must be invalidated whenever that certain thing is modified.

A typical scenario: a user has modified a node that appears in two views, three blocks and on twelve pages. Without cache tags, we couldn't possibly know which cache items to invalidate, so we'd have to invalidate everything: we had to sacrifice effectiveness to achieve correctness. With cache tags, we can have both.

Example:

// A cache item with nodes, users, and some custom module data.
$tags = array(
  'my_custom_tag',
  'node:1',
  'node:3',
  'user:7',
);
\Drupal::cache()
  ->set($cid, $data, CacheBackendInterface::CACHE_PERMANENT, $tags);

// Invalidate all cache items with certain tags.
\Drupal\Core\Cache\Cache::invalidateTags(array(
  'user:1',
));

Drupal is a content management system, so naturally you want changes to your content to be reflected everywhere, immediately. That's why we made sure that every entity type in Drupal 8 automatically has support for cache tags: when you save an entity, you can be sure that the cache items that have the corresponding cache tags will be invalidated. This also is the case when you define your own entity types: you'll get the exact same cache tag invalidation as any of the built-in entity types, with the ability to override any of the default behavior if needed. See \Drupal\Core\Cache\CacheableDependencyInterface::getCacheTags(), \Drupal\Core\Entity\EntityTypeInterface::getListCacheTags(), \Drupal\Core\Entity\Entity::invalidateTagsOnSave() and \Drupal\Core\Entity\Entity::invalidateTagsOnDelete().

Cache contexts

Some computed data depends on contextual data, such as the user roles of the logged-in user who is viewing a page, the language the page is being rendered in, the theme being used, etc. When caching the output of such a calculation, you must cache each variation separately, along with information about which variation of the contextual data was used in the calculation. The next time the computed data is needed, if the context matches that for an existing cached data set, the cached data can be reused; if no context matches, a new data set can be calculated and cached for later use.

Cache contexts are services tagged with 'cache.context', whose classes implement \Drupal\Core\Cache\Context\CacheContextInterface. See https://www.drupal.org/docs/drupal-apis/cache-api/cache-contexts for more information on cache contexts, including a list of the contexts that exist in Drupal core, and information on how to define your own contexts. See the Services and the Dependency Injection Container topic for more information about services.

Typically, the cache context is specified as part of the #cache property of a render array; see the Caching section of the Render API overview topic for details.

Configuration

By default cached data is stored in the database. This can be configured though so that all cached data, or that of an individual cache bin, uses a different cache backend, such as APCu or Memcache, for storage.

In a settings.php file, you can override the service used for a particular cache bin. For example, if your service implementation of \Drupal\Core\Cache\CacheBackendInterface was called cache.custom, the following line would make Drupal use it for the 'cache_render' bin:

$settings['cache']['bins']['render'] = 'cache.custom';

Additionally, you can register your cache implementation to be used by default for all cache bins with:

$settings['cache']['default'] = 'cache.custom';

For cache bins that are stored in the database, the number of rows is limited to 5000 by default. This can be changed for all database cache bins. For example, to instead limit the number of rows to 50000:

$settings['database_cache_max_rows']['default'] = 50000;

Or per bin (in this example we allow infinite entries):

$settings['database_cache_max_rows']['bins']['dynamic_page_cache'] = -1;

For monitoring reasons it might be useful to figure out the amount of data stored in tables. The following SQL snippet can be used for that:


SELECT table_name AS `Table`, table_rows AS 'Num. of Rows',
ROUND(((data_length + index_length) / 1024 / 1024), 2) `Size in MB` FROM
information_schema.TABLES WHERE table_schema = '***DATABASE_NAME***' AND
table_name LIKE 'cache_%'  ORDER BY (data_length + index_length) DESC
LIMIT 10;

Finally, you can chain multiple cache backends together, see \Drupal\Core\Cache\ChainedFastBackend and \Drupal\Core\Cache\BackendChain.

See also

\Drupal\Core\Cache\DatabaseBackend

https://www.drupal.org/node/1884796

File

core/core.api.php, line 404
Documentation landing page and topics, plus core library hooks.

Functions

Namesort descending Location Description
Drupal::cache core/lib/Drupal.php Returns the requested cache bin.

Classes

Namesort descending Location Description
BackendChain core/lib/Drupal/Core/Cache/BackendChain.php Defines a chained cache implementation for combining multiple cache backends.
Cache core/lib/Drupal/Core/Cache/Cache.php Helper methods for cache.
CacheableMetadata core/lib/Drupal/Core/Cache/CacheableMetadata.php Defines a generic class for passing cacheability metadata.
CacheCollector core/lib/Drupal/Core/Cache/CacheCollector.php Default implementation for CacheCollectorInterface.
ChainedFastBackend core/lib/Drupal/Core/Cache/ChainedFastBackend.php Defines a backend with a fast and a consistent backend chain.
DatabaseBackend core/lib/Drupal/Core/Cache/DatabaseBackend.php Defines a default cache implementation.
MemoryBackend core/lib/Drupal/Core/Cache/MemoryBackend.php Defines a memory cache implementation.
MemoryCache core/lib/Drupal/Core/Cache/MemoryCache/MemoryCache.php Defines a memory cache implementation.
NullBackend core/lib/Drupal/Core/Cache/NullBackend.php Defines a stub cache implementation.
PhpBackend core/lib/Drupal/Core/Cache/PhpBackend.php Defines a PHP cache implementation.

Interfaces

Namesort descending Location Description
CacheableDependencyInterface core/lib/Drupal/Core/Cache/CacheableDependencyInterface.php Defines an interface for objects which may be used by other cached objects.
CacheBackendInterface core/lib/Drupal/Core/Cache/CacheBackendInterface.php Defines an interface for cache implementations.
CacheCollectorInterface core/lib/Drupal/Core/Cache/CacheCollectorInterface.php Provides a caching wrapper to be used in place of large structures.
CacheTagsChecksumInterface core/lib/Drupal/Core/Cache/CacheTagsChecksumInterface.php Provides checksums for cache tag invalidations.
CacheTagsInvalidatorInterface core/lib/Drupal/Core/Cache/CacheTagsInvalidatorInterface.php Defines required methods for classes wanting to handle cache tag changes.
MemoryCacheInterface core/lib/Drupal/Core/Cache/MemoryCache/MemoryCacheInterface.php Defines an interface for memory cache implementations.