Issue #2202537 by alexpott: Avoid repeated cache tag invalidations.

8.0.x
Nathaniel Catchpole 2014-02-24 10:36:22 +00:00
parent 78e5e9bfd9
commit 1747f674f9
3 changed files with 109 additions and 2 deletions

View File

@ -165,12 +165,17 @@ class DatabaseBackend implements CacheBackendInterface {
protected function doSet($cid, $data, $expire, $tags) {
$flat_tags = $this->flattenTags($tags);
$deleted_tags = &drupal_static('Drupal\Core\Cache\DatabaseBackend::deletedTags', array());
// Remove tags that were already deleted during this request from the static
// cache so that another deletion for them will be correctly updated.
$invalidated_tags = &drupal_static('Drupal\Core\Cache\DatabaseBackend::invalidatedTags', array());
// Remove tags that were already deleted or invalidated during this request
// from the static caches so that another deletion or invalidation can
// occur.
foreach ($flat_tags as $tag) {
if (isset($deleted_tags[$tag])) {
unset($deleted_tags[$tag]);
}
if (isset($invalidated_tags[$tag])) {
unset($invalidated_tags[$tag]);
}
}
$checksum = $this->checksumTags($flat_tags);
$fields = array(
@ -301,7 +306,13 @@ class DatabaseBackend implements CacheBackendInterface {
public function invalidateTags(array $tags) {
try {
$tag_cache = &drupal_static('Drupal\Core\Cache\CacheBackendInterface::tagCache', array());
$invalidated_tags = &drupal_static('Drupal\Core\Cache\DatabaseBackend::invalidatedTags', array());
foreach ($this->flattenTags($tags) as $tag) {
// Only invalidate tags once per request unless they are written again.
if (isset($invalidated_tags[$tag])) {
continue;
}
$invalidated_tags[$tag] = TRUE;
unset($tag_cache[$tag]);
$this->connection->merge('cache_tags')
->insertFields(array('invalidations' => 1))

View File

@ -1055,6 +1055,7 @@ abstract class WebTestBase extends TestBase {
// Clear the tag cache.
drupal_static_reset('Drupal\Core\Cache\CacheBackendInterface::tagCache');
drupal_static_reset('Drupal\Core\Cache\DatabaseBackend::deletedTags');
drupal_static_reset('Drupal\Core\Cache\DatabaseBackend::invalidatedTags');
$this->container->get('config.factory')->reset();
$this->container->get('state')->resetCache();

View File

@ -0,0 +1,95 @@
<?php
/**
* @file
* Contains \Drupal\system\Tests\Cache\DatabaseBackendTagTest.
*/
namespace Drupal\system\Tests\Cache;
use Drupal\Core\Cache\Cache;
use Drupal\Core\DependencyInjection\ContainerBuilder;
use Drupal\simpletest\DrupalUnitTestBase;
use Symfony\Component\DependencyInjection\Reference;
/**
* Tests DatabaseBackend cache tag implementation.
*/
class DatabaseBackendTagTest extends DrupalUnitTestBase {
/**
* Modules to enable.
*
* @var array
*/
public static $modules = array('system');
public static function getInfo() {
return array(
'name' => 'Database backend tag test',
'description' => 'Tests database backend cache tag implementation.',
'group' => 'Cache',
);
}
/**
* {@inheritdoc}
*/
public function containerBuild(ContainerBuilder $container) {
parent::containerBuild($container);
// Change container to database cache backends.
$container
->register('cache_factory', 'Drupal\Core\Cache\CacheFactory')
->addArgument(new Reference('settings'))
->addMethodCall('setContainer', array(new Reference('service_container')));
}
public function testTagInvalidations() {
// Create cache entry in multiple bins.
$tags = array('test_tag' => array(1, 2, 3));
$bins = array('path', 'bootstrap', 'page');
foreach ($bins as $bin) {
$bin = \Drupal::cache($bin);
$bin->set('test', 'value', Cache::PERMANENT, $tags);
$this->assertTrue($bin->get('test'), 'Cache item was set in bin.');
}
$invalidations_before = intval(db_select('cache_tags')->fields('cache_tags', array('invalidations'))->condition('tag', 'test_tag:2')->execute()->fetchField());
Cache::invalidateTags(array('test_tag' => array(2)));
// Test that cache entry has been invalidated in multiple bins.
foreach ($bins as $bin) {
$bin = \Drupal::cache($bin);
$this->assertFalse($bin->get('test'), 'Tag invalidation affected item in bin.');
}
// Test that only one tag invalidation has occurred.
$invalidations_after = intval(db_select('cache_tags')->fields('cache_tags', array('invalidations'))->condition('tag', 'test_tag:2')->execute()->fetchField());
$this->assertEqual($invalidations_after, $invalidations_before + 1, 'Only one addition cache tag invalidation has occurred after invalidating a tag used in multiple bins.');
}
public function testTagDeletetions() {
// Create cache entry in multiple bins.
$tags = array('test_tag' => array(1, 2, 3));
$bins = array('path', 'bootstrap', 'page');
foreach ($bins as $bin) {
$bin = \Drupal::cache($bin);
$bin->set('test', 'value', Cache::PERMANENT, $tags);
$this->assertTrue($bin->get('test'), 'Cache item was set in bin.');
}
$deletions_before = intval(db_select('cache_tags')->fields('cache_tags', array('deletions'))->condition('tag', 'test_tag:2')->execute()->fetchField());
Cache::deleteTags(array('test_tag' => array(2)));
// Test that cache entry has been deleted in multiple bins.
foreach ($bins as $bin) {
$bin = \Drupal::cache($bin);
$this->assertFalse($bin->get('test'), 'Tag invalidation affected item in bin.');
}
// Test that only one tag deletion has occurred.
$deletions_after = intval(db_select('cache_tags')->fields('cache_tags', array('deletions'))->condition('tag', 'test_tag:2')->execute()->fetchField());
$this->assertEqual($deletions_after, $deletions_before + 1, 'Only one addition cache tag deletion has occurred after deleting a tag used in multiple bins.');
}
}