Issue #2162013 by alexpott, Berdir, sun, pwolanin: Critical performance regression due to config schema + TypedData lookups in installer.
parent
360c0c0757
commit
284241ecb9
|
@ -107,7 +107,7 @@ services:
|
|||
class: Drupal\Core\Config\InstallStorage
|
||||
config.typed:
|
||||
class: Drupal\Core\Config\TypedConfigManager
|
||||
arguments: ['@config.storage', '@config.storage.schema']
|
||||
arguments: ['@config.storage', '@config.storage.schema', '@cache.config']
|
||||
database:
|
||||
class: Drupal\Core\Database\Connection
|
||||
factory_class: Drupal\Core\Database\Database
|
||||
|
|
|
@ -390,7 +390,8 @@ function install_begin_request(&$install_state) {
|
|||
|
||||
$container->register('config.typed', 'Drupal\Core\Config\TypedConfigManager')
|
||||
->addArgument(new Reference('config.storage'))
|
||||
->addArgument(new Reference('config.storage.schema'));
|
||||
->addArgument(new Reference('config.storage.schema'))
|
||||
->addArgument(new Reference('cache.config'));
|
||||
|
||||
$container->register('config.factory', 'Drupal\Core\Config\ConfigFactory')
|
||||
->addArgument(new Reference('config.storage'))
|
||||
|
|
|
@ -11,6 +11,7 @@ use Drupal\Component\Utility\NestedArray;
|
|||
use Drupal\Component\Utility\String;
|
||||
use Drupal\Core\Config\ConfigNameException;
|
||||
use Drupal\Core\Config\Context\ContextInterface;
|
||||
use Drupal\Core\Config\Schema\SchemaIncompleteException;
|
||||
use Drupal\Core\TypedData\PrimitiveInterface;
|
||||
use Drupal\Core\TypedData\Type\FloatInterface;
|
||||
use Drupal\Core\TypedData\Type\IntegerInterface;
|
||||
|
@ -516,37 +517,6 @@ class Config {
|
|||
return $this->schemaWrapper;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the definition for the configuration key.
|
||||
*
|
||||
* @param string $key
|
||||
* A string that maps to a key within the configuration data.
|
||||
*
|
||||
* @return \Drupal\Core\Config\Schema\Element
|
||||
*
|
||||
* @throws \Drupal\Core\Config\ConfigException
|
||||
* Thrown when schema is incomplete.
|
||||
*/
|
||||
protected function getSchemaForKey($key) {
|
||||
$parts = explode('.', $key);
|
||||
$schema_wrapper = $this->getSchemaWrapper();
|
||||
if (count($parts) == 1) {
|
||||
$schema = $schema_wrapper->get($key);
|
||||
}
|
||||
else {
|
||||
$schema = clone $schema_wrapper;
|
||||
foreach ($parts as $nested_key) {
|
||||
if (!is_object($schema) || !method_exists($schema, 'get')) {
|
||||
throw new ConfigException(String::format("Incomplete schema for !key key in configuration object !name.", array('!name' => $this->name, '!key' => $key)));
|
||||
}
|
||||
else {
|
||||
$schema = $schema->get($nested_key);
|
||||
}
|
||||
}
|
||||
}
|
||||
return $schema;
|
||||
}
|
||||
|
||||
/**
|
||||
* Casts the value to correct data type using the configuration schema.
|
||||
*
|
||||
|
@ -564,7 +534,7 @@ class Config {
|
|||
}
|
||||
elseif (is_scalar($value)) {
|
||||
try {
|
||||
$element = $this->getSchemaForKey($key);
|
||||
$element = $this->getSchemaWrapper()->get($key);
|
||||
if ($element instanceof PrimitiveInterface) {
|
||||
// Special handling for integers and floats since the configuration
|
||||
// system is primarily concerned with saving values from the Form API
|
||||
|
@ -586,7 +556,7 @@ class Config {
|
|||
$value = $element->getString();
|
||||
}
|
||||
}
|
||||
catch (\Exception $e) {
|
||||
catch (SchemaIncompleteException $e) {
|
||||
// @todo throw an exception due to an incomplete schema. Only possible
|
||||
// once https://drupal.org/node/1910624 is complete.
|
||||
}
|
||||
|
|
|
@ -7,10 +7,8 @@
|
|||
|
||||
namespace Drupal\Core\Config\Schema;
|
||||
|
||||
use Drupal\Component\Utility\NestedArray;
|
||||
use Drupal\Core\TypedData\ComplexDataInterface;
|
||||
use Drupal\Core\TypedData\TypedDataInterface;
|
||||
use \InvalidArgumentException;
|
||||
use Drupal\Component\Utility\String;
|
||||
|
||||
/**
|
||||
* Defines a mapping configuration element.
|
||||
|
@ -36,17 +34,29 @@ class Mapping extends ArrayElement implements ComplexDataInterface {
|
|||
|
||||
/**
|
||||
* Implements Drupal\Core\TypedData\ComplexDataInterface::get().
|
||||
*
|
||||
* Since all configuration objects are mappings the function will except a dot
|
||||
* delimited key to access nested values, for example, 'page.front'.
|
||||
*/
|
||||
public function get($property_name) {
|
||||
$parts = explode('.', $property_name);
|
||||
$root_key = array_shift($parts);
|
||||
$elements = $this->getElements();
|
||||
if (isset($elements[$property_name])) {
|
||||
return $elements[$property_name];
|
||||
if (isset($elements[$root_key])) {
|
||||
$element = $elements[$root_key];
|
||||
}
|
||||
else {
|
||||
throw new InvalidArgumentException(format_string("The configuration property @key doesn't exist.", array(
|
||||
'@key' => $property_name,
|
||||
)));
|
||||
throw new SchemaIncompleteException(String::format("The configuration property @key doesn't exist.", array('@key' => $property_name)));
|
||||
}
|
||||
|
||||
// If $property_name contained a dot recurse into the keys.
|
||||
foreach ($parts as $key) {
|
||||
if (!is_object($element) || !method_exists($element, 'get')) {
|
||||
throw new SchemaIncompleteException(String::format("The configuration property @key does not exist.", array('@key' => $property_name)));
|
||||
}
|
||||
$element = $element->get($key);
|
||||
}
|
||||
return $element;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\Core\Config\Schema\SchemaIncompleteException.
|
||||
*/
|
||||
|
||||
namespace Drupal\Core\Config\Schema;
|
||||
|
||||
/**
|
||||
* An exception thrown when a config schema is incomplete.
|
||||
*/
|
||||
class SchemaIncompleteException extends \RuntimeException {
|
||||
}
|
|
@ -18,7 +18,7 @@ class Sequence extends ArrayElement implements ListInterface {
|
|||
* Overrides ArrayElement::parse()
|
||||
*/
|
||||
protected function parse() {
|
||||
$definition = $definition = $this->getItemDefinition();
|
||||
$definition = $this->getItemDefinition();
|
||||
$elements = array();
|
||||
foreach ($this->value as $key => $value) {
|
||||
$elements[$key] = $this->parseElement($key, $value, $definition);
|
||||
|
@ -60,7 +60,7 @@ class Sequence extends ArrayElement implements ListInterface {
|
|||
* Typed configuration element.
|
||||
*/
|
||||
public function get($key) {
|
||||
$elements = $this->parse();
|
||||
$elements = $this->getElements();
|
||||
return $elements[$key];
|
||||
}
|
||||
|
||||
|
|
|
@ -11,13 +11,20 @@ use Drupal\Component\Plugin\Exception\PluginException;
|
|||
use Drupal\Component\Plugin\PluginManagerBase;
|
||||
use Drupal\Component\Utility\NestedArray;
|
||||
use Drupal\Component\Utility\String;
|
||||
use Drupal\Core\Config\TypedConfigManagerInterface;
|
||||
use Drupal\Core\Cache\CacheBackendInterface;
|
||||
|
||||
/**
|
||||
* Manages config type plugins.
|
||||
*/
|
||||
class TypedConfigManager extends PluginManagerBase implements TypedConfigManagerInterface {
|
||||
|
||||
/**
|
||||
* The cache ID for the definitions.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const CACHE_ID = 'typed_config_definitions';
|
||||
|
||||
/**
|
||||
* A storage controller instance for reading configuration data.
|
||||
*
|
||||
|
@ -39,6 +46,13 @@ class TypedConfigManager extends PluginManagerBase implements TypedConfigManager
|
|||
*/
|
||||
protected $definitions;
|
||||
|
||||
/**
|
||||
* Cache backend for the definitions.
|
||||
*
|
||||
* @var \Drupal\Core\Cache\CacheBackendInterface
|
||||
*/
|
||||
protected $cache;
|
||||
|
||||
/**
|
||||
* Creates a new typed configuration manager.
|
||||
*
|
||||
|
@ -46,10 +60,13 @@ class TypedConfigManager extends PluginManagerBase implements TypedConfigManager
|
|||
* The storage controller object to use for reading schema data
|
||||
* @param \Drupal\Core\Config\StorageInterface $schemaStorage
|
||||
* The storage controller object to use for reading schema data
|
||||
* @param \Drupal\Core\Cache\CacheBackendInterface $cache
|
||||
* The cache backend to use for caching the definitions.
|
||||
*/
|
||||
public function __construct(StorageInterface $configStorage, StorageInterface $schemaStorage) {
|
||||
public function __construct(StorageInterface $configStorage, StorageInterface $schemaStorage, CacheBackendInterface $cache) {
|
||||
$this->configStorage = $configStorage;
|
||||
$this->schemaStorage = $schemaStorage;
|
||||
$this->cache = $cache;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -157,22 +174,34 @@ class TypedConfigManager extends PluginManagerBase implements TypedConfigManager
|
|||
}
|
||||
|
||||
/**
|
||||
* Implements Drupal\Component\Plugin\Discovery\DiscoveryInterface::getDefinitions().
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getDefinitions() {
|
||||
if (!isset($this->definitions)) {
|
||||
$this->definitions = array();
|
||||
foreach ($this->schemaStorage->listAll() as $name) {
|
||||
if ($schema = $this->schemaStorage->read($name)) {
|
||||
if ($cache = $this->cache->get($this::CACHE_ID)) {
|
||||
$this->definitions = $cache->data;
|
||||
}
|
||||
else {
|
||||
$this->definitions = array();
|
||||
foreach ($this->schemaStorage->readMultiple($this->schemaStorage->listAll()) as $schema) {
|
||||
foreach ($schema as $type => $definition) {
|
||||
$this->definitions[$type] = $definition;
|
||||
}
|
||||
}
|
||||
$this->cache->set($this::CACHE_ID, $this->definitions);
|
||||
}
|
||||
}
|
||||
return $this->definitions;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function clearCachedDefinitions() {
|
||||
$this->definitions = NULL;
|
||||
$this->cache->delete($this::CACHE_ID);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets fallback metadata name.
|
||||
*
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
|
||||
namespace Drupal\locale;
|
||||
|
||||
use Drupal\Core\Cache\CacheBackendInterface;
|
||||
use Drupal\Core\Language\Language;
|
||||
use Drupal\Core\Config\TypedConfigManager;
|
||||
use Drupal\Core\Config\StorageInterface;
|
||||
|
@ -47,10 +48,12 @@ class LocaleConfigManager extends TypedConfigManager {
|
|||
* data.
|
||||
* @param \Drupal\locale\StringStorageInterface $localeStorage
|
||||
* The locale storage to use for reading string translations.
|
||||
* @param \Drupal\Core\Cache\CacheBackendInterface $cache
|
||||
* The cache backend to use for caching the definitions.
|
||||
*/
|
||||
public function __construct(StorageInterface $configStorage, StorageInterface $schemaStorage, StorageInterface $installStorage, StringStorageInterface $localeStorage) {
|
||||
public function __construct(StorageInterface $configStorage, StorageInterface $schemaStorage, StorageInterface $installStorage, StringStorageInterface $localeStorage, CacheBackendInterface $cache) {
|
||||
// Note we use the install storage for the parent constructor.
|
||||
parent::__construct($configStorage, $schemaStorage);
|
||||
parent::__construct($configStorage, $schemaStorage, $cache);
|
||||
$this->installStorage = $installStorage;
|
||||
$this->localeStorage = $localeStorage;
|
||||
}
|
||||
|
|
|
@ -46,7 +46,8 @@ class LocaleConfigManagerTest extends DrupalUnitTestBase {
|
|||
$this->container->get('config.storage.installer'),
|
||||
$this->container->get('config.storage.schema'),
|
||||
$this->container->get('config.storage.installer'),
|
||||
$this->container->get('locale.storage')
|
||||
$this->container->get('locale.storage'),
|
||||
$this->container->get('cache.config')
|
||||
);
|
||||
|
||||
$language = new Language(array('id' => 'de'));
|
||||
|
|
|
@ -11,7 +11,7 @@ services:
|
|||
arguments: ['@entity.manager']
|
||||
locale.config.typed:
|
||||
class: Drupal\locale\LocaleConfigManager
|
||||
arguments: ['@config.storage', '@config.storage.schema', '@config.storage.installer', '@locale.storage']
|
||||
arguments: ['@config.storage', '@config.storage.schema', '@config.storage.installer', '@locale.storage', '@cache.config']
|
||||
locale.storage:
|
||||
class: Drupal\locale\StringDatabaseStorage
|
||||
arguments: ['@database']
|
||||
|
|
Loading…
Reference in New Issue