diff --git a/core/lib/Drupal/Core/Cache/DatabaseTagBackend.php b/core/lib/Drupal/Core/Cache/DatabaseTagBackend.php index 6f4a143..015d86d 100644 --- a/core/lib/Drupal/Core/Cache/DatabaseTagBackend.php +++ b/core/lib/Drupal/Core/Cache/DatabaseTagBackend.php @@ -8,6 +8,7 @@ namespace Drupal\Core\Cache; use Drupal\Core\Database\Connection; +use Drupal\Core\Database\SchemaObjectExistsException; /** * Class DatabaseTag. @@ -43,17 +44,23 @@ public function invalidateTags(array $tags) { if (isset($invalidated_tags[$tag])) { continue; } - $invalidated_tags[$tag] = TRUE; - unset($tag_cache[$tag]); $this->connection->merge('cache_tags') ->insertFields(array('invalidations' => 1)) ->expression('invalidations', 'invalidations + 1') ->key(array('tag' => $tag)) ->execute(); + + $invalidated_tags[$tag] = TRUE; + unset($tag_cache[$tag]); } } catch (\Exception $e) { - $this->catchException($e, 'cache_tags'); + if ($this->ensureBinExists()) { + $this->invalidateTags($tags); + } + else { + throw $e; + } } } @@ -63,26 +70,35 @@ public function invalidateTags(array $tags) { public function deleteTags(array $tags) { $deleted_tags = &drupal_static('Drupal\Core\Cache\DatabaseTagBackend::deletedTags', array()); $tag_cache = &drupal_static('Drupal\Core\Cache\CacheTagBackendInterface::tagCache', array()); - foreach ($this->flattenTags($tags) as $tag) { - // Only delete tags once per request unless they are written again. - if (isset($deleted_tags[$tag])) { - continue; - } - $deleted_tags[$tag] = TRUE; - unset($tag_cache[$tag]); - try { + try { + + foreach ($this->flattenTags($tags) as $tag) { + // Only delete tags once per request unless they are written again. + if (isset($deleted_tags[$tag])) { + continue; + } $this->connection->merge('cache_tags') ->insertFields(array('deletions' => 1)) ->expression('deletions', 'deletions + 1') ->key(array('tag' => $tag)) ->execute(); + + $deleted_tags[$tag] = TRUE; + unset($tag_cache[$tag]); + } - catch (\Exception $e) { - $this->catchException($e, 'cache_tags'); + } + catch (\Exception $e) { + if ($this->ensureBinExists()) { + $this->deleteTags($tags); + } + else { + throw $e; } } } + /** * {@inheritdoc} */ @@ -107,7 +123,17 @@ public function checksumTags(array $tags, $set_context) { $query_tags = array_diff($tags, array_keys($tag_cache)); if ($query_tags) { - $db_tags = $this->connection->query('SELECT tag, invalidations, deletions FROM {cache_tags} WHERE tag IN (:tags)', array(':tags' => $query_tags))->fetchAllAssoc('tag', \PDO::FETCH_ASSOC); + try { + $db_tags = $this->connection->query('SELECT tag, invalidations, deletions FROM {cache_tags} WHERE tag IN (:tags)', array(':tags' => $query_tags))->fetchAllAssoc('tag', \PDO::FETCH_ASSOC); + } + catch (\Exception $e) { + if ($this->ensureBinExists()) { + $db_tags = $this->connection->query('SELECT tag, invalidations, deletions FROM {cache_tags} WHERE tag IN (:tags)', array(':tags' => $query_tags))->fetchAllAssoc('tag', \PDO::FETCH_ASSOC); + } + else { + throw $e; + } + } $tag_cache += $db_tags; // Fill static cache with empty objects for tags not found in the database. @@ -138,27 +164,63 @@ public function prepareGet(&$item) { } /** - * Act on an exception when cache might be stale. - * - * If the cache_tags table does not yet exist, that's fine but if the table - * exists and yet the query failed, then the cache is stale and the - * exception needs to propagate. - * - * @param $e - * The exception. - * @param string|null $table_name - * The table name, defaults to $this->bin. Can be cache_tags. + * {@inheritdoc}. */ - protected function catchException(\Exception $e, $table_name = NULL) { - if ($this->connection->schema()->tableExists($table_name ?: $this->bin)) { - throw $e; + public function clearCache() { + // Nothing to do here. + } + + /** + * Check if the cache bin exists and create it if not. + */ + protected function ensureBinExists() { + try { + $database_schema = $this->connection->schema(); + if (!$database_schema->tableExists('cache_tags')) { + $schema_definition = $this->schemaDefinition(); + $database_schema->createTable('cache_tags', $schema_definition['cache_tags']); + + return TRUE; + } } + // If another process has already created the cache table, attempting to + // recreate it will throw an exception. In this case just catch the + // exception and do nothing. + catch (SchemaObjectExistsException $e) { + return TRUE; + } + return FALSE; } /** - * {@inheritdoc}. + * Defines the schema for the cache bin and cache_tags table. */ - public function clearCache() { - // Nothing to do here. + public function schemaDefinition() { + $schema['cache_tags'] = array( + 'description' => 'Cache table for tracking cache tags related to the cache bin.', + 'fields' => array( + 'tag' => array( + 'description' => 'Namespace-prefixed tag string.', + 'type' => 'varchar', + 'length' => 255, + 'not null' => TRUE, + 'default' => '', + ), + 'invalidations' => array( + 'description' => 'Number incremented when the tag is invalidated.', + 'type' => 'int', + 'not null' => TRUE, + 'default' => 0, + ), + 'deletions' => array( + 'description' => 'Number incremented when the tag is deleted.', + 'type' => 'int', + 'not null' => TRUE, + 'default' => 0, + ), + ), + 'primary key' => array('tag'), + ); + return $schema; } } diff --git a/core/modules/book/lib/Drupal/book/BookManager.php b/core/modules/book/lib/Drupal/book/BookManager.php index 7b4f3f0..295b417 100644 --- a/core/modules/book/lib/Drupal/book/BookManager.php +++ b/core/modules/book/lib/Drupal/book/BookManager.php @@ -444,7 +444,7 @@ public function deleteFromBook($nid) { } $this->updateOriginalParent($original); $this->books = NULL; - \Drupal::cache('menu')->deleteTags(array('bid' => $original['bid'])); + Cache::deleteTags(array('bid' => $original['bid'])); } /** @@ -744,7 +744,7 @@ public function saveBookLink(array $link, $new) { $query->execute(); } foreach ($affected_bids as $bid) { - \Drupal::cache('menu')->deleteTags(array('bid' => $bid)); + Cache::deleteTags(array('bid' => $bid)); } } diff --git a/core/modules/system/lib/Drupal/system/Tests/Cache/DatabaseBackendTagTest.php b/core/modules/system/lib/Drupal/system/Tests/Cache/DatabaseBackendTagTest.php index 1645779..50660af 100644 --- a/core/modules/system/lib/Drupal/system/Tests/Cache/DatabaseBackendTagTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/Cache/DatabaseBackendTagTest.php @@ -54,6 +54,9 @@ public function testTagInvalidations() { $this->assertTrue($bin->get('test'), 'Cache item was set in bin.'); } + // Let's invalidate non-existing tag to create schema. + Cache::invalidateTags(array('create_schema' => TRUE)); + $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))); @@ -78,6 +81,9 @@ public function testTagDeletetions() { $this->assertTrue($bin->get('test'), 'Cache item was set in bin.'); } + // Let's invalidate non-existing tag to create schema. + Cache::invalidateTags(array('create_schema' => TRUE)); + $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)));