diff --git a/core/lib/Drupal/Core/Cache/DatabaseBackend.php b/core/lib/Drupal/Core/Cache/DatabaseBackend.php index ded8b35..24a1603 100644 --- a/core/lib/Drupal/Core/Cache/DatabaseBackend.php +++ b/core/lib/Drupal/Core/Cache/DatabaseBackend.php @@ -9,6 +9,7 @@ use Drupal\Component\Utility\Crypt; use Drupal\Core\Database\Connection; +use Drupal\Core\Database\IntegrityConstraintViolationException; use Drupal\Core\Database\SchemaObjectExistsException; /** @@ -202,6 +203,34 @@ protected function doSet($cid, $data, $expire, $tags) { * {@inheritdoc} */ public function setMultiple(array $items) { + $try_again = FALSE; + try { + // The bin might not yet exist. + $this->doSetMultiple($items); + } + catch (IntegrityConstraintViolationException $e) { + // Ignore exceptions because a key already exists. In this case we + // conflicted with another process that very likely wrote the same data. + return; + } + catch (\Exception $e) { + // If there was an exception, try to create the bins. + if (!$try_again = $this->ensureBinExists()) { + // If the exception happened for other reason than the missing bin + // table, propagate the exception. + throw $e; + } + } + // Now that the bin has been created, try again if necessary. + if ($try_again) { + $this->doSetMultiple($items); + } + } + + /** + * {@inheritdoc} + */ + public function doSetMultiple(array $items) { // Use a transaction so that the database can write the changes in a single // commit. $transaction = $this->connection->startTransaction(); diff --git a/core/lib/Drupal/Core/Cache/DatabaseBackendFactory.php b/core/lib/Drupal/Core/Cache/DatabaseBackendFactory.php index 59b0b22..25a1947 100644 --- a/core/lib/Drupal/Core/Cache/DatabaseBackendFactory.php +++ b/core/lib/Drupal/Core/Cache/DatabaseBackendFactory.php @@ -48,7 +48,13 @@ function __construct(Connection $connection, CacheTagsChecksumInterface $checksu * The cache backend object for the specified cache bin. */ function get($bin) { - return new DatabaseBackend($this->connection, $this->checksumProvider, $bin); + // Use the optimized mysql implementation if possible. + if ($this->connection->databaseType() == 'mysql') { + return new MySqlDatabaseBackend($this->connection, $this->checksumProvider, $bin); + } + else { + return new DatabaseBackend($this->connection, $this->checksumProvider, $bin); + } } } diff --git a/core/lib/Drupal/Core/Cache/MySqlDatabaseBackend.php b/core/lib/Drupal/Core/Cache/MySqlDatabaseBackend.php new file mode 100644 index 0000000..93e6232 --- /dev/null +++ b/core/lib/Drupal/Core/Cache/MySqlDatabaseBackend.php @@ -0,0 +1,72 @@ +doSetMultiple([$cid => [ + 'data' => $data, + 'expire' => $expire, + 'tags' => $tags, + ], + ]); + } + + /** + * {@inheritdoc} + */ + public function doSetMultiple(array $items) { + $query_parts = array(); + $values = array(); + + $index = 0; + foreach ($items as $cid => $item) { + $item += array( + 'expire' => CacheBackendInterface::CACHE_PERMANENT, + 'tags' => array(), + ); + $index++; + + Cache::validateTags($item['tags']); + $item['tags'] = array_unique($item['tags']); + // Sort the cache tags so that they are stored consistently in the DB. + sort($item['tags']); + + $serialized = 0; + if (!is_string($item['data'])) { + $item['data'] = serialize($item['data']); + $serialized = 1; + } + $query_parts[] = "(:cid$index, :created$index, :expire$index, :tags$index, :checksum$index, :data$index, :serialized$index)"; + $values += array( + ":cid$index" => $this->normalizeCid($cid), + ":created$index" => round(microtime(TRUE), 3), + ":expire$index" => $item['expire'], + ":tags$index" => implode(' ', $item['tags']), + ":checksum$index" => $this->checksumProvider->getCurrentChecksum($item['tags']), + ":data$index" => $item['data'], + ":serialized$index" => $serialized + ); + } + + $query = "REPLACE {" . $this->connection->escapeTable($this->bin) . "} (cid, created, expire, tags, checksum, data, serialized) VALUES " . implode(', ', $query_parts); + $this->connection->query($query, $values); + } + +}