Issue #3055443 by alexpott, amateescu, catch, Berdir, chr.fritsch: Switch to a memory backend for running the database updates
parent
199ef1a187
commit
c22202ecdb
|
@ -64,26 +64,21 @@ class MemoryBackend implements CacheBackendInterface, CacheTagsInvalidatorInterf
|
|||
* Checks that items are either permanent or did not expire, and returns data
|
||||
* as appropriate.
|
||||
*
|
||||
* @param object $cache
|
||||
* @param array $cache
|
||||
* An item loaded from self::get() or self::getMultiple().
|
||||
* @param bool $allow_invalid
|
||||
* (optional) If TRUE, cache items may be returned even if they have expired
|
||||
* or been invalidated.
|
||||
*
|
||||
* @return mixed
|
||||
* @return object|false
|
||||
* The item with data as appropriate or FALSE if there is no
|
||||
* valid item to load.
|
||||
*/
|
||||
protected function prepareItem($cache, $allow_invalid) {
|
||||
if (!isset($cache->data)) {
|
||||
protected function prepareItem(array $cache, $allow_invalid) {
|
||||
if (!isset($cache['data'])) {
|
||||
return FALSE;
|
||||
}
|
||||
// The object passed into this function is the one stored in $this->cache.
|
||||
// We must clone it as part of the preparation step so that the actual
|
||||
// cache object is not affected by the unserialize() call or other
|
||||
// manipulations of the returned object.
|
||||
|
||||
$prepared = clone $cache;
|
||||
$prepared = (object) $cache;
|
||||
$prepared->data = unserialize($prepared->data);
|
||||
|
||||
// Check expire time.
|
||||
|
@ -104,7 +99,10 @@ class MemoryBackend implements CacheBackendInterface, CacheTagsInvalidatorInterf
|
|||
$tags = array_unique($tags);
|
||||
// Sort the cache tags so that they are stored consistently in the database.
|
||||
sort($tags);
|
||||
$this->cache[$cid] = (object) [
|
||||
|
||||
// Do not create an object at this point to minimize the number of objects
|
||||
// garbage collection has to keep a track off.
|
||||
$this->cache[$cid] = [
|
||||
'cid' => $cid,
|
||||
'data' => serialize($data),
|
||||
'created' => $this->getRequestTime(),
|
||||
|
@ -148,7 +146,7 @@ class MemoryBackend implements CacheBackendInterface, CacheTagsInvalidatorInterf
|
|||
*/
|
||||
public function invalidate($cid) {
|
||||
if (isset($this->cache[$cid])) {
|
||||
$this->cache[$cid]->expire = $this->getRequestTime() - 1;
|
||||
$this->cache[$cid]['expire'] = $this->getRequestTime() - 1;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -158,7 +156,7 @@ class MemoryBackend implements CacheBackendInterface, CacheTagsInvalidatorInterf
|
|||
public function invalidateMultiple(array $cids) {
|
||||
$items = array_intersect_key($this->cache, array_flip($cids));
|
||||
foreach ($items as $cid => $item) {
|
||||
$this->cache[$cid]->expire = $this->getRequestTime() - 1;
|
||||
$this->cache[$cid]['expire'] = $this->getRequestTime() - 1;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -167,8 +165,8 @@ class MemoryBackend implements CacheBackendInterface, CacheTagsInvalidatorInterf
|
|||
*/
|
||||
public function invalidateTags(array $tags) {
|
||||
foreach ($this->cache as $cid => $item) {
|
||||
if (array_intersect($tags, $item->tags)) {
|
||||
$this->cache[$cid]->expire = $this->getRequestTime() - 1;
|
||||
if (array_intersect($tags, $item['tags'])) {
|
||||
$this->cache[$cid]['expire'] = $this->getRequestTime() - 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -178,7 +176,7 @@ class MemoryBackend implements CacheBackendInterface, CacheTagsInvalidatorInterf
|
|||
*/
|
||||
public function invalidateAll() {
|
||||
foreach ($this->cache as $cid => $item) {
|
||||
$this->cache[$cid]->expire = $this->getRequestTime() - 1;
|
||||
$this->cache[$cid]['expire'] = $this->getRequestTime() - 1;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -8,40 +8,29 @@ use Drupal\Core\Cache\MemoryBackend;
|
|||
/**
|
||||
* Defines a memory cache implementation.
|
||||
*
|
||||
* Stores cache items in memory using a PHP array.
|
||||
* Stores cache items in memory using a PHP array. Cache data is not serialized
|
||||
* thereby returning the same object as was cached.
|
||||
*
|
||||
* @ingroup cache
|
||||
*/
|
||||
class MemoryCache extends MemoryBackend implements MemoryCacheInterface {
|
||||
|
||||
/**
|
||||
* Prepares a cached item.
|
||||
*
|
||||
* Checks that items are either permanent or did not expire, and returns data
|
||||
* as appropriate.
|
||||
*
|
||||
* @param object $cache
|
||||
* An item loaded from self::get() or self::getMultiple().
|
||||
* @param bool $allow_invalid
|
||||
* (optional) If TRUE, cache items may be returned even if they have expired
|
||||
* or been invalidated. Defaults to FALSE.
|
||||
*
|
||||
* @return mixed
|
||||
* The item with data as appropriate or FALSE if there is no
|
||||
* valid item to load.
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function prepareItem($cache, $allow_invalid = FALSE) {
|
||||
if (!isset($cache->data)) {
|
||||
protected function prepareItem(array $cache, $allow_invalid = FALSE) {
|
||||
if (!isset($cache['data'])) {
|
||||
return FALSE;
|
||||
}
|
||||
$prepared = (object) $cache;
|
||||
// Check expire time.
|
||||
$cache->valid = $cache->expire == static::CACHE_PERMANENT || $cache->expire >= $this->getRequestTime();
|
||||
$prepared->valid = $prepared->expire == static::CACHE_PERMANENT || $prepared->expire >= $this->getRequestTime();
|
||||
|
||||
if (!$allow_invalid && !$cache->valid) {
|
||||
if (!$allow_invalid && !$prepared->valid) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return $cache;
|
||||
return $prepared;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -51,8 +40,11 @@ class MemoryCache extends MemoryBackend implements MemoryCacheInterface {
|
|||
assert(Inspector::assertAllStrings($tags), 'Cache tags must be strings.');
|
||||
$tags = array_unique($tags);
|
||||
|
||||
$this->cache[$cid] = (object) [
|
||||
// Do not create an object at this point to minimize the number of objects
|
||||
// garbage collection has to keep a track off.
|
||||
$this->cache[$cid] = [
|
||||
'cid' => $cid,
|
||||
// Note that $data is not serialized.
|
||||
'data' => $data,
|
||||
'created' => $this->getRequestTime(),
|
||||
'expire' => $expire,
|
||||
|
|
|
@ -0,0 +1,57 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Core\Update;
|
||||
|
||||
use Drupal\Core\Cache\CacheBackendInterface;
|
||||
use Drupal\Core\Cache\MemoryBackend;
|
||||
|
||||
/**
|
||||
* Defines a cache backend for use during updating Drupal.
|
||||
*
|
||||
* Passes on deletes to another backend while using a memory backend to avoid
|
||||
* using anything cached prior to running updates.
|
||||
*/
|
||||
class UpdateBackend extends MemoryBackend {
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
parent::delete($cid);
|
||||
$this->backend->delete($cid);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function deleteMultiple(array $cids) {
|
||||
parent::deleteMultiple($cids);
|
||||
$this->backend->deleteMultiple($cids);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function deleteAll() {
|
||||
parent::deleteAll();
|
||||
$this->backend->deleteAll();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,51 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Core\Update;
|
||||
|
||||
use Drupal\Core\Cache\CacheFactoryInterface;
|
||||
|
||||
/**
|
||||
* Cache factory implementation for updating Drupal.
|
||||
*
|
||||
* 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));
|
||||
}
|
||||
return $this->bins[$bin];
|
||||
}
|
||||
|
||||
}
|
|
@ -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 @@ class UpdateServiceProvider implements ServiceProviderInterface, ServiceModifier
|
|||
* {@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
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue