diff --git a/core/core.services.yml b/core/core.services.yml index dec001f..9e5fc06 100644 --- a/core/core.services.yml +++ b/core/core.services.yml @@ -45,6 +45,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: diff --git a/core/lib/Drupal/Core/Config/CachedStorage.php b/core/lib/Drupal/Core/Config/CachedStorage.php index 30ad1f7..db5d598 100644 --- a/core/lib/Drupal/Core/Config/CachedStorage.php +++ b/core/lib/Drupal/Core/Config/CachedStorage.php @@ -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. */ @@ -27,13 +27,6 @@ class CachedStorage implements StorageInterface, StorageCacheInterface { protected $storage; /** - * The cache factory. - * - * @var \Drupal\Core\Cache\CacheFactoryInterface - */ - protected $cacheFactory; - - /** * The instantiated Cache backend. * * @var \Drupal\Core\Cache\CacheBackendInterface @@ -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 . ':'; + } + } diff --git a/core/modules/config/src/Tests/Storage/CachedStorageTest.php b/core/modules/config/src/Tests/Storage/CachedStorageTest.php index b289693..11c3599 100644 --- a/core/modules/config/src/Tests/Storage/CachedStorageTest.php +++ b/core/modules/config/src/Tests/Storage/CachedStorageTest.php @@ -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()); diff --git a/core/tests/Drupal/Tests/Core/Config/CachedStorageTest.php b/core/tests/Drupal/Tests/Core/Config/CachedStorageTest.php index 90b29bb..e01e505 100644 --- a/core/tests/Drupal/Tests/Core/Config/CachedStorageTest.php +++ b/core/tests/Drupal/Tests/Core/Config/CachedStorageTest.php @@ -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)); }