Introduction
Cache Consistent provides a transactional aware cache backend wrapper as a means of ensuring cache consistency in setups not using the database as a cache backend. #1679344: Race condition in node_save() when not using DB for cache_field
Cache Consistent works best with the database isolation level is set to READ-COMMITTED.
It CAN be used with REPEATABLE-READ, but will in this case only mitigate the problem,
not eliminate it all together. When configured for REPEATABLE-READ, there may also
be occasionally more cache-misses.
The code-snippets on this page is meant for use in settings.php. See Step 3: Create settings.php, services.yml and the files directory for more info on settings.php in general.
Installation - Drupal 7
- Download and enable the module (https://www.drupal.org/documentation/install/modules-themes/modules-7)
- Apply the core patch included with the module.
- Clear cache.
Installation - Drupal 8
- Download and enable the module (https://www.drupal.org/documentation/install/modules-themes/modules-8)
- Apply the core patch included with the transactional php module. Alternatively, see https://www.drupal.org/node/1679344.
- Clear cache.
Configuration - MySQL
[mysqld]
binlog_format=row
transaction-isolation=READ-COMMITTED
It can also be advantageous to change the auto increment locking mode to "interleaved"
[mysqld]
innodb_autoinc_lock_mode=2
binlog_format=row
transaction-isolation=READ-COMMITTED
Isolation level can also be set from settings.php if necessary:
<?php
// When using MySQL, but not possible to change mysql server configuration,
// the isolation level can be set during database initialization.
$databases['default']['default']['init_commands'] = array(
'isolation_level' => "SET SESSION tx_isolation='READ-COMMITTED'",
);
?>
Configuration - Drupal 7
<?php
// Include apropriate backends.
$conf['cache_backends'] = array(
'sites/all/modules/contrib/memcache/memcache.inc',
'sites/all/modules/contrib/cache_consistent/cache_consistent.inc',
);
// Set default cache backend to use Cache Consistent.
$conf['cache_default_class'] = 'ConsistentCache';
// Use regular database backend for form cache (because it's not a cache).
$conf['cache_class_cache_form'] = 'DrupalDatabaseCache';
// Set Cache Consistent's default backend to memcache.
$conf['consistent_cache_default_class'] = 'MemCacheDrupal';
// Don't skip cache_set() operations during transactions.
// When using the isolation level REPEATABLE-READ, safe mode should be set to
// TRUE. When using READ-COMMITTED, it should be FALSE.
// What safe mode does, is that it discards cache writes during transactions, to
// avoid storing stale data into the cache. This is due to transaction snapshots
// and MVCC.
$conf['consistent_cache_default_safe'] = FALSE;
// Set cache consistent's buffer mechanism to ConsistentCacheBuffer. (default)
// There's currently only one buffer mechanism bundled with Cache Consistent.
$conf['consistent_cache_buffer_mechanism'] = 'ConsistentCacheBuffer';
// Don't do strict expire checking on each item for all bins. (default)
$conf['consistent_cache_default_strict'] = FALSE;
// Strict expire checking on each item for cache_bootstrap.
$conf['consistent_cache_strict_cache_bootstrap'] = TRUE;
?>
Configuration - Drupal 8
<?php
// When enabled, without any configuration, the Cache Consistent module will
// attempt to determine which cache backends to wrap.
// There are some configuration options available in order to finetune Cache
// Consistent. See below.
// To disable Cache Consistent without disabling the module, inform Cache
// Consistent that the entire cache system is intrinsically consistent.
$settings['cache']['consistent'] = TRUE; // effectively disables Cache Consistent.
// If using a backend that does not need to (or should) be buffered when a
// transaction is in progress, you can explicitly tell Cache Consistent not
// to wrap that backend.
// Warning: If a cache backend is NOT handled by Cache Consistent, the cache
// backend MUST implement the CacheTagsInvalidatorInterface in order to handle
// cache tag invalidations.
// This is because cache tag invalidators can be shared across backends, and
// buffering one without the other will lead to lack of invalidations or double
// invalidations.
$settings['cache']['consistent_backends']['cache.backend.weirdbackend'] = TRUE;
// For READ-COMMITTED setups, you may wan't to inform Cache Consistent about
// the default connection's isolation level.
// Valid options are:
// 0: READ-UNCOMMITTED
// 1: READ-COMMITTED
// 2: REPEATABLE-READ (default)
// 3: SERIALIZABLE
// Inform Cache Consistent that db's isolation level is READ-COMMITTED.
$settings['cache']['isolation_level'] = 1;
?>
EXPERIMENTAL: To optimize cache operations, you can add a "scrubber" to cache consistent. This will eliminate redundant cache operations within a transaction.
Put in your sites/default/services.yml:
cache_consistent.scrubber:
class: Drupal\cache_consistent\Cache\CacheConsistentScrubber
tags:
- { name: cache_consistent_scrubber }
Tests
The tests bundled with the module requires PHP 5.4 due to the use of traits.
FAQ
Why do I need this?
Whenever cache operations occur inside a transaction, the result of the cache
operation will be visible to concurrent requests, even though the transaction
hasn't been committed yet (or even rolled back).
But my site is running fine??? ... I think?
A lot of sites may run fine without using Cache Consistent. If you're using only
the database as a cache backend, you don't even need Cache Consistent. But the minute
you use another backend for cache, you introduce the possibiliy of a race condition
that can result in the cache being populated with old data. One situation where
this especially can be seen is when using entitycache. This makes a non-database
cache backend very susceptible to a race condition that can result in nodes being
uneditable due to the "The content on this page has either been modified by
another user, or you have already submitted modifications using this form. As a
result, your changes cannot be saved." error, which can then only be resolved
by clearing cache.
But if I just clear the cache, the site works again and runs fine?
Yes. That is correct. And if you like that solution, just stick to it.
Cache Heuristic
Version 7.x-2.x is compatible with Cache Heuristic 7.x-2.x.
Cache Heuristic should always be the first cache backend in the chain, for example:
Cache Heuristic => Cache Consistent => Memcache
<?php
$conf['cache_backends'] = array(
'sites/all/modules/contrib/memcache/memcache.inc',
'sites/all/modules/contrib/cache_heuristic/cache_heuristic.inc',
'sites/all/modules/contrib/cache_consistent/cache_consistent.inc',
'sites/all/modules/contrib/cache_heuristic/memcache.deferred.inc',
);
$conf['cache_default_class'] = 'HeuristicCache';
$conf['heuristic_cache_default_class'] = 'ConsistentCache';
$conf['consistent_cache_default_class'] = 'MemCacheDrupalDeferred';
$conf['consistent_cache_default_safe'] = FALSE;
?>
Caveats
Useful links
Project information
- Project categories: Performance, Developer tools
36 sites report using this module
- Created by gielfeldt on , updated
Stable releases for this project are covered by the security advisory policy.
There are currently no supported stable releases.

