diff --git a/core/lib/Drupal/Core/Cache/MultiBackend.php b/core/lib/Drupal/Core/Cache/MultiBackend.php index 7acaa15..97623fe 100644 --- a/core/lib/Drupal/Core/Cache/MultiBackend.php +++ b/core/lib/Drupal/Core/Cache/MultiBackend.php @@ -7,6 +7,8 @@ namespace Drupal\Core\Cache; +use Drupal\Core\Lock\LockBackendInterface; + /** * A proxy cache backend to preload multiple items. * @@ -28,6 +30,13 @@ class MultiBackend implements CacheBackendInterface, CacheTagsInvalidatorInterfa protected $cacheBackend; /** + * The lock backend. + * + * @var \Drupal\Core\Lock\LockBackendInterface + */ + protected $lock; + + /** * The cache bin name. * * @var string @@ -67,14 +76,17 @@ class MultiBackend implements CacheBackendInterface, CacheTagsInvalidatorInterfa * * @param \Drupal\Core\Cache\CacheBackendInterface $cache_backend * The cache backend. + * @param \Drupal\Core\Lock\LockBackendInterface $lock + * The lock backend. * @param string $bin * The cache bin for which the object is created. * @param string $multi_cid_prefix * (optional) The prefix to use for storage of the cids to preload. This * value will be suffixed with the bin name. */ - public function __construct(CacheBackendInterface $cache_backend, $bin, $multi_cid_prefix = 'cache_multi') { + public function __construct(CacheBackendInterface $cache_backend, LockBackendInterface $lock, $bin, $multi_cid_prefix = 'cache_multi') { $this->cacheBackend = $cache_backend; + $this->lock = $lock; $this->bin = $bin; $this->multiCidKey = $multi_cid_prefix . ':' . $bin; } @@ -249,27 +261,39 @@ public function removeBin() { * Pre load Multiple items. */ protected function preloadMultiple() { - if (!$this->preloaded) { - $this->preloaded = TRUE; - $multi_cid_cache = $this->cacheBackend->get($this->multiCidKey); + if ($this->preloaded) { + return; + } - // Return now if there are no cids to load. - if (empty($multi_cid_cache->data)) { - return; - } + $this->preloaded = TRUE; + $multi_cid_items = $this->getmultiCidItems(); - $this->multiCids = $multi_cid_cache->data; - $this->cacheItems = $this->cacheBackend->getMultiple($this->multiCids); + // Return now if there are no cids to load. + if (empty($multi_cid_items)) { + return; + } - // If the loaded items and the multi cids are different reset them. - if (array_diff_key($this->cacheItems, $this->multiCids)) { - $this->resetMultiKeys(); - $this->addMultiKeys(array_keys($this->cacheItems)); - } + $this->multiCids = $multi_cid_items; + $this->cacheItems = $this->cacheBackend->getMultiple($this->multiCids); + + // If the loaded items and the multi cids are different reset them. + if (array_diff_key($this->cacheItems, $this->multiCids)) { + $this->resetMultiKeys(); + $this->addMultiKeys(array_keys($this->cacheItems)); } } /** + * Gets multi cid items from the storage backend. + * + * @return array + */ + protected function getmultiCidItems() { + $multi_cid_cache = $this->cacheBackend->get($this->multiCidKey); + return !empty($multi_cid_cache->data) ? $multi_cid_cache->data : []; + } + + /** * Prepares a cached item. * * Checks that items are either permanent or did not expire, and returns data @@ -315,12 +339,19 @@ protected function prepareItem($cache, $allow_invalid) { * @param array $keys */ protected function addMultiKeys(array $keys) { - foreach ($keys as $key) { - $this->multiCids[$key] = $key; - } + if ($this->lock->acquire($this->multiCidKey)) { + $multi_cid_items = $this->getmultiCidItems(); + + foreach ($keys as $key) { + $this->multiCids[$key] = $key; + $multi_cid_items[$key] = $key; + } - // @todo Move this to set on destruct? - $this->cacheBackend->set($this->multiCidKey, $this->multiCids); + // @todo Move this to set on destruct? + $this->cacheBackend->set($this->multiCidKey, $multi_cid_items); + + $this->lock->release($this->multiCidKey); + } } /** @@ -329,19 +360,29 @@ protected function addMultiKeys(array $keys) { * @param array $keys */ protected function removeMultiKeys(array $keys) { - foreach ($keys as $key) { - unset($this->multiCids[$key]); - } + if ($this->lock->acquire($this->multiCidKey)) { + $multi_cid_items = $this->getmultiCidItems(); - $this->cacheBackend->set($this->multiCidKey, $this->multiCids); + foreach ($keys as $key) { + unset($this->multiCids[$key], $multi_cid_items[$key]); + } + + $this->cacheBackend->set($this->multiCidKey, $multi_cid_items); + + $this->lock->release($this->multiCidKey); + } } /** * Resets multi cache keys. */ protected function resetMultiKeys() { - $this->cacheBackend->delete($this->multiCidKey); - $this->multiCids = []; + if ($this->lock->acquire($this->multiCidKey)) { + $this->cacheBackend->delete($this->multiCidKey); + $this->multiCids = []; + + $this->lock->release($this->multiCidKey); + } } /** diff --git a/core/modules/system/src/Tests/Cache/MultiBackendUnitTest.php b/core/modules/system/src/Tests/Cache/MultiBackendUnitTest.php index 793ec83..ce6d17f 100644 --- a/core/modules/system/src/Tests/Cache/MultiBackendUnitTest.php +++ b/core/modules/system/src/Tests/Cache/MultiBackendUnitTest.php @@ -24,7 +24,7 @@ class MultiBackendUnitTest extends GenericCacheBackendUnitTestBase { * A new MultiBackend object. */ protected function createCacheBackend($bin) { - $multi = new MultiBackend(new MemoryBackend('foo'), $bin); + $multi = new MultiBackend(new MemoryBackend('foo'), $this->container->get('lock'), $bin); \Drupal::service('cache_tags.invalidator')->addInvalidator($multi); return $multi; diff --git a/core/modules/views/views.services.yml b/core/modules/views/views.services.yml index 45e7ec3..7dd6edd 100644 --- a/core/modules/views/views.services.yml +++ b/core/modules/views/views.services.yml @@ -1,7 +1,7 @@ services: cache.views_plugin: class: Drupal\Core\Cache\MultiBackend - arguments: ['@cache.discovery', views_plugins] + arguments: ['@cache.discovery','@lock', views_plugins] public: false plugin.manager.views.access: class: Drupal\views\Plugin\ViewsPluginManager