Commit 64bd363f authored by webchick's avatar webchick

Issue #2326203 by effulgentsia, alexpott: Fixed Config's cached storage should only use one bin.

parent 1203a092
......@@ -51,6 +51,13 @@ services:
factory_method: get
factory_service: cache_factory
arguments: [bootstrap]
cache.config:
class: Drupal\Core\Cache\CacheBackendInterface
tags:
- { name: cache.bin }
factory_method: get
factory_service: cache_factory
arguments: [config]
cache.default:
class: Drupal\Core\Cache\CacheBackendInterface
tags:
......
......@@ -8,12 +8,12 @@
namespace Drupal\Core\Config;
use Drupal\Core\Cache\Cache;
use Drupal\Core\Cache\CacheFactoryInterface;
use Drupal\Core\Cache\CacheBackendInterface;
/**
* Defines the cached storage.
*
* The class gets another storage and the cache factory injected. It reads from
* The class gets another storage and a cache backend injected. It reads from
* the cache and delegates the read to the storage on a cache miss. It also
* handles cache invalidation.
*/
......@@ -26,13 +26,6 @@ class CachedStorage implements StorageInterface, StorageCacheInterface {
*/
protected $storage;
/**
* The cache factory.
*
* @var \Drupal\Core\Cache\CacheFactoryInterface
*/
protected $cacheFactory;
/**
* The instantiated Cache backend.
*
......@@ -52,20 +45,12 @@ class CachedStorage implements StorageInterface, StorageCacheInterface {
*
* @param \Drupal\Core\Config\StorageInterface $storage
* A configuration storage to be cached.
* @param \Drupal\Core\Cache\CacheFactoryInterface $cache_factory
* A cache factory used for getting cache backends.
* @param \Drupal\Core\Cache\CacheBackendInterface $cache
* A cache backend used to store configuration.
*/
public function __construct(StorageInterface $storage, CacheFactoryInterface $cache_factory) {
public function __construct(StorageInterface $storage, CacheBackendInterface $cache) {
$this->storage = $storage;
$this->cacheFactory = $cache_factory;
$collection = $this->getCollectionName();
if ($collection == StorageInterface::DEFAULT_COLLECTION) {
$bin = 'config';
}
else {
$bin = 'config_' . str_replace('.', '_', $collection);
}
$this->cache = $this->cacheFactory->get($bin);
$this->cache = $cache;
}
/**
......@@ -82,7 +67,8 @@ public function exists($name) {
* Implements Drupal\Core\Config\StorageInterface::read().
*/
public function read($name) {
if ($cache = $this->cache->get($name)) {
$cache_key = $this->getCacheKey($name);
if ($cache = $this->cache->get($cache_key)) {
// The cache contains either the cached configuration data or FALSE
// if the configuration file does not exist.
return $cache->data;
......@@ -90,7 +76,7 @@ public function read($name) {
// Read from the storage on a cache miss and cache the data. Also cache
// information about missing configuration objects.
$data = $this->storage->read($name);
$this->cache->set($name, $data);
$this->cache->set($cache_key, $data);
return $data;
}
......@@ -98,32 +84,41 @@ public function read($name) {
* {@inheritdoc}
*/
public function readMultiple(array $names) {
$list = array();
// The names array is passed by reference and will only contain the names of
// config object not found after the method call.
// @see \Drupal\Core\Cache\CacheBackendInterface::getMultiple()
$cached_list = $this->cache->getMultiple($names);
if (!empty($names)) {
$list = $this->storage->readMultiple($names);
$data_to_return = array();
$cache_keys_map = $this->getCacheKeys($names);
$cache_keys = array_values($cache_keys_map);
$cached_list = $this->cache->getMultiple($cache_keys);
if (!empty($cache_keys)) {
// $cache_keys_map contains the full $name => $cache_key map, while
// $cache_keys contains just the $cache_key values that weren't found in
// the cache.
// @see \Drupal\Core\Cache\CacheBackendInterface::getMultiple()
$names_to_get = array_keys(array_intersect($cache_keys_map, $cache_keys));
$list = $this->storage->readMultiple($names_to_get);
// Cache configuration objects that were loaded from the storage, cache
// missing configuration objects as an explicit FALSE.
$items = array();
foreach ($names as $name) {
$items[$name] = array('data' => isset($list[$name]) ? $list[$name] : FALSE);
foreach ($names_to_get as $name) {
$data = isset($list[$name]) ? $list[$name] : FALSE;
$data_to_return[$name] = $data;
$items[$cache_keys_map[$name]] = array('data' => $data);
}
$this->cache->setMultiple($items);
}
// Add the configuration objects from the cache to the list.
foreach ($cached_list as $name => $cache) {
$list[$name] = $cache->data;
$cache_keys_inverse_map = array_flip($cache_keys_map);
foreach ($cached_list as $cache_key => $cache) {
$name = $cache_keys_inverse_map[$cache_key];
$data_to_return[$name] = $cache->data;
}
// Ensure that only existing configuration objects are returned, filter out
// cached information about missing objects.
return array_filter($list);
return array_filter($data_to_return);
}
/**
......@@ -133,7 +128,7 @@ public function write($name, array $data) {
if ($this->storage->write($name, $data)) {
// While not all written data is read back, setting the cache instead of
// just deleting it avoids cache rebuild stampedes.
$this->cache->set($name, $data);
$this->cache->set($this->getCacheKey($name), $data);
Cache::deleteTags(array($this::FIND_BY_PREFIX_CACHE_TAG => TRUE));
$this->findByPrefixCache = array();
return TRUE;
......@@ -148,7 +143,7 @@ public function delete($name) {
// If the cache was the first to be deleted, another process might start
// rebuilding the cache before the storage is gone.
if ($this->storage->delete($name)) {
$this->cache->delete($name);
$this->cache->delete($this->getCacheKey($name));
Cache::deleteTags(array($this::FIND_BY_PREFIX_CACHE_TAG => TRUE));
$this->findByPrefixCache = array();
return TRUE;
......@@ -163,8 +158,8 @@ public function rename($name, $new_name) {
// If the cache was the first to be deleted, another process might start
// rebuilding the cache before the storage is renamed.
if ($this->storage->rename($name, $new_name)) {
$this->cache->delete($name);
$this->cache->delete($new_name);
$this->cache->delete($this->getCacheKey($name));
$this->cache->delete($this->getCacheKey($new_name));
Cache::deleteTags(array($this::FIND_BY_PREFIX_CACHE_TAG => TRUE));
$this->findByPrefixCache = array();
return TRUE;
......@@ -214,23 +209,24 @@ public function listAll($prefix = '') {
* An array containing matching configuration object names.
*/
protected function findByPrefix($prefix) {
if (!isset($this->findByPrefixCache[$prefix])) {
$cache_key = $this->getCacheKey($prefix);
if (!isset($this->findByPrefixCache[$cache_key])) {
// The : character is not allowed in config file names, so this can not
// conflict.
if ($cache = $this->cache->get('find:' . $prefix)) {
$this->findByPrefixCache[$prefix] = $cache->data;
if ($cache = $this->cache->get('find:' . $cache_key)) {
$this->findByPrefixCache[$cache_key] = $cache->data;
}
else {
$this->findByPrefixCache[$prefix] = $this->storage->listAll($prefix);
$this->findByPrefixCache[$cache_key] = $this->storage->listAll($prefix);
$this->cache->set(
'find:' . $prefix,
$this->findByPrefixCache[$prefix],
'find:' . $cache_key,
$this->findByPrefixCache[$cache_key],
Cache::PERMANENT,
array($this::FIND_BY_PREFIX_CACHE_TAG => TRUE)
);
}
}
return $this->findByPrefixCache[$prefix];
return $this->findByPrefixCache[$cache_key];
}
/**
......@@ -239,9 +235,9 @@ protected function findByPrefix($prefix) {
public function deleteAll($prefix = '') {
// If the cache was the first to be deleted, another process might start
// rebuilding the cache before the storage is renamed.
$cids = $this->storage->listAll($prefix);
$names = $this->storage->listAll($prefix);
if ($this->storage->deleteAll($prefix)) {
$this->cache->deleteMultiple($cids);
$this->cache->deleteMultiple($this->getCacheKeys($names));
return TRUE;
}
return FALSE;
......@@ -260,7 +256,7 @@ public function resetListCache() {
public function createCollection($collection) {
return new static(
$this->storage->createCollection($collection),
$this->cacheFactory
$this->cache
);
}
......@@ -278,4 +274,49 @@ public function getCollectionName() {
return $this->storage->getCollectionName();
}
/**
* Returns a cache key for a configuration name using the collection.
*
* @param string $name
* The configuration name.
*
* @return string
* The cache key for the configuration name.
*/
protected function getCacheKey($name) {
return $this->getCollectionPrefix() . $name;
}
/**
* Returns a cache key map for an array of configuration names.
*
* @param array $names
* The configuration names.
*
* @return array
* An array of cache keys keyed by configuration names.
*/
protected function getCacheKeys(array $names) {
$prefix = $this->getCollectionPrefix();
$cache_keys = array_map(function($name) use ($prefix) {
return $prefix . $name;
}, $names);
return array_combine($names, $cache_keys);
}
/**
* Returns a cache ID prefix to use for the collection.
*
* @return string
* The cache ID prefix.
*/
protected function getCollectionPrefix() {
$collection = $this->storage->getCollectionName();
if ($collection == StorageInterface::DEFAULT_COLLECTION) {
return '';
}
return $collection . ':';
}
}
......@@ -37,7 +37,7 @@ class CachedStorageTest extends ConfigStorageTestBase {
protected function setUp() {
parent::setUp();
$this->filestorage = new FileStorage($this->configDirectories[CONFIG_ACTIVE_DIRECTORY]);
$this->storage = new CachedStorage($this->filestorage, \Drupal::service('cache_factory'));
$this->storage = new CachedStorage($this->filestorage, \Drupal::service('cache.config'));
$this->cache = \Drupal::service('cache_factory')->get('config');
// ::listAll() verifications require other configuration data to exist.
$this->storage->write('system.performance', array());
......
......@@ -19,10 +19,6 @@ class CachedStorageTest extends UnitTestCase {
*/
protected $cacheFactory;
protected function setUp() {
$this->cacheFactory = $this->getMock('Drupal\Core\Cache\CacheFactoryInterface');
}
/**
* Test listAll static cache.
*/
......@@ -37,11 +33,8 @@ public function testListAllStaticCache() {
->will($this->returnValue($response));
$cache = new NullBackend(__FUNCTION__);
$this->cacheFactory->expects($this->once())
->method('get')
->with('config')
->will($this->returnValue($cache));
$cachedStorage = new CachedStorage($storage, $this->cacheFactory);
$cachedStorage = new CachedStorage($storage, $cache);
$this->assertEquals($response, $cachedStorage->listAll($prefix));
$this->assertEquals($response, $cachedStorage->listAll($prefix));
}
......@@ -57,11 +50,8 @@ public function testListAllPrimedPersistentCache() {
$response = array("$prefix." . $this->randomMachineName(), "$prefix." . $this->randomMachineName());
$cache = new MemoryBackend(__FUNCTION__);
$cache->set('find:' . $prefix, $response);
$this->cacheFactory->expects($this->once())
->method('get')
->with('config')
->will($this->returnValue($cache));
$cachedStorage = new CachedStorage($storage, $this->cacheFactory);
$cachedStorage = new CachedStorage($storage, $cache);
$this->assertEquals($response, $cachedStorage->listAll($prefix));
}
......@@ -87,11 +77,8 @@ public function testGetMultipleOnPrimedCache() {
foreach ($configCacheValues as $key => $value) {
$cache->set($key, $value);
}
$this->cacheFactory->expects($this->once())
->method('get')
->with('config')
->will($this->returnValue($cache));
$cachedStorage = new CachedStorage($storage, $this->cacheFactory);
$cachedStorage = new CachedStorage($storage, $cache);
$this->assertEquals($configCacheValues, $cachedStorage->readMultiple($configNames));
}
......@@ -128,14 +115,10 @@ public function testGetMultipleOnPartiallyPrimedCache() {
$storage = $this->getMock('Drupal\Core\Config\StorageInterface');
$storage->expects($this->once())
->method('readMultiple')
->with(array(2 => $configNames[2], 4 => $configNames[4]))
->with(array($configNames[2], $configNames[4]))
->will($this->returnValue($response));
$this->cacheFactory->expects($this->once())
->method('get')
->with('config')
->will($this->returnValue($cache));
$cachedStorage = new CachedStorage($storage, $this->cacheFactory);
$cachedStorage = new CachedStorage($storage, $cache);
$expected_data = $configCacheValues + array($configNames[2] => $config_exists_not_cached_data);
$this->assertEquals($expected_data, $cachedStorage->readMultiple($configNames));
......@@ -159,11 +142,8 @@ public function testReadNonExistentFileCacheMiss() {
->method('read')
->with($name)
->will($this->returnValue(FALSE));
$this->cacheFactory->expects($this->once())
->method('get')
->with('config')
->will($this->returnValue($cache));
$cachedStorage = new CachedStorage($storage, $this->cacheFactory);
$cachedStorage = new CachedStorage($storage, $cache);
$this->assertFalse($cachedStorage->read($name));
......@@ -183,11 +163,8 @@ public function testReadNonExistentFileCached() {
$storage = $this->getMock('Drupal\Core\Config\StorageInterface');
$storage->expects($this->never())
->method('read');
$this->cacheFactory->expects($this->once())
->method('get')
->with('config')
->will($this->returnValue($cache));
$cachedStorage = new CachedStorage($storage, $this->cacheFactory);
$cachedStorage = new CachedStorage($storage, $cache);
$this->assertFalse($cachedStorage->read($name));
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment