DatabaseCacheTagsChecksum.php 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148
  1. <?php
  2. namespace Drupal\Core\Cache;
  3. use Drupal\Core\Database\Connection;
  4. use Drupal\Core\Database\DatabaseException;
  5. /**
  6. * Cache tags invalidations checksum implementation that uses the database.
  7. */
  8. class DatabaseCacheTagsChecksum implements CacheTagsChecksumInterface, CacheTagsInvalidatorInterface {
  9. use CacheTagsChecksumTrait;
  10. /**
  11. * The database connection.
  12. *
  13. * @var \Drupal\Core\Database\Connection
  14. */
  15. protected $connection;
  16. /**
  17. * Constructs a DatabaseCacheTagsChecksum object.
  18. *
  19. * @param \Drupal\Core\Database\Connection $connection
  20. * The database connection.
  21. */
  22. public function __construct(Connection $connection) {
  23. $this->connection = $connection;
  24. }
  25. /**
  26. * {@inheritdoc}
  27. */
  28. protected function doInvalidateTags(array $tags) {
  29. try {
  30. foreach ($tags as $tag) {
  31. $this->connection->merge('cachetags')
  32. ->insertFields(['invalidations' => 1])
  33. ->expression('invalidations', 'invalidations + 1')
  34. ->key('tag', $tag)
  35. ->execute();
  36. }
  37. }
  38. catch (\Exception $e) {
  39. // Create the cache table, which will be empty. This fixes cases during
  40. // core install where cache tags are invalidated before the table is
  41. // created.
  42. if (!$this->ensureTableExists()) {
  43. $this->catchException($e);
  44. }
  45. }
  46. }
  47. /**
  48. * {@inheritdoc}
  49. */
  50. protected function getTagInvalidationCounts(array $tags) {
  51. try {
  52. return $this->connection->query('SELECT tag, invalidations FROM {cachetags} WHERE tag IN ( :tags[] )', [':tags[]' => $tags])
  53. ->fetchAllKeyed();
  54. }
  55. catch (\Exception $e) {
  56. // If the table does not exist yet, create.
  57. if (!$this->ensureTableExists()) {
  58. $this->catchException($e);
  59. }
  60. }
  61. return [];
  62. }
  63. /**
  64. * Check if the cache tags table exists and create it if not.
  65. */
  66. protected function ensureTableExists() {
  67. try {
  68. $database_schema = $this->connection->schema();
  69. // Create the cache tags table if it does not exist.
  70. if (!$database_schema->tableExists('cachetags')) {
  71. $schema_definition = $this->schemaDefinition();
  72. $database_schema->createTable('cachetags', $schema_definition);
  73. return TRUE;
  74. }
  75. }
  76. // If another process has already created the cachetags table, attempting to
  77. // recreate it will throw an exception. In this case just catch the
  78. // exception and do nothing.
  79. catch (DatabaseException $e) {
  80. return TRUE;
  81. }
  82. return FALSE;
  83. }
  84. /**
  85. * Defines the schema for the {cachetags} table.
  86. *
  87. * @internal
  88. */
  89. public function schemaDefinition() {
  90. $schema = [
  91. 'description' => 'Cache table for tracking cache tag invalidations.',
  92. 'fields' => [
  93. 'tag' => [
  94. 'description' => 'Namespace-prefixed tag string.',
  95. 'type' => 'varchar_ascii',
  96. 'length' => 255,
  97. 'not null' => TRUE,
  98. 'default' => '',
  99. ],
  100. 'invalidations' => [
  101. 'description' => 'Number incremented when the tag is invalidated.',
  102. 'type' => 'int',
  103. 'not null' => TRUE,
  104. 'default' => 0,
  105. ],
  106. ],
  107. 'primary key' => ['tag'],
  108. ];
  109. return $schema;
  110. }
  111. /**
  112. * Act on an exception when cache might be stale.
  113. *
  114. * If the {cachetags} table does not yet exist, that's fine but if the table
  115. * exists and yet the query failed, then the cache is stale and the
  116. * exception needs to propagate.
  117. *
  118. * @param \Exception $e
  119. * The exception.
  120. *
  121. * @throws \Exception
  122. */
  123. protected function catchException(\Exception $e) {
  124. if ($this->connection->schema()->tableExists('cachetags')) {
  125. throw $e;
  126. }
  127. }
  128. /**
  129. * {@inheritdoc}
  130. */
  131. public function getDatabaseConnection() {
  132. return $this->connection;
  133. }
  134. }