Issue #2224847 by pwolanin, damiankloip, danblack, Berdir: Fixed Automatically shorten cid's in Cache\DatabaseBackend and PhpBackend.
parent
8ca26dcf34
commit
4fb1180802
|
@ -7,6 +7,7 @@
|
|||
|
||||
namespace Drupal\Core\Cache;
|
||||
|
||||
use Drupal\Component\Utility\Crypt;
|
||||
use Drupal\Core\Database\Connection;
|
||||
use Drupal\Core\Database\SchemaObjectExistsException;
|
||||
|
||||
|
@ -62,6 +63,10 @@ class DatabaseBackend implements CacheBackendInterface {
|
|||
* Implements Drupal\Core\Cache\CacheBackendInterface::getMultiple().
|
||||
*/
|
||||
public function getMultiple(&$cids, $allow_invalid = FALSE) {
|
||||
$cid_mapping = array();
|
||||
foreach ($cids as $cid) {
|
||||
$cid_mapping[$this->normalizeCid($cid)] = $cid;
|
||||
}
|
||||
// When serving cached pages, the overhead of using ::select() was found
|
||||
// to add around 30% overhead to the request. Since $this->bin is a
|
||||
// variable, this means the call to ::query() here uses a concatenated
|
||||
|
@ -71,13 +76,15 @@ class DatabaseBackend implements CacheBackendInterface {
|
|||
// ::select() is a much smaller proportion of the request.
|
||||
$result = array();
|
||||
try {
|
||||
$result = $this->connection->query('SELECT cid, data, created, expire, serialized, tags, checksum_invalidations, checksum_deletions FROM {' . $this->connection->escapeTable($this->bin) . '} WHERE cid IN (:cids)', array(':cids' => $cids));
|
||||
$result = $this->connection->query('SELECT cid, data, created, expire, serialized, tags, checksum_invalidations, checksum_deletions FROM {' . $this->connection->escapeTable($this->bin) . '} WHERE cid IN (:cids)', array(':cids' => array_keys($cid_mapping)));
|
||||
}
|
||||
catch (\Exception $e) {
|
||||
// Nothing to do.
|
||||
}
|
||||
$cache = array();
|
||||
foreach ($result as $item) {
|
||||
// Map the cache ID back to the original.
|
||||
$item->cid = $cid_mapping[$item->cid];
|
||||
$item = $this->prepareItem($item, $allow_invalid);
|
||||
if ($item) {
|
||||
$cache[$item->cid] = $item;
|
||||
|
@ -196,7 +203,7 @@ class DatabaseBackend implements CacheBackendInterface {
|
|||
}
|
||||
|
||||
$this->connection->merge($this->bin)
|
||||
->key('cid', $cid)
|
||||
->key('cid', $this->normalizeCid($cid))
|
||||
->fields($fields)
|
||||
->execute();
|
||||
}
|
||||
|
@ -284,14 +291,14 @@ class DatabaseBackend implements CacheBackendInterface {
|
|||
* Implements Drupal\Core\Cache\CacheBackendInterface::deleteMultiple().
|
||||
*/
|
||||
public function deleteMultiple(array $cids) {
|
||||
$cids = array_values(array_map(array($this, 'normalizeCid'), $cids));
|
||||
try {
|
||||
// Delete in chunks when a large array is passed.
|
||||
do {
|
||||
foreach (array_chunk($cids, 1000) as $cids_chunk) {
|
||||
$this->connection->delete($this->bin)
|
||||
->condition('cid', array_splice($cids, 0, 1000), 'IN')
|
||||
->condition('cid', $cids_chunk, 'IN')
|
||||
->execute();
|
||||
}
|
||||
while (count($cids));
|
||||
}
|
||||
catch (\Exception $e) {
|
||||
// Create the cache table, which will be empty. This fixes cases during
|
||||
|
@ -357,15 +364,15 @@ class DatabaseBackend implements CacheBackendInterface {
|
|||
* Implements Drupal\Core\Cache\CacheBackendInterface::invalideMultiple().
|
||||
*/
|
||||
public function invalidateMultiple(array $cids) {
|
||||
$cids = array_values(array_map(array($this, 'normalizeCid'), $cids));
|
||||
try {
|
||||
// Update in chunks when a large array is passed.
|
||||
do {
|
||||
foreach (array_chunk($cids, 1000) as $cids_chunk) {
|
||||
$this->connection->update($this->bin)
|
||||
->fields(array('expire' => REQUEST_TIME - 1))
|
||||
->condition('cid', array_splice($cids, 0, 1000), 'IN')
|
||||
->condition('cid', $cids_chunk, 'IN')
|
||||
->execute();
|
||||
}
|
||||
while (count($cids));
|
||||
}
|
||||
catch (\Exception $e) {
|
||||
$this->catchException($e);
|
||||
|
@ -548,6 +555,26 @@ class DatabaseBackend implements CacheBackendInterface {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures that cache IDs have a maximum length of 255 characters.
|
||||
*
|
||||
* @param string $cid
|
||||
* The passed in cache ID.
|
||||
*
|
||||
* @return string
|
||||
* A cache ID that is at most 255 characters long.
|
||||
*/
|
||||
protected function normalizeCid($cid) {
|
||||
// Nothing to do if the ID length is 255 characters or less.
|
||||
if (strlen($cid) <= 255) {
|
||||
return $cid;
|
||||
}
|
||||
// Return a string that uses as much as possible of the original cache ID
|
||||
// with the hash appended.
|
||||
$hash = Crypt::hashBase64($cid);
|
||||
return substr($cid, 0, 255 - strlen($hash)) . $hash;
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines the schema for the cache bin and cache_tags table.
|
||||
*/
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
namespace Drupal\Core\Cache;
|
||||
|
||||
use Drupal\Core\PhpStorage\PhpStorageFactory;
|
||||
use Drupal\Component\Utility\Variable;
|
||||
use Drupal\Component\Utility\Crypt;
|
||||
|
||||
/**
|
||||
* Defines a PHP cache implementation.
|
||||
|
@ -49,7 +49,22 @@ class PhpBackend implements CacheBackendInterface {
|
|||
* {@inheritdoc}
|
||||
*/
|
||||
public function get($cid, $allow_invalid = FALSE) {
|
||||
if ($file = $this->storage()->getFullPath($cid)) {
|
||||
return $this->getByHash($this->normalizeCid($cid), $allow_invalid);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch a cache item using a hashed cache ID.
|
||||
*
|
||||
* @param string $cidhash
|
||||
* The hashed version of the original cache ID after being normalized.
|
||||
* @param bool $allow_invalid
|
||||
* (optional) If TRUE, a cache item may be returned even if it is expired or
|
||||
* has been invalidated.
|
||||
*
|
||||
* @return bool|mixed
|
||||
*/
|
||||
protected function getByHash($cidhash, $allow_invalid = FALSE) {
|
||||
if ($file = $this->storage()->getFullPath($cidhash)) {
|
||||
$cache = @include $file;
|
||||
}
|
||||
if (isset($cache)) {
|
||||
|
@ -125,14 +140,14 @@ class PhpBackend implements CacheBackendInterface {
|
|||
'expire' => $expire,
|
||||
);
|
||||
$item->tags = $this->flattenTags($tags);
|
||||
$this->writeItem($cid, $item);
|
||||
$this->writeItem($this->normalizeCid($cid), $item);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function delete($cid) {
|
||||
$this->storage()->delete($cid);
|
||||
$this->storage()->delete($this->normalizeCid($cid));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -149,10 +164,10 @@ class PhpBackend implements CacheBackendInterface {
|
|||
*/
|
||||
public function deleteTags(array $tags) {
|
||||
$flat_tags = $this->flattenTags($tags);
|
||||
foreach ($this->storage()->listAll() as $cid) {
|
||||
$item = $this->get($cid);
|
||||
foreach ($this->storage()->listAll() as $cidhash) {
|
||||
$item = $this->getByHash($cidhash);
|
||||
if (is_object($item) && array_intersect($flat_tags, $item->tags)) {
|
||||
$this->delete($cid);
|
||||
$this->delete($item->cid);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -168,9 +183,19 @@ class PhpBackend implements CacheBackendInterface {
|
|||
* {@inheritdoc}
|
||||
*/
|
||||
public function invalidate($cid) {
|
||||
if ($item = $this->get($cid)) {
|
||||
$this->invalidatebyHash($this->normalizeCid($cid));
|
||||
}
|
||||
|
||||
/**
|
||||
* Invalidate one cache item.
|
||||
*
|
||||
* @param string $cidhash
|
||||
* The hashed version of the original cache ID after being normalized.
|
||||
*/
|
||||
protected function invalidatebyHash($cidhash) {
|
||||
if ($item = $this->getByHash($cidhash)) {
|
||||
$item->expire = REQUEST_TIME - 1;
|
||||
$this->writeItem($cid, $item);
|
||||
$this->writeItem($cidhash, $item);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -188,10 +213,10 @@ class PhpBackend implements CacheBackendInterface {
|
|||
*/
|
||||
public function invalidateTags(array $tags) {
|
||||
$flat_tags = $this->flattenTags($tags);
|
||||
foreach ($this->storage()->listAll() as $cid) {
|
||||
$item = $this->get($cid);
|
||||
foreach ($this->storage()->listAll() as $cidhash) {
|
||||
$item = $this->getByHash($cidhash);
|
||||
if ($item && array_intersect($flat_tags, $item->tags)) {
|
||||
$this->invalidate($cid);
|
||||
$this->invalidate($item->cid);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -200,7 +225,9 @@ class PhpBackend implements CacheBackendInterface {
|
|||
* {@inheritdoc}
|
||||
*/
|
||||
public function invalidateAll() {
|
||||
$this->invalidateMultiple($this->storage()->listAll());
|
||||
foreach($this->storage()->listAll() as $cidhash) {
|
||||
$this->invalidatebyHash($cidhash);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -248,20 +275,20 @@ class PhpBackend implements CacheBackendInterface {
|
|||
/**
|
||||
* Writes a cache item to PhpStorage.
|
||||
*
|
||||
* @param string $cid
|
||||
* The cache ID of the data to store.
|
||||
* @param string $cidhash
|
||||
* The hashed version of the original cache ID after being normalized.
|
||||
* @param \stdClass $item
|
||||
* The cache item to store.
|
||||
*/
|
||||
protected function writeItem($cid, \stdClass $item) {
|
||||
protected function writeItem($cidhash, \stdClass $item) {
|
||||
$content = '<?php return unserialize(' . var_export(serialize($item), TRUE) . ');';
|
||||
$this->storage()->save($cid, $content);
|
||||
$this->storage()->save($cidhash, $content);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the PHP code storage object to use.
|
||||
*
|
||||
* @return \Drupal\Core\PhpStorage\PhpStorageInterface
|
||||
* @return \Drupal\Component\PhpStorage\PhpStorageInterface
|
||||
*/
|
||||
protected function storage() {
|
||||
if (!isset($this->storage)) {
|
||||
|
@ -270,4 +297,17 @@ class PhpBackend implements CacheBackendInterface {
|
|||
return $this->storage;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures a normalized cache ID.
|
||||
*
|
||||
* @param string $cid
|
||||
* The passed in cache ID.
|
||||
*
|
||||
* @return string
|
||||
* A normalized cache ID.
|
||||
*/
|
||||
protected function normalizeCid($cid) {
|
||||
return Crypt::hashBase64($cid);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
/**
|
||||
* @file
|
||||
* Definition of Drupal\system\Tests\Cache\GenericCacheBackendUnitTestBase.
|
||||
* Contains \Drupal\system\Tests\Cache\GenericCacheBackendUnitTestBase.
|
||||
*/
|
||||
|
||||
namespace Drupal\system\Tests\Cache;
|
||||
|
@ -207,6 +207,11 @@ abstract class GenericCacheBackendUnitTestBase extends DrupalUnitTestBase {
|
|||
$cached->data->this_should_not_be_in_the_cache = TRUE;
|
||||
$fresh_cached = $backend->get('test7');
|
||||
$this->assertFalse(isset($fresh_cached->data->this_should_not_be_in_the_cache));
|
||||
|
||||
// Check with a long key.
|
||||
$cid = str_repeat('a', 300);
|
||||
$backend->set($cid, 'test');
|
||||
$this->assertEqual('test', $backend->get($cid)->data);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -230,6 +235,11 @@ abstract class GenericCacheBackendUnitTestBase extends DrupalUnitTestBase {
|
|||
|
||||
$backend->delete('test2');
|
||||
$this->assertIdentical(FALSE, $backend->get('test2'), "Backend does not contain data for cache id test2 after deletion.");
|
||||
|
||||
$long_cid = str_repeat('a', 300);
|
||||
$backend->set($long_cid, 'test');
|
||||
$backend->delete($long_cid);
|
||||
$this->assertIdentical(FALSE, $backend->get($long_cid), "Backend does not contain data for long cache id after deletion.");
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -267,6 +277,7 @@ abstract class GenericCacheBackendUnitTestBase extends DrupalUnitTestBase {
|
|||
$backend = $this->getCacheBackend();
|
||||
|
||||
// Set numerous testing keys.
|
||||
$long_cid = str_repeat('a', 300);
|
||||
$backend->set('test1', 1);
|
||||
$backend->set('test2', 3);
|
||||
$backend->set('test3', 5);
|
||||
|
@ -274,6 +285,7 @@ abstract class GenericCacheBackendUnitTestBase extends DrupalUnitTestBase {
|
|||
$backend->set('test5', 11);
|
||||
$backend->set('test6', 13);
|
||||
$backend->set('test7', 17);
|
||||
$backend->set($long_cid, 300);
|
||||
|
||||
// Mismatch order for harder testing.
|
||||
$reference = array(
|
||||
|
@ -341,6 +353,12 @@ abstract class GenericCacheBackendUnitTestBase extends DrupalUnitTestBase {
|
|||
$this->assertFalse(in_array('test2', $cids), "Existing cache id test2 is not in cids array.");
|
||||
$this->assertFalse(in_array('test7', $cids), "Existing cache id test7 is not in cids array.");
|
||||
$this->assertFalse(in_array('test19', $cids), "Added cache id test19 is not in cids array.");
|
||||
|
||||
// Test with a long $cid and non-numeric array key.
|
||||
$cids = array('key:key' => $long_cid);
|
||||
$return = $backend->getMultiple($cids);
|
||||
$this->assertEqual(300, $return[$long_cid]->data);
|
||||
$this->assertTrue(empty($cids));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -420,6 +438,9 @@ abstract class GenericCacheBackendUnitTestBase extends DrupalUnitTestBase {
|
|||
// Test if that expected keys do not exist.
|
||||
$this->assertIdentical(FALSE, $backend->get('test19'), "Cache id test19 does not exist.");
|
||||
$this->assertIdentical(FALSE, $backend->get('test21'), "Cache id test21 does not exist.");
|
||||
|
||||
// Calling deleteMultiple() with an empty array should not cause an error.
|
||||
$this->assertFalse($backend->deleteMultiple(array()));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -528,6 +549,10 @@ abstract class GenericCacheBackendUnitTestBase extends DrupalUnitTestBase {
|
|||
$cids = $reference;
|
||||
$ret = $backend->getMultiple($cids, TRUE);
|
||||
$this->assertEqual(count($ret), 4, 'Four items returned.');
|
||||
|
||||
// Calling invalidateMultiple() with an empty array should not cause an
|
||||
// error.
|
||||
$this->assertFalse($backend->invalidateMultiple(array()));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
Loading…
Reference in New Issue