diff --git a/core/lib/Drupal/Core/Update/UpdateBackend.php b/core/lib/Drupal/Core/Update/UpdateBackend.php new file mode 100644 index 0000000000000000000000000000000000000000..3101093b5894c84f10729e7e3f308a639c4615ac --- /dev/null +++ b/core/lib/Drupal/Core/Update/UpdateBackend.php @@ -0,0 +1,54 @@ +<?php + +namespace Drupal\Core\Update; + +use Drupal\Core\Cache\CacheBackendInterface; +use Drupal\Core\Cache\NullBackend; + +/** + * Defines a cache backend for use during Drupal database updates. + * + * Passes on deletes to another backend while extending the NullBackend to avoid + * using anything cached prior to running updates. + */ +class UpdateBackend extends NullBackend { + + /** + * The regular runtime cache backend. + * + * @var \Drupal\Core\Cache\CacheBackendInterface + */ + protected $backend; + + /** + * UpdateBackend constructor. + * + * @param \Drupal\Core\Cache\CacheBackendInterface $backend + * The regular runtime cache backend. + */ + public function __construct(CacheBackendInterface $backend) { + $this->backend = $backend; + } + + /** + * {@inheritdoc} + */ + public function delete($cid) { + $this->backend->delete($cid); + } + + /** + * {@inheritdoc} + */ + public function deleteMultiple(array $cids) { + $this->backend->deleteMultiple($cids); + } + + /** + * {@inheritdoc} + */ + public function deleteAll() { + $this->backend->deleteAll(); + } + +} diff --git a/core/lib/Drupal/Core/Update/UpdateCacheBackendFactory.php b/core/lib/Drupal/Core/Update/UpdateCacheBackendFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..1a459f3a0bd1d2224dc6135bac772b30d6b438ee --- /dev/null +++ b/core/lib/Drupal/Core/Update/UpdateCacheBackendFactory.php @@ -0,0 +1,51 @@ +<?php + +namespace Drupal\Core\Update; + +use Drupal\Core\Cache\CacheFactoryInterface; + +/** + * Cache factory implementation for use during Drupal database updates. + * + * Decorates the regular runtime cache_factory service so that caches use + * \Drupal\Core\Update\UpdateBackend. + * + * @see \Drupal\Core\Update\UpdateServiceProvider::register() + */ +class UpdateCacheBackendFactory implements CacheFactoryInterface { + + /** + * The regular runtime cache_factory service. + * + * @var \Drupal\Core\Cache\CacheFactoryInterface + */ + protected $cacheFactory; + + /** + * Instantiated update cache bins. + * + * @var \Drupal\Core\Update\UpdateBackend[] + */ + protected $bins = []; + + /** + * UpdateCacheBackendFactory constructor. + * + * @param \Drupal\Core\Cache\CacheFactoryInterface $cache_factory + * The regular runtime cache_factory service. + */ + public function __construct(CacheFactoryInterface $cache_factory) { + $this->cacheFactory = $cache_factory; + } + + /** + * {@inheritdoc} + */ + public function get($bin) { + if (!isset($this->bins[$bin])) { + $this->bins[$bin] = new UpdateBackend($this->cacheFactory->get($bin), $bin); + } + return $this->bins[$bin]; + } + +} diff --git a/core/lib/Drupal/Core/Update/UpdateServiceProvider.php b/core/lib/Drupal/Core/Update/UpdateServiceProvider.php index 22c9131eb381ca935dd2fa33ffa809cffab36526..7ac68e6b18e4aab9d5f475af94002d49a04d0098 100644 --- a/core/lib/Drupal/Core/Update/UpdateServiceProvider.php +++ b/core/lib/Drupal/Core/Update/UpdateServiceProvider.php @@ -19,8 +19,16 @@ class UpdateServiceProvider implements ServiceProviderInterface, ServiceModifier */ public function register(ContainerBuilder $container) { $definition = new Definition('Drupal\Core\Cache\NullBackend', ['null']); + $definition->setDeprecated(TRUE, 'The "%service_id%\" service is deprecated. While updating Drupal all caches use \Drupal\Core\Update\UpdateBackend. See https://www.drupal.org/node/3066407'); $container->setDefinition('cache.null', $definition); + // Decorate the cache factory in order to use + // \Drupal\Core\Update\UpdateBackend while running updates. + $container + ->register('update.cache_factory', UpdateCacheBackendFactory::class) + ->setDecoratedService('cache_factory') + ->addArgument(new Reference('update.cache_factory.inner')); + $container->addCompilerPass(new UpdateCompilerPass(), PassConfig::TYPE_REMOVE, 128); } @@ -28,25 +36,6 @@ public function register(ContainerBuilder $container) { * {@inheritdoc} */ public function alter(ContainerBuilder $container) { - // Ensures for some services that they don't cache. - $null_cache_service = new Reference('cache.null'); - - $definition = $container->getDefinition('asset.resolver'); - $definition->replaceArgument(5, $null_cache_service); - - $definition = $container->getDefinition('library.discovery.collector'); - $definition->replaceArgument(0, $null_cache_service); - - $definition = $container->getDefinition('theme.registry'); - $definition->replaceArgument(1, $null_cache_service); - $definition->replaceArgument(7, $null_cache_service); - - $definition = $container->getDefinition('theme.initialization'); - $definition->replaceArgument(2, $null_cache_service); - - $definition = $container->getDefinition('plugin.manager.element_info'); - $definition->replaceArgument(1, $null_cache_service); - // Prevent the alias-based path processor, which requires a path_alias db // table, from being registered to the path processor manager. We do this by // removing the tags that the compiler pass looks for. This means the url diff --git a/core/modules/system/system.install b/core/modules/system/system.install index 018d5d003ed740c0d189e32df2f26ceef1bdf752..218bc72442820c0829d11e2436ba797a347b0a9f 100644 --- a/core/modules/system/system.install +++ b/core/modules/system/system.install @@ -1859,20 +1859,20 @@ function system_update_8011() { * Enable automated cron module and move the config into it. */ function system_update_8013() { - $config_factory = \Drupal::configFactory(); - $system_cron_config = $config_factory->getEditable('system.cron'); - if ($autorun = $system_cron_config->get('threshold.autorun')) { + $autorun = \Drupal::configFactory()->getEditable('system.cron')->get('threshold.autorun'); + if ($autorun) { // Install 'automated_cron' module. \Drupal::service('module_installer')->install(['automated_cron'], FALSE); // Copy 'autorun' value into the new module's 'interval' setting. - $config_factory->getEditable('automated_cron.settings') + \Drupal::configFactory()->getEditable('automated_cron.settings') ->set('interval', $autorun) ->save(TRUE); } // Remove the 'autorun' key in system module config. - $system_cron_config + \Drupal::configFactory() + ->getEditable('system.cron') ->clear('threshold.autorun') ->save(TRUE); } diff --git a/core/modules/system/tests/src/Functional/Update/UpdateCacheTest.php b/core/modules/system/tests/src/Functional/Update/UpdateCacheTest.php new file mode 100644 index 0000000000000000000000000000000000000000..1d9765da4ddf17dca44dab0e2be4e50bb7d66262 --- /dev/null +++ b/core/modules/system/tests/src/Functional/Update/UpdateCacheTest.php @@ -0,0 +1,44 @@ +<?php + +namespace Drupal\Tests\system\Functional\Update; + +use Drupal\Core\Url; +use Drupal\Tests\BrowserTestBase; +use Drupal\Tests\RequirementsPageTrait; + +/** + * Tests caches during updates. + * + * @group Update + */ +class UpdateCacheTest extends BrowserTestBase { + use RequirementsPageTrait; + + /** + * Tests that caches are cleared during updates. + * + * @see \Drupal\Core\Update\UpdateServiceProvider + * @see \Drupal\Core\Update\UpdateBackend + */ + public function testCaches() { + \Drupal::cache()->set('will_not_exist_after_update', TRUE); + // The site might be broken at the time so logging in using the UI might + // not work, so we use the API itself. + $this->writeSettings([ + 'settings' => [ + 'update_free_access' => (object) [ + 'value' => TRUE, + 'required' => TRUE, + ], + ], + ]); + + // Clicking continue should clear the caches. + $this->drupalGet(Url::fromRoute('system.db_update', [], ['path_processing' => FALSE])); + $this->updateRequirementsProblem(); + $this->clickLink(t('Continue')); + + $this->assertFalse(\Drupal::cache()->get('will_not_exist_after_update', FALSE)); + } + +}