diff --git a/core/includes/cache.inc b/core/includes/cache.inc index 341d77f..74afcbf 100644 --- a/core/includes/cache.inc +++ b/core/includes/cache.inc @@ -44,19 +44,38 @@ function cache($bin = 'cache') { } /** - * Invalidates the items associated with given list of tags. + * Deletes items from all bins with any of the specified tags. + * + * Many sites have more than one active cache backend, and each backend may use + * a different strategy for storing tags against cache items, and deleting cache + * items associated with a given tag. + * + * When deleting a given list of tags, we iterate over each cache backend, and + * and call deleteTags() on each. + * + * @param array $tags + * The list of tags to delete cache items for. + */ +function cache_delete_tags(array $tags) { + foreach (CacheFactory::getBackends() as $bin => $class) { + cache($bin)->deleteTags($tags); + } +} + +/** + * Marks cache items from all bins with any of the specified tags as invalid. * * Many sites have more than one active cache backend, and each backend my use * a different strategy for storing tags against cache items, and invalidating * cache items associated with a given tag. * * When invalidating a given list of tags, we iterate over each cache backend, - * and call invalidate on each. + * and call invalidateTags() on each. * * @param array $tags * The list of tags to invalidate cache items for. */ -function cache_invalidate(array $tags) { +function cache_invalidate_tags(array $tags) { foreach (CacheFactory::getBackends() as $bin => $class) { cache($bin)->invalidateTags($tags); } diff --git a/core/includes/common.inc b/core/includes/common.inc index edd7c2d..a57b1ac 100644 --- a/core/includes/common.inc +++ b/core/includes/common.inc @@ -6623,7 +6623,7 @@ function drupal_flush_all_caches() { // This is executed based on old/previously known information, which is // sufficient, since new extensions cannot have any primed caches yet. foreach (module_invoke_all('cache_flush') as $bin) { - cache($bin)->flush(); + cache($bin)->deleteAll(); } // Flush asset file caches. diff --git a/core/includes/entity.inc b/core/includes/entity.inc index b3171ea..a866864 100644 --- a/core/includes/entity.inc +++ b/core/includes/entity.inc @@ -50,7 +50,7 @@ function entity_get_info($entity_type = NULL) { function entity_info_cache_clear() { drupal_static_reset('entity_get_info'); // Clear all languages. - cache()->invalidateTags(array('entity_info' => TRUE)); + cache()->deleteTags(array('entity_info' => TRUE)); } /** diff --git a/core/includes/menu.inc b/core/includes/menu.inc index 2e55366..6492b10 100644 --- a/core/includes/menu.inc +++ b/core/includes/menu.inc @@ -2617,7 +2617,7 @@ function menu_link_load($mlid) { * Clears the cached cached data for a single named menu. */ function menu_cache_clear($menu_name = 'tools') { - cache('menu')->invalidateTags(array('menu' => $menu_name)); + cache('menu')->deleteTags(array('menu' => $menu_name)); // Also clear the menu system static caches. menu_reset_static_cache(); } @@ -2629,7 +2629,7 @@ function menu_cache_clear($menu_name = 'tools') { * might have been made to the router items or menu links. */ function menu_cache_clear_all() { - cache('menu')->flush(); + cache('menu')->deleteAll(); menu_reset_static_cache(); } @@ -3258,13 +3258,13 @@ function _menu_clear_page_cache() { // Clear the page and block caches, but at most twice, including at // the end of the page load when there are multiple links saved or deleted. if ($cache_cleared == 0) { - cache_invalidate(array('content' => TRUE)); + cache_invalidate_tags(array('content' => TRUE)); // Keep track of which menus have expanded items. _menu_set_expanded_menus(); $cache_cleared = 1; } elseif ($cache_cleared == 1) { - drupal_register_shutdown_function('cache_invalidate', array('content', TRUE)); + drupal_register_shutdown_function('cache_invalidate_tags', array('content', TRUE)); // Keep track of which menus have expanded items. drupal_register_shutdown_function('_menu_set_expanded_menus'); $cache_cleared = 2; diff --git a/core/includes/schema.inc b/core/includes/schema.inc index aab8f8a..7661dcb 100644 --- a/core/includes/schema.inc +++ b/core/includes/schema.inc @@ -103,7 +103,7 @@ function drupal_get_complete_schema($rebuild = FALSE) { drupal_alter('schema', $schema); if ($rebuild) { - cache()->invalidateTags(array('schema' => TRUE)); + cache()->deleteTags(array('schema' => TRUE)); } // If the schema is empty, avoid saving it: some database engines require // the schema to perform queries, and this could lead to infinite loops. diff --git a/core/lib/Drupal/Core/Cache/BackendChain.php b/core/lib/Drupal/Core/Cache/BackendChain.php index 4918767..f913d5f 100644 --- a/core/lib/Drupal/Core/Cache/BackendChain.php +++ b/core/lib/Drupal/Core/Cache/BackendChain.php @@ -31,6 +31,15 @@ class BackendChain implements CacheBackendInterface { protected $backends = array(); /** + * Constructs a DatabaseBackend object. + * + * @param string $bin + * The cache bin for which the object is created. + */ + public function __construct($bin) { + } + + /** * Appends a cache backend to the cache chain. * * @param CacheBackendInterface $backend @@ -61,17 +70,11 @@ class BackendChain implements CacheBackendInterface { } /** - * Implements Drupal\Core\Cache\CacheBackendInterface::__construct(). - */ - public function __construct($bin) { - } - - /** * Implements Drupal\Core\Cache\CacheBackendInterface::get(). */ - public function get($cid) { + public function get($cid, $allowInvalid = FALSE) { foreach ($this->backends as $index => $backend) { - if (($return = $backend->get($cid)) !== FALSE) { + if (($return = $backend->get($cid, $allowInvalid)) !== FALSE) { // We found a result, propagate it to all missed backends. if ($index > 0) { for ($i = ($index - 1); 0 <= $i; --$i) { @@ -89,11 +92,11 @@ class BackendChain implements CacheBackendInterface { /** * Implements Drupal\Core\Cache\CacheBackendInterface::getMultiple(). */ - function getMultiple(&$cids) { + public function getMultiple(&$cids, $allowInvalid = FALSE) { $return = array(); foreach ($this->backends as $index => $backend) { - $items = $backend->getMultiple($cids); + $items = $backend->getMultiple($cids, $allowInvalid); // Propagate the values that could be retrieved from the current cache // backend to all missed backends. @@ -120,7 +123,7 @@ function getMultiple(&$cids) { /** * Implements Drupal\Core\Cache\CacheBackendInterface::set(). */ - function set($cid, $data, $expire = CacheBackendInterface::CACHE_PERMANENT, array $tags = array()) { + public function set($cid, $data, $expire = CacheBackendInterface::CACHE_PERMANENT, array $tags = array()) { foreach ($this->backends as $backend) { $backend->set($cid, $data, $expire, $tags); } @@ -129,7 +132,7 @@ function set($cid, $data, $expire = CacheBackendInterface::CACHE_PERMANENT, arra /** * Implements Drupal\Core\Cache\CacheBackendInterface::delete(). */ - function delete($cid) { + public function delete($cid) { foreach ($this->backends as $backend) { $backend->delete($cid); } @@ -138,27 +141,54 @@ function delete($cid) { /** * Implements Drupal\Core\Cache\CacheBackendInterface::deleteMultiple(). */ - function deleteMultiple(array $cids) { + public function deleteMultiple(array $cids) { foreach ($this->backends as $backend) { $backend->deleteMultiple($cids); } } /** - * Implements Drupal\Core\Cache\CacheBackendInterface::flush(). + * Implements Drupal\Core\Cache\CacheBackendInterface::deleteTags(). */ - public function flush() { + public function deleteTags(array $tags) { foreach ($this->backends as $backend) { - $backend->flush(); + $backend->deleteTags($tags); + } + } + + /** + * Implements Drupal\Core\Cache\CacheBackendInterface::deleteAll(). + */ + public function deleteAll() { + foreach ($this->backends as $backend) { + $backend->deleteAll(); } } /** * Implements Drupal\Core\Cache\CacheBackendInterface::expire(). */ - public function expire() { + public function deleteExpired() { + foreach ($this->backends as $backend) { + $backend->deleteExpired(); + } + } + + /** + * Implements Drupal\Core\Cache\CacheBackendInterface::invalidate(). + */ + public function invalidate($cid) { + foreach ($this->backends as $backend) { + $backend->invalidate($cid); + } + } + + /** + * Implements Drupal\Core\Cache\CacheBackendInterface::invalidateMultiple(). + */ + public function invalidateMultiple(array $cids) { foreach ($this->backends as $backend) { - $backend->expire(); + $backend->invalidateMultiple($cids); } } diff --git a/core/lib/Drupal/Core/Cache/CacheBackendInterface.php b/core/lib/Drupal/Core/Cache/CacheBackendInterface.php index ee1df76..4c30559 100644 --- a/core/lib/Drupal/Core/Cache/CacheBackendInterface.php +++ b/core/lib/Drupal/Core/Cache/CacheBackendInterface.php @@ -48,54 +48,59 @@ interface CacheBackendInterface { /** - * Indicates that the item should never be removed unless explicitly selected. - * - * The item may be removed using cache()->delete() with a cache ID. + * Indicates that the item should never be removed unless explicitly deleted. */ const CACHE_PERMANENT = 0; /** * Returns data from the persistent cache. * - * Data may be stored as either plain text or as serialized data. cache_get() - * will automatically return unserialized objects and arrays. - * - * @param $cid + * @param string $cid * The cache ID of the data to retrieve. + * @param bool $allowInvalid + * (optional) If TRUE, a cache item may be returned even if it is expired or + * has been invalidated. The "valid" property of the returned object + * indicates whether the item is valid or not. Defaults to FALSE. * - * @return - * The cache or FALSE on failure. + * @return object|false + * The cache item or FALSE on failure. + * + * @see Drupal\Core\Cache\CacheBackendInterface::getMultiple() */ - public function get($cid); + public function get($cid, $allowInvalid = FALSE); /** * Returns data from the persistent cache when given an array of cache IDs. * - * @param $cids + * @param array $cids * An array of cache IDs for the data to retrieve. This is passed by * reference, and will have the IDs successfully returned from cache * removed. + * @param bool $allowInvalid + * (optional) If TRUE, cache items may be returned even if they have expired + * or been invalidated. The "valid" property of the returned objects + * indicates whether each item is valid or not. Defaults to FALSE. * - * @return - * An array of the items successfully returned from cache indexed by cid. + * @return array + * An array of cache item objects indexed by cache ID. + * + * @see Drupal\Core\Cache\CacheBackendInterface::get() */ - public function getMultiple(&$cids); + public function getMultiple(&$cids, $allowInvalid = FALSE); /** * Stores data in the persistent cache. * - * @param $cid + * @param string $cid * The cache ID of the data to store. - * @param $data - * The data to store in the cache. Complex data types will be automatically - * serialized before insertion. - * Strings will be stored as plain text and not serialized. - * @param $expire + * @param mixed $data + * The data to store in the cache. + * @param int $expire * One of the following values: - * - CacheBackendInterface::CACHE_PERMANENT: Indicates that the item - * should never be removed unless cache->delete($cid) is used explicitly. - * - A Unix timestamp: Indicates that the item should be kept at least until - * the given time. + * - CacheBackendInterface::CACHE_PERMANENT: Indicates that the item should + * not be removed unless it is deleted explicitly. + * - A Unix timestamp: Indicates that the item will be considered invalid + * after this time. * @param array $tags * An array of tags to be stored with the cache item. These should normally * identify objects used to build the cache item, which should trigger @@ -108,42 +113,132 @@ /** * Deletes an item from the cache. * - * @param $cid - * The cache ID to delete. + * If the cache item is being deleted because it is no longer "fresh", you may + * consider using invalidate() instead. This allows callers to retrieve the + * invalid item by calling get() with $allowInvalid set to TRUE. In some cases + * an invalid item may be acceptable rather than having to rebuild the cache. + * + * @param string $cid + * The cache ID to delete. + * + * @see Drupal\Core\Cache\CacheBackendInterface::invalidate() + * @see Drupal\Core\Cache\CacheBackendInterface::deleteMultiple() + * @see Drupal\Core\Cache\CacheBackendInterface::deleteTags() + * @see Drupal\Core\Cache\CacheBackendInterface::deleteAll() */ public function delete($cid); /** * Deletes multiple items from the cache. * - * @param $cids - * An array of $cids to delete. + * If the cache items are being deleted because it is no longer "fresh", you + * may consider using invalidateMultiple() instead. This allows callers to + * retrieve the invalid items by calling get() with $allowInvalid set to TRUE. + * In some cases an invalid item may be acceptable rather than having to + * rebuild the cache. + * + * @param array $cids + * An array of cache IDs to delete. + * + * @see Drupal\Core\Cache\CacheBackendInterface::invalidateMultiple() + * @see Drupal\Core\Cache\CacheBackendInterface::delete() + * @see Drupal\Core\Cache\CacheBackendInterface::deleteTags() + * @see Drupal\Core\Cache\CacheBackendInterface::deleteAll() + * @see Drupal\Core\Cache\CacheBackendInterface::deleteExpired() + */ + public function deleteMultiple(array $cids); + + /** + * Deletes items with any of the specified tags. + * + * If the cache items are being deleted because it is no longer "fresh", you + * may consider using invalidateTags() instead. This allows callers to + * retrieve the invalid items by calling get() with $allowInvalid set to TRUE. + * In some cases an invalid item may be acceptable rather than having to + * rebuild the cache. + * + * @param array $tags + * Associative array of tags, in the same format that is passed to + * CacheBackendInterface::set(). + * + * @see Drupal\Core\Cache\CacheBackendInterface::set() + * @see Drupal\Core\Cache\CacheBackendInterface::invalidateTags() + * @see Drupal\Core\Cache\CacheBackendInterface::delete() + * @see Drupal\Core\Cache\CacheBackendInterface::deleteMultiple() + * @see Drupal\Core\Cache\CacheBackendInterface::deleteAll() + * @see Drupal\Core\Cache\CacheBackendInterface::deleteExpired() */ - public function deleteMultiple(Array $cids); + public function deleteTags(array $tags); /** - * Flushes all cache items in a bin. + * Deletes all cache items in a bin. + * + * @see Drupal\Core\Cache\CacheBackendInterface::delete() + * @see Drupal\Core\Cache\CacheBackendInterface::deleteMultiple() + * @see Drupal\Core\Cache\CacheBackendInterface::deleteTags() + * @see Drupal\Core\Cache\CacheBackendInterface::deleteExpired() */ - public function flush(); + public function deleteAll(); /** - * Expires temporary items from the cache. + * Deletes expired items from the cache. + * + * @see Drupal\Core\Cache\CacheBackendInterface::delete() + * @see Drupal\Core\Cache\CacheBackendInterface::deleteMultiple() + * @see Drupal\Core\Cache\CacheBackendInterface::deleteTags() + * @see Drupal\Core\Cache\CacheBackendInterface::deleteAll() + * @see Drupal\Core\Cache\CacheBackendInterface::deleteExpired() */ - public function expire(); + public function deleteExpired(); /** - * Invalidates each tag in the $tags array. + * Marks a cache item as invalid. + * + * Invalid items may be returned in later calls to get(), if the $allowInvalid + * argument is TRUE. + * + * @param string $cid + * The cache ID to invalidate. + * + * @see Drupal\Core\Cache\CacheBackendInterface::delete() + * @see Drupal\Core\Cache\CacheBackendInterface::invalidateMultiple() + * @see Drupal\Core\Cache\CacheBackendInterface::invalidateTags() + */ + public function invalidate($cid); + + /** + * Marks cache items as invalid. + * + * Invalid items may be returned in later calls to get(), if the $allowInvalid + * argument is TRUE. + * + * @param string $cids + * An array of cache IDs to invalidate. + * + * @see Drupal\Core\Cache\CacheBackendInterface::deleteMultiple() + * @see Drupal\Core\Cache\CacheBackendInterface::invalidate() + * @see Drupal\Core\Cache\CacheBackendInterface::invalidateTags() + */ + public function invalidateMultiple(array $cids); + + /** + * Marks cache items with any of the specified tags as invalid. * * @param array $tags * Associative array of tags, in the same format that is passed to * CacheBackendInterface::set(). * - * @see CacheBackendInterface::set() + * @see Drupal\Core\Cache\CacheBackendInterface::set() + * @see Drupal\Core\Cache\CacheBackendInterface::deleteTags() + * @see Drupal\Core\Cache\CacheBackendInterface::invalidate() + * @see Drupal\Core\Cache\CacheBackendInterface::invalidateMultiple() */ public function invalidateTags(array $tags); /** * Performs garbage collection on a cache bin. + * + * The backend may choose to delete expired or invalidated items. */ public function garbageCollection(); diff --git a/core/lib/Drupal/Core/Cache/DatabaseBackend.php b/core/lib/Drupal/Core/Cache/DatabaseBackend.php index a12ed13..a82df6a 100644 --- a/core/lib/Drupal/Core/Cache/DatabaseBackend.php +++ b/core/lib/Drupal/Core/Cache/DatabaseBackend.php @@ -9,6 +9,7 @@ use Drupal\Core\Database\Database; use Exception; +use PDO; /** * Defines a default cache implementation. @@ -24,11 +25,6 @@ class DatabaseBackend implements CacheBackendInterface { protected $bin; /** - * A static cache of all tags checked during the request. - */ - protected static $tagCache = array(); - - /** * Constructs a DatabaseBackend object. * * @param string $bin @@ -46,16 +42,16 @@ class DatabaseBackend implements CacheBackendInterface { /** * Implements Drupal\Core\Cache\CacheBackendInterface::get(). */ - public function get($cid) { + public function get($cid, $allowInvalid = FALSE) { $cids = array($cid); - $cache = $this->getMultiple($cids); + $cache = $this->getMultiple($cids, $allowInvalid); return reset($cache); } /** * Implements Drupal\Core\Cache\CacheBackendInterface::getMultiple(). */ - public function getMultiple(&$cids) { + public function getMultiple(&$cids, $allowInvalid = FALSE) { try { // When serving cached pages, the overhead of using ::select() was found // to add around 30% overhead to the request. Since $this->bin is a @@ -64,10 +60,10 @@ class DatabaseBackend implements CacheBackendInterface { // is used here only due to the performance overhead we would incur // otherwise. When serving an uncached page, the overhead of using // ::select() is a much smaller proportion of the request. - $result = Database::getConnection()->query('SELECT cid, data, created, expire, serialized, tags, checksum FROM {' . Database::getConnection()->escapeTable($this->bin) . '} WHERE cid IN (:cids)', array(':cids' => $cids)); + $result = Database::getConnection()->query('SELECT cid, data, created, expire, serialized, tags, checksum_invalidations, checksum_deletions FROM {' . Database::getConnection()->escapeTable($this->bin) . '} WHERE cid IN (:cids)', array(':cids' => $cids)); $cache = array(); foreach ($result as $item) { - $item = $this->prepareItem($item); + $item = $this->prepareItem($item, $allowInvalid); if ($item) { $cache[$item->cid] = $item; } @@ -90,22 +86,37 @@ class DatabaseBackend implements CacheBackendInterface { * * @param stdClass $cache * An item loaded from cache_get() or cache_get_multiple(). + * @param bool $allowInvalid + * If FALSE, the method returns FALSE if the cache item is not valid. * - * @return mixed - * The item with data unserialized as appropriate or FALSE if there is no - * valid item to load. + * @return mixed|false + * The item with data unserialized as appropriate and a property indicating + * whether the item is valid, or FALSE if there is no valid item to load. */ - protected function prepareItem($cache) { + protected function prepareItem($cache, $allowInvalid) { if (!isset($cache->data)) { return FALSE; } - // The cache data is invalid if any of its tags have been cleared since. - if ($cache->tags) { - $cache->tags = explode(' ', $cache->tags); - if (!$this->validTags($cache->checksum, $cache->tags)) { - return FALSE; - } + $cache->tags = $cache->tags ? explode(' ', $cache->tags) : array(); + + $checksum = $this->checksumTags($cache->tags); + + // Check if deleteTags() has been called with any of the entry's tags. + if ($cache->checksum_deletions != $checksum['deletions']) { + return FALSE; + } + + // Check expire time. + $cache->valid = $cache->expire == CacheBackendInterface::CACHE_PERMANENT || $cache->expire >= REQUEST_TIME; + + // Check if invalidateTags() has been called with any of the entry's tags. + if ($cache->checksum_invalidations != $checksum['invalidations']) { + $cache->valid = FALSE; + } + + if (!$allowInvalid && !$cache->valid) { + return FALSE; } // Unserialize and return the cached data. @@ -120,23 +131,26 @@ class DatabaseBackend implements CacheBackendInterface { * Implements Drupal\Core\Cache\CacheBackendInterface::set(). */ public function set($cid, $data, $expire = CacheBackendInterface::CACHE_PERMANENT, array $tags = array()) { - $fields = array( - 'serialized' => 0, - 'created' => REQUEST_TIME, - 'expire' => $expire, - 'tags' => implode(' ', $this->flattenTags($tags)), - 'checksum' => $this->checksumTags($tags), - ); - if (!is_string($data)) { - $fields['data'] = serialize($data); - $fields['serialized'] = 1; - } - else { - $fields['data'] = $data; - $fields['serialized'] = 0; - } - try { + $flat_tags = $this->flattenTags($tags); + $checksum = $this->checksumTags($flat_tags); + $fields = array( + 'serialized' => 0, + 'created' => REQUEST_TIME, + 'expire' => $expire, + 'tags' => implode(' ', $flat_tags), + 'checksum_invalidations' => $checksum['invalidations'], + 'checksum_deletions' => $checksum['deletions'], + ); + if (!is_string($data)) { + $fields['data'] = serialize($data); + $fields['serialized'] = 1; + } + else { + $fields['data'] = $data; + $fields['serialized'] = 0; + } + Database::getConnection()->merge($this->bin) ->key(array('cid' => $cid)) ->fields($fields) @@ -170,16 +184,31 @@ class DatabaseBackend implements CacheBackendInterface { } /** - * Implements Drupal\Core\Cache\CacheBackendInterface::flush(). + * Implements Drupal\Core\Cache\CacheBackendInterface::deleteTags(). */ - public function flush() { + public function deleteTags(array $tags) { + $tag_cache = &drupal_static('Drupal\Core\Cache\CacheBackendInterface::tagCache'); + foreach ($this->flattenTags($tags) as $tag) { + unset($tag_cache[$tag]); + Database::getConnection()->merge('cache_tags') + ->insertFields(array('deletions' => 1)) + ->expression('deletions', 'deletions + 1') + ->key(array('tag' => $tag)) + ->execute(); + } + } + + /** + * Implements Drupal\Core\Cache\CacheBackendInterface::deleteAll(). + */ + public function deleteAll() { Database::getConnection()->truncate($this->bin)->execute(); } /** - * Implements Drupal\Core\Cache\CacheBackendInterface::expire(). + * Implements Drupal\Core\Cache\CacheBackendInterface::deleteExpired(). */ - public function expire() { + public function deleteExpired() { Database::getConnection()->delete($this->bin) ->condition('expire', CacheBackendInterface::CACHE_PERMANENT, '<>') ->condition('expire', REQUEST_TIME, '<') @@ -187,36 +216,56 @@ class DatabaseBackend implements CacheBackendInterface { } /** - * Implements Drupal\Core\Cache\CacheBackendInterface::garbageCollection(). + * Implements Drupal\Core\Cache\CacheBackendInterface::invalidate(). */ - public function garbageCollection() { - $this->expire(); + public function invalidate($cid) { + $this->invalidateMultiple(array($cid)); } /** - * Compares two checksums of tags. Used to determine whether to serve a cached - * item or treat it as invalidated. - * - * @param integer $checksum - * The initial checksum to compare against. - * @param array $tags - * An array of tags to calculate a checksum for. - * - * @return boolean - * TRUE if the checksums match, FALSE otherwise. + * Implements Drupal\Core\Cache\CacheBackendInterface::invalideMultiple(). + */ + public function invalidateMultiple(array $cids) { + // Update in chunks when a large array is passed. + do { + Database::getConnection()->update($this->bin) + ->fields(array('expire' => REQUEST_TIME - 1)) + ->condition('cid', array_splice($cids, 0, 1000), 'IN') + ->execute(); + } + while (count($cids)); + } + + /** + * Implements Drupal\Core\Cache\CacheBackendInterface::invalidateTags(). */ - protected function validTags($checksum, array $tags) { - return $checksum == $this->checksumTags($tags); + public function invalidateTags(array $tags) { + $tag_cache = &drupal_static('Drupal\Core\Cache\CacheBackendInterface::tagCache'); + foreach ($this->flattenTags($tags) as $tag) { + unset($tag_cache[$tag]); + Database::getConnection()->merge('cache_tags') + ->insertFields(array('invalidations' => 1)) + ->expression('invalidations', 'invalidations + 1') + ->key(array('tag' => $tag)) + ->execute(); + } } /** - * Flattens a tags array into a numeric array suitable for string storage. + * Implements Drupal\Core\Cache\CacheBackendInterface::garbageCollection(). + */ + public function garbageCollection() { + $this->deleteExpired(); + } + + /** + * 'Flattens' a tags array into an array of strings. * * @param array $tags * Associative array of tags to flatten. * * @return array - * Numeric array of flattened tag identifiers. + * An indexed array of flattened tag identifiers. */ protected function flattenTags(array $tags) { if (isset($tags[0])) { @@ -238,51 +287,38 @@ class DatabaseBackend implements CacheBackendInterface { } /** - * Implements Drupal\Core\Cache\CacheBackendInterface::invalidateTags(). - */ - public function invalidateTags(array $tags) { - foreach ($this->flattenTags($tags) as $tag) { - unset(self::$tagCache[$tag]); - Database::getConnection()->merge('cache_tags') - ->key(array('tag' => $tag)) - ->fields(array('invalidations' => 1)) - ->expression('invalidations', 'invalidations + 1') - ->execute(); - } - } - - /** * Returns the sum total of validations for a given set of tags. * * @param array $tags - * Associative array of tags. + * Array of flat tags. * - * @return integer + * @return int * Sum of all invalidations. + * + * @see Drupal\Core\Cache\DatabaseBackend::flattenTags() */ - protected function checksumTags($tags) { - $checksum = 0; - $query_tags = array(); + protected function checksumTags($flat_tags) { + $tag_cache = &drupal_static('Drupal\Core\Cache\CacheBackendInterface::tagCache', array()); - foreach ($this->flattenTags($tags) as $tag) { - if (isset(self::$tagCache[$tag])) { - $checksum += self::$tagCache[$tag]; - } - else { - $query_tags[] = $tag; - } - } + $checksum = array( + 'invalidations' => 0, + 'deletions' => 0, + ); + + $query_tags = array_diff($flat_tags, array_keys($tag_cache)); if ($query_tags) { - try { - if ($db_tags = Database::getConnection()->query('SELECT tag, invalidations FROM {cache_tags} WHERE tag IN (:tags)', array(':tags' => $query_tags))->fetchAllKeyed()) { - self::$tagCache = array_merge(self::$tagCache, $db_tags); - $checksum += array_sum($db_tags); - } - } - catch (Exception $e) { - // The database may not be available, so we'll ignore cache_set requests. - } + $db_tags = Database::getConnection()->query('SELECT tag, invalidations, deletions FROM {cache_tags} WHERE tag IN (:tags)', array(':tags' => $query_tags))->fetchAllAssoc('tag', PDO::FETCH_ASSOC); + $tag_cache += $db_tags; + + // Fill static cache with empty objects for tags not found in the database. + $tag_cache += array_fill_keys(array_diff($query_tags, array_keys($db_tags)), $checksum); } + + foreach ($flat_tags as $tag) { + $checksum['invalidations'] += $tag_cache[$tag]['invalidations']; + $checksum['deletions'] += $tag_cache[$tag]['deletions']; + } + return $checksum; } diff --git a/core/lib/Drupal/Core/Cache/InstallBackend.php b/core/lib/Drupal/Core/Cache/InstallBackend.php index e23294a..b2ae035 100644 --- a/core/lib/Drupal/Core/Cache/InstallBackend.php +++ b/core/lib/Drupal/Core/Cache/InstallBackend.php @@ -36,14 +36,14 @@ class InstallBackend extends DatabaseBackend { /** * Overrides Drupal\Core\Cache\DatabaseBackend::get(). */ - public function get($cid) { + public function get($cid, $allowInvalid = FALSE) { return FALSE; } /** * Overrides Drupal\Core\Cache\DatabaseBackend::getMultiple(). */ - public function getMultiple(&$cids) { + public function getMultiple(&$cids, $allowInvalid = FALSE) { return array(); } @@ -77,36 +77,72 @@ class InstallBackend extends DatabaseBackend { } /** - * Overrides Drupal\Core\Cache\DatabaseBackend::invalidateTags(). + * Overrides Drupal\Core\Cache\DatabaseBackend::deleteAll(). */ - public function invalidateTags(array $tags) { + public function deleteAll() { try { if (class_exists('Drupal\Core\Database\Database')) { - parent::invalidateTags($tags); + parent::deleteAll(); } } catch (Exception $e) {} } /** - * Overrides Drupal\Core\Cache\DatabaseBackend::flush(). + * Overrides Drupal\Core\Cache\DatabaseBackend::deleteExpired(). */ - public function flush() { + public function deleteExpired() { try { if (class_exists('Drupal\Core\Database\Database')) { - parent::flush(); + parent::deleteExpired(); } } catch (Exception $e) {} } /** - * Overrides Drupal\Core\Cache\DatabaseBackend::expire(). + * Overrides Drupal\Core\Cache\DatabaseBackend::deleteTags(). */ - public function expire() { + public function deleteTags(array $tags) { try { if (class_exists('Drupal\Core\Database\Database')) { - parent::expire(); + parent::deleteTags($tags); + } + } + catch (Exception $e) {} + } + + /** + * Overrides Drupal\Core\Cache\DatabaseBackend::invalidate(). + */ + public function invalidate($cid) { + try { + if (class_exists('Drupal\Core\Database\Database')) { + parent::invalidate($cid); + } + } + catch (Exception $e) {} + } + + /** + * Overrides Drupal\Core\Cache\DatabaseBackend::invalidateMultiple(). + */ + public function invalidateMultiple(array $cids) { + try { + if (class_exists('Drupal\Core\Database\Database')) { + parent::invalidateMultiple($cids); + } + } + catch (Exception $e) {} + } + + /** + * Overrides Drupal\Core\Cache\DatabaseBackend::invalidateTags(). + */ + public function invalidateTags(array $tags) { + try { + if (class_exists('Drupal\Core\Database\Database')) { + parent::invalidateTags($tags); } } catch (Exception $e) {} diff --git a/core/lib/Drupal/Core/Cache/MemoryBackend.php b/core/lib/Drupal/Core/Cache/MemoryBackend.php index 837df0e..87c970a 100644 --- a/core/lib/Drupal/Core/Cache/MemoryBackend.php +++ b/core/lib/Drupal/Core/Cache/MemoryBackend.php @@ -24,11 +24,6 @@ class MemoryBackend implements CacheBackendInterface { protected $cache = array(); /** - * All tags invalidated during the request. - */ - protected $invalidatedTags = array(); - - /** * Constructs a MemoryBackend object. * * @param string $bin @@ -40,9 +35,9 @@ class MemoryBackend implements CacheBackendInterface { /** * Implements Drupal\Core\Cache\CacheBackendInterface::get(). */ - public function get($cid) { + public function get($cid, $allowInvalid = FALSE) { if (isset($this->cache[$cid])) { - return $this->prepareItem($this->cache[$cid]); + return $this->prepareItem($this->cache[$cid], $allowInvalid); } else { return FALSE; @@ -52,13 +47,13 @@ class MemoryBackend implements CacheBackendInterface { /** * Implements Drupal\Core\Cache\CacheBackendInterface::getMultiple(). */ - public function getMultiple(&$cids) { + public function getMultiple(&$cids, $allowInvalid = FALSE) { $ret = array(); $items = array_intersect_key($this->cache, array_flip($cids)); foreach ($items as $item) { - $item = $this->prepareItem($item); + $item = $this->prepareItem($item, $allowInvalid); if ($item) { $ret[$item->cid] = $item; } @@ -82,13 +77,15 @@ class MemoryBackend implements CacheBackendInterface { * The item with data as appropriate or FALSE if there is no * valid item to load. */ - protected function prepareItem($cache) { + protected function prepareItem($cache, $allowInvalid) { if (!isset($cache->data)) { return FALSE; } - // The cache data is invalid if any of its tags have been cleared since. - if (count($cache->tags) && $this->hasInvalidatedTags($cache)) { + // Check expire time. + $cache->valid = $cache->expire == CacheBackendInterface::CACHE_PERMANENT || $cache->expire >= REQUEST_TIME; + + if (!$allowInvalid && !$cache->valid) { return FALSE; } @@ -102,29 +99,13 @@ class MemoryBackend implements CacheBackendInterface { $this->cache[$cid] = (object) array( 'cid' => $cid, 'data' => $data, + 'created' => REQUEST_TIME, 'expire' => $expire, - 'tags' => $tags, - 'checksum' => $this->checksum($this->flattenTags($tags)), + 'tags' => $this->flattenTags($tags), ); } /** - * Calculates a checksum so data can be invalidated using tags. - */ - public function checksum($tags) { - $checksum = ''; - - foreach ($tags as $tag) { - // Has the tag already been invalidated. - if (isset($this->invalidatedTags[$tag])) { - $checksum = $checksum . $tag . ':' . $this->invalidatedTags[$tag]; - } - } - - return $checksum; - } - - /** * Implements Drupal\Core\Cache\CacheBackendInterface::delete(). */ public function delete($cid) { @@ -139,47 +120,70 @@ class MemoryBackend implements CacheBackendInterface { } /** - * Implements Drupal\Core\Cache\CacheBackendInterface::flush(). + * Implements Drupal\Core\Cache\CacheBackendInterface::deleteTags(). + */ + public function deleteTags(array $tags) { + $flat_tags = $this->flattenTags($tags); + foreach ($this->cache as $cid => $item) { + if (array_intersect($flat_tags, $item->tags)) { + unset($this->cache[$cid]); + } + } + } + + /** + * Implements Drupal\Core\Cache\CacheBackendInterface::deleteAll(). */ - public function flush() { + public function deleteAll() { $this->cache = array(); } /** - * Implements Drupal\Core\Cache\CacheBackendInterface::expire(). + * Implements Drupal\Core\Cache\CacheBackendInterface::deleteExpired(). * - * Cache expiration is not implemented for PHP ArrayBackend as this backend - * only persists during a single request and expiration are done using + * Cache expiration is not implemented for MemoryBackend as this backend only + * persists during a single request and expiration are done using * REQUEST_TIME. */ - public function expire() { + public function deleteExpired() { } /** - * Checks to see if any of the tags associated with a cache object have been - * invalidated. - * - * @param object @cache - * An cache object to calculate and compare it's original checksum for. - * - * @return boolean - * TRUE if the a tag has been invalidated, FALSE otherwise. + * Implements Drupal\Core\Cache\CacheBackendInterface::invalidate(). + */ + public function invalidate($cid) { + $this->cache[$cid]->expire = REQUEST_TIME - 1; + } + + /** + * Implements Drupal\Core\Cache\CacheBackendInterface::invalidateMultiple(). */ - protected function hasInvalidatedTags($cache) { - if ($cache->checksum != $this->checksum($this->flattenTags($cache->tags))) { - return TRUE; + public function invalidateMultiple(array $cids) { + foreach ($cids as $cid) { + $this->cache[$cid]->expire = REQUEST_TIME - 1; + } + } + + /** + * Implements Drupal\Core\Cache\CacheBackendInterface::invalidateTags(). + */ + public function invalidateTags(array $tags) { + $flat_tags = $this->flattenTags($tags); + foreach ($this->cache as $cid => $item) { + if (array_intersect($flat_tags, $item->tags)) { + $this->cache[$cid]->expire = REQUEST_TIME - 1; + } } - return FALSE; } /** - * Flattens a tags array into a numeric array suitable for string storage. + * 'Flattens' a tags array into an array of strings. * * @param array $tags * Associative array of tags to flatten. * - * @return - * An array of flattened tag identifiers. + * @return array + * An indexed array of strings. */ protected function flattenTags(array $tags) { if (isset($tags[0])) { @@ -201,20 +205,6 @@ class MemoryBackend implements CacheBackendInterface { } /** - * Implements Drupal\Core\Cache\CacheBackendInterface::invalidateTags(). - */ - public function invalidateTags(array $tags) { - foreach ($this->flattenTags($tags) as $tag) { - if (isset($this->invalidatedTags[$tag])) { - $this->invalidatedTags[$tag] = $this->invalidatedTags[$tag] + 1; - } - else { - $this->invalidatedTags[$tag] = 1; - } - } - } - - /** * Implements Drupal\Core\Cache\CacheBackendInterface::isEmpty(). */ public function isEmpty() { diff --git a/core/lib/Drupal/Core/Cache/NullBackend.php b/core/lib/Drupal/Core/Cache/NullBackend.php index 66a9938..96e35a6 100644 --- a/core/lib/Drupal/Core/Cache/NullBackend.php +++ b/core/lib/Drupal/Core/Cache/NullBackend.php @@ -31,14 +31,14 @@ class NullBackend implements CacheBackendInterface { /** * Implements Drupal\Core\Cache\CacheBackendInterface::get(). */ - public function get($cid) { + public function get($cid, $allowInvalid = FALSE) { return FALSE; } /** * Implements Drupal\Core\Cache\CacheBackendInterface::getMultiple(). */ - public function getMultiple(&$cids) { + public function getMultiple(&$cids, $allowInvalid = FALSE) { return array(); } @@ -58,19 +58,29 @@ class NullBackend implements CacheBackendInterface { public function deleteMultiple(array $cids) {} /** - * Implements Drupal\Core\Cache\CacheBackendInterface::flush(). + * Implements Drupal\Core\Cache\CacheBackendInterface::deleteAll(). */ - public function flush() {} + public function deleteAll() {} /** - * Implements Drupal\Core\Cache\CacheBackendInterface::expire(). + * Implements Drupal\Core\Cache\CacheBackendInterface::deleteExpired(). */ - public function expire() {} + public function deleteExpired() {} /** - * Implements Drupal\Core\Cache\CacheBackendInterface::garbageCollection(). + * Implements Drupal\Core\Cache\CacheBackendInterface::deleteTags(). */ - public function garbageCollection() {} + public function deleteTags(array $tags) {} + + /** + * Implements Drupal\Core\Cache\CacheBackendInterface::invalidate(). + */ + public function invalidate($cid) {} + + /** + * Implements Drupal\Core\Cache\CacheBackendInterface::invalidateMultiple(). + */ + public function invalidateMultiple(array $cids) {} /** * Implements Drupal\Core\Cache\CacheBackendInterface::invalidateTags(). @@ -78,6 +88,11 @@ class NullBackend implements CacheBackendInterface { public function invalidateTags(array $tags) {} /** + * Implements Drupal\Core\Cache\CacheBackendInterface::garbageCollection(). + */ + public function garbageCollection() {} + + /** * Implements Drupal\Core\Cache\CacheBackendInterface::isEmpty(). */ public function isEmpty() { diff --git a/core/modules/aggregator/aggregator.parser.inc b/core/modules/aggregator/aggregator.parser.inc index bacaa84..ff2e522 100644 --- a/core/modules/aggregator/aggregator.parser.inc +++ b/core/modules/aggregator/aggregator.parser.inc @@ -45,7 +45,7 @@ function aggregator_aggregator_parse($feed) { $feed->modified = $modified; // Clear the page and block caches. - cache_invalidate(array('content' => TRUE)); + cache_invalidate_tags(array('content' => TRUE)); return TRUE; } diff --git a/core/modules/block/block.admin.inc b/core/modules/block/block.admin.inc index 2a17a55..2ef6602 100644 --- a/core/modules/block/block.admin.inc +++ b/core/modules/block/block.admin.inc @@ -207,7 +207,7 @@ function block_admin_display_form_submit($form, &$form_state) { throw $e; } drupal_set_message(t('The block settings have been updated.')); - cache_invalidate(array('content' => TRUE)); + cache_invalidate_tags(array('content' => TRUE)); } /** @@ -606,7 +606,7 @@ function block_admin_configure_submit($form, &$form_state) { throw $e; } drupal_set_message(t('The block configuration has been saved.')); - cache_invalidate(array('content' => TRUE)); + cache_invalidate_tags(array('content' => TRUE)); $form_state['redirect'] = 'admin/structure/block'; } } @@ -715,7 +715,7 @@ function block_add_block_form_submit($form, &$form_state) { } drupal_set_message(t('The block has been created.')); - cache_invalidate(array('content' => TRUE)); + cache_invalidate_tags(array('content' => TRUE)); $form_state['redirect'] = 'admin/structure/block'; } @@ -763,7 +763,7 @@ function block_custom_block_delete_submit($form, &$form_state) { ->execute(); drupal_set_message(t('The block %name has been removed.', array('%name' => $form_state['values']['info']))); - cache_invalidate(array('content' => TRUE)); + cache_invalidate_tags(array('content' => TRUE)); $form_state['redirect'] = 'admin/structure/block'; return; } diff --git a/core/modules/block/lib/Drupal/block/Tests/BlockCacheTest.php b/core/modules/block/lib/Drupal/block/Tests/BlockCacheTest.php index 3a5776c..d2d6c64 100644 --- a/core/modules/block/lib/Drupal/block/Tests/BlockCacheTest.php +++ b/core/modules/block/lib/Drupal/block/Tests/BlockCacheTest.php @@ -74,7 +74,7 @@ function testCachePerRole() { $this->assertText($old_content, 'Block is served from the cache.'); // Clear the cache and verify that the stale data is no longer there. - cache_invalidate(array('content' => TRUE)); + cache_invalidate_tags(array('content' => TRUE)); $this->drupalGet(''); $this->assertNoText($old_content, 'Block cache clear removes stale cache data.'); $this->assertText($current_content, 'Fresh block content is displayed after clearing the cache.'); diff --git a/core/modules/comment/comment.admin.inc b/core/modules/comment/comment.admin.inc index 52d1740..4220edd 100644 --- a/core/modules/comment/comment.admin.inc +++ b/core/modules/comment/comment.admin.inc @@ -204,7 +204,7 @@ function comment_admin_overview_submit($form, &$form_state) { } drupal_set_message(t('The update has been performed.')); $form_state['redirect'] = 'admin/content/comment'; - cache_invalidate(array('content' => TRUE)); + cache_invalidate_tags(array('content' => TRUE)); } /** @@ -252,7 +252,7 @@ function comment_multiple_delete_confirm($form, &$form_state) { function comment_multiple_delete_confirm_submit($form, &$form_state) { if ($form_state['values']['confirm']) { comment_delete_multiple(array_keys($form_state['values']['comments'])); - cache_invalidate(array('content' => TRUE)); + cache_invalidate_tags(array('content' => TRUE)); $count = count($form_state['values']['comments']); watchdog('content', 'Deleted @count comments.', array('@count' => $count)); drupal_set_message(format_plural($count, 'Deleted 1 comment.', 'Deleted @count comments.')); @@ -311,7 +311,7 @@ function comment_confirm_delete_submit($form, &$form_state) { drupal_set_message(t('The comment and all its replies have been deleted.')); watchdog('content', 'Deleted comment @cid and its replies.', array('@cid' => $comment->cid)); // Clear the cache so an anonymous user sees that his comment was deleted. - cache_invalidate(array('content' => TRUE)); + cache_invalidate_tags(array('content' => TRUE)); $form_state['redirect'] = "node/$comment->nid"; } diff --git a/core/modules/comment/comment.module b/core/modules/comment/comment.module index e700723..8621fe5 100644 --- a/core/modules/comment/comment.module +++ b/core/modules/comment/comment.module @@ -1985,7 +1985,7 @@ function comment_unpublish_by_keyword_action_submit($form, $form_state) { */ function comment_save_action(Comment $comment) { comment_save($comment); - cache_invalidate(array('content' => TRUE)); + cache_invalidate_tags(array('content' => TRUE)); watchdog('action', 'Saved comment %title', array('%title' => $comment->subject)); } diff --git a/core/modules/comment/lib/Drupal/comment/CommentFormController.php b/core/modules/comment/lib/Drupal/comment/CommentFormController.php index 10c6b47..7422565 100644 --- a/core/modules/comment/lib/Drupal/comment/CommentFormController.php +++ b/core/modules/comment/lib/Drupal/comment/CommentFormController.php @@ -378,6 +378,6 @@ class CommentFormController extends EntityFormController { $form_state['redirect'] = $redirect; // Clear the block and page caches so that anonymous users see the comment // they have posted. - cache_invalidate(array('content' => TRUE)); + cache_invalidate_tags(array('content' => TRUE)); } } diff --git a/core/modules/comment/lib/Drupal/comment/Tests/CommentBlockTest.php b/core/modules/comment/lib/Drupal/comment/Tests/CommentBlockTest.php index 0ec3af2..895fbd4 100644 --- a/core/modules/comment/lib/Drupal/comment/Tests/CommentBlockTest.php +++ b/core/modules/comment/lib/Drupal/comment/Tests/CommentBlockTest.php @@ -73,7 +73,7 @@ function testRecentCommentBlock() { user_role_revoke_permissions(DRUPAL_ANONYMOUS_RID, array('access comments')); // drupalCreateNode() does not automatically flush content caches unlike // posting a node from a node form. - cache_invalidate(array('content' => TRUE)); + cache_invalidate_tags(array('content' => TRUE)); $this->drupalGet(''); $this->assertNoText($block['title'], 'Block was not found.'); user_role_grant_permissions(DRUPAL_ANONYMOUS_RID, array('access comments')); diff --git a/core/modules/field/field.info.inc b/core/modules/field/field.info.inc index b659c97..5fab889 100644 --- a/core/modules/field/field.info.inc +++ b/core/modules/field/field.info.inc @@ -147,7 +147,7 @@ function _field_info_collate_types() { function _field_info_collate_types_reset() { drupal_static_reset('_field_info_collate_types'); // Clear all languages. - cache('field')->invalidateTags(array('field_info_types' => TRUE)); + cache('field')->deleteTags(array('field_info_types' => TRUE)); } /** diff --git a/core/modules/field/field.module b/core/modules/field/field.module index bb948ac..686e3f5 100644 --- a/core/modules/field/field.module +++ b/core/modules/field/field.module @@ -794,7 +794,7 @@ function _field_extra_fields_pre_render($elements) { * Clears the field info and field data caches. */ function field_cache_clear() { - cache('field')->flush(); + cache('field')->deleteAll(); field_info_cache_clear(); } diff --git a/core/modules/field/lib/Drupal/field/FieldInfo.php b/core/modules/field/lib/Drupal/field/FieldInfo.php index d203e42..65aa26c 100644 --- a/core/modules/field/lib/Drupal/field/FieldInfo.php +++ b/core/modules/field/lib/Drupal/field/FieldInfo.php @@ -112,7 +112,7 @@ class FieldInfo { $this->bundleExtraFields = array(); - cache('field')->invalidateTags(array('field_info' => TRUE)); + cache('field')->deleteTags(array('field_info' => TRUE)); } /** diff --git a/core/modules/filter/filter.module b/core/modules/filter/filter.module index 2af53f8..510dd82 100644 --- a/core/modules/filter/filter.module +++ b/core/modules/filter/filter.module @@ -310,7 +310,7 @@ function filter_format_save($format) { $return = SAVED_UPDATED; // Clear the filter cache whenever a text format is updated. - cache('filter')->invalidateTags(array('filter_format' => $format->format)); + cache('filter')->deleteTags(array('filter_format' => $format->format)); } filter_formats_reset(); @@ -340,7 +340,7 @@ function filter_format_disable($format) { // Clear the filter cache whenever a text format is disabled. filter_formats_reset(); - cache('filter')->invalidateTags(array('filter_format' => $format->format)); + cache('filter')->deleteTags(array('filter_format' => $format->format)); } /** @@ -487,7 +487,7 @@ function filter_formats($account = NULL) { * @see filter_formats() */ function filter_formats_reset() { - cache()->invalidateTags(array('filter_formats' => TRUE)); + cache()->deleteTags(array('filter_formats' => TRUE)); cache()->delete('filter_list_format'); drupal_static_reset('filter_list_format'); drupal_static_reset('filter_formats'); diff --git a/core/modules/forum/forum.admin.inc b/core/modules/forum/forum.admin.inc index e3c564a..e128aa4 100644 --- a/core/modules/forum/forum.admin.inc +++ b/core/modules/forum/forum.admin.inc @@ -115,7 +115,7 @@ function forum_form_submit($form, &$form_state) { case SAVED_UPDATED: drupal_set_message(t('The @type %term has been updated.', array('%term' => $form_state['values']['name'], '@type' => $type))); // Clear the page and block caches to avoid stale data. - cache_invalidate(array('content' => TRUE)); + cache_invalidate_tags(array('content' => TRUE)); break; } $form_state['redirect'] = 'admin/structure/forum'; diff --git a/core/modules/image/image.api.php b/core/modules/image/image.api.php index c11347f..d2990f5 100644 --- a/core/modules/image/image.api.php +++ b/core/modules/image/image.api.php @@ -79,7 +79,7 @@ function hook_image_effect_info_alter(&$effects) { */ function hook_image_style_flush($style) { // Empty cached data that contains information about the style. - cache('mymodule')->flush(); + cache('mymodule')->deleteAll(); } /** diff --git a/core/modules/image/image.module b/core/modules/image/image.module index c763dc6..221e07c 100644 --- a/core/modules/image/image.module +++ b/core/modules/image/image.module @@ -835,9 +835,9 @@ function image_style_flush($style) { // Clear page caches when flushing. if (module_exists('block')) { - cache('block')->flush(); + cache('block')->deleteAll(); } - cache('page')->flush(); + cache('page')->deleteAll(); } /** diff --git a/core/modules/locale/lib/Drupal/locale/Tests/LocaleTranslationTest.php b/core/modules/locale/lib/Drupal/locale/Tests/LocaleTranslationTest.php index f6ff9c3..e8cec32 100644 --- a/core/modules/locale/lib/Drupal/locale/Tests/LocaleTranslationTest.php +++ b/core/modules/locale/lib/Drupal/locale/Tests/LocaleTranslationTest.php @@ -260,7 +260,7 @@ function testJavaScriptTranslation() { // Test JavaScript translation rebuilding. file_unmanaged_delete($js_file); $this->assertTrue($result = !file_exists($js_file), t('JavaScript file deleted: %file', array('%file' => $result ? $js_file : t('found')))); - cache_invalidate(array('content' => TRUE)); + cache_invalidate_tags(array('content' => TRUE)); _locale_rebuild_js($langcode); $this->assertTrue($result = file_exists($js_file), t('JavaScript file rebuilt: %file', array('%file' => $result ? $js_file : t('not found')))); } diff --git a/core/modules/locale/locale.module b/core/modules/locale/locale.module index 85ed5d1..3e29944 100644 --- a/core/modules/locale/locale.module +++ b/core/modules/locale/locale.module @@ -239,7 +239,7 @@ function locale_stream_wrappers() { function locale_language_insert($language) { // @todo move these two cache clears out. See http://drupal.org/node/1293252 // Changing the language settings impacts the interface. - cache('page')->flush(); + cache('page')->deleteAll(); // Force JavaScript translation file re-creation for the new language. _locale_invalidate_js($language->langcode); } @@ -250,7 +250,7 @@ function locale_language_insert($language) { function locale_language_update($language) { // @todo move these two cache clears out. See http://drupal.org/node/1293252 // Changing the language settings impacts the interface. - cache('page')->flush(); + cache('page')->deleteAll(); // Force JavaScript translation file re-creation for the modified language. _locale_invalidate_js($language->langcode); } @@ -269,7 +269,7 @@ function locale_language_delete($language) { _locale_invalidate_js($language->langcode); // Changing the language settings impacts the interface: - cache('page')->flush(); + cache('page')->deleteAll(); // Clearing all locale cache from database cache()->delete('locale:' . $language->langcode); @@ -790,7 +790,7 @@ function _locale_refresh_translations($langcodes, $lids) { } } // Clear locale cache. - cache()->invalidateTags(array('locale' => TRUE)); + cache()->deleteTags(array('locale' => TRUE)); } /** diff --git a/core/modules/node/lib/Drupal/node/NodeFormController.php b/core/modules/node/lib/Drupal/node/NodeFormController.php index 215ad81..b854c04 100644 --- a/core/modules/node/lib/Drupal/node/NodeFormController.php +++ b/core/modules/node/lib/Drupal/node/NodeFormController.php @@ -383,7 +383,7 @@ class NodeFormController extends EntityFormController { } // Clear the page and block caches. - cache_invalidate(array('content' => TRUE)); + cache_invalidate_tags(array('content' => TRUE)); } /** diff --git a/core/modules/node/lib/Drupal/node/Tests/NodeBlockFunctionalTest.php b/core/modules/node/lib/Drupal/node/Tests/NodeBlockFunctionalTest.php index 46b003f..f5e33ee 100644 --- a/core/modules/node/lib/Drupal/node/Tests/NodeBlockFunctionalTest.php +++ b/core/modules/node/lib/Drupal/node/Tests/NodeBlockFunctionalTest.php @@ -113,7 +113,7 @@ function testRecentNodeBlock() { $node4 = $this->drupalCreateNode($default_settings); // drupalCreateNode() does not automatically flush content caches unlike // posting a node from a node form. - cache_invalidate(array('content' => TRUE)); + cache_invalidate_tags(array('content' => TRUE)); // Test that all four nodes are shown. $this->drupalGet(''); diff --git a/core/modules/node/node.admin.inc b/core/modules/node/node.admin.inc index e171b75..e733753 100644 --- a/core/modules/node/node.admin.inc +++ b/core/modules/node/node.admin.inc @@ -663,7 +663,7 @@ function node_admin_nodes_submit($form, &$form_state) { } call_user_func_array($function, $args); - cache_invalidate(array('content' => TRUE)); + cache_invalidate_tags(array('content' => TRUE)); } else { // We need to rebuild the form to go to a second step. For example, to diff --git a/core/modules/node/node.module b/core/modules/node/node.module index 08d7810..8cbab75 100644 --- a/core/modules/node/node.module +++ b/core/modules/node/node.module @@ -757,7 +757,7 @@ function _node_types_build($rebuild = FALSE) { * Clears the node type cache. */ function node_type_cache_reset() { - cache()->invalidateTags(array('node_types' => TRUE)); + cache()->deleteTags(array('node_types' => TRUE)); drupal_static_reset('_node_types_build'); } @@ -3256,7 +3256,7 @@ function node_access_rebuild($batch_mode = FALSE) { if (!isset($batch)) { drupal_set_message(t('Content permissions have been rebuilt.')); node_access_needs_rebuild(FALSE); - cache_invalidate(array('content' => TRUE)); + cache_invalidate_tags(array('content' => TRUE)); } } @@ -3316,7 +3316,7 @@ function _node_access_rebuild_batch_finished($success, $results, $operations) { else { drupal_set_message(t('The content access permissions have not been properly rebuilt.'), 'error'); } - cache_invalidate(array('content' => TRUE)); + cache_invalidate_tags(array('content' => TRUE)); } /** diff --git a/core/modules/path/lib/Drupal/path/Tests/PathAliasTest.php b/core/modules/path/lib/Drupal/path/Tests/PathAliasTest.php index 4168275..23ff7f7 100644 --- a/core/modules/path/lib/Drupal/path/Tests/PathAliasTest.php +++ b/core/modules/path/lib/Drupal/path/Tests/PathAliasTest.php @@ -50,12 +50,12 @@ function testPathCache() { // Visit the system path for the node and confirm a cache entry is // created. - cache('path')->flush(); + cache('path')->deleteAll(); $this->drupalGet($edit['source']); $this->assertTrue(cache('path')->get($edit['source']), 'Cache entry was created.'); // Visit the alias for the node and confirm a cache entry is created. - cache('path')->flush(); + cache('path')->deleteAll(); $this->drupalGet($edit['alias']); $this->assertTrue(cache('path')->get($edit['source']), 'Cache entry was created.'); } diff --git a/core/modules/poll/poll.module b/core/modules/poll/poll.module index d3f8d3a..de85c16 100644 --- a/core/modules/poll/poll.module +++ b/core/modules/poll/poll.module @@ -791,7 +791,7 @@ function poll_vote($form, &$form_state) { ->condition('chid', $choice) ->execute(); - cache_invalidate(array('content' => TRUE)); + cache_invalidate_tags(array('content' => TRUE)); if (!$user->uid) { // The vote is recorded so the user gets the result view instead of the diff --git a/core/modules/system/lib/Drupal/system/Tests/Cache/BackendChainImplementationUnitTest.php b/core/modules/system/lib/Drupal/system/Tests/Cache/BackendChainImplementationUnitTest.php index 318373e..9c528ce 100644 --- a/core/modules/system/lib/Drupal/system/Tests/Cache/BackendChainImplementationUnitTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/Cache/BackendChainImplementationUnitTest.php @@ -208,32 +208,32 @@ class BackendChainImplementationUnitTest extends UnitTestBase { $this->assertFalse($this->chain->isEmpty(), 'Chain is not empty'); // This is the only test that needs to start with an empty chain. - $this->chain->flush(); - $this->assert($this->chain->isEmpty(), 'Chain have been emptied by the flush() call'); + $this->chain->deleteAll(); + $this->assert($this->chain->isEmpty(), 'Chain have been emptied by the deleteAll() call'); $this->secondBackend->set('test', 5); $this->assertFalse($this->chain->isEmpty(), 'Chain is not empty anymore now that the second backend has something'); } /** - * Test that the flush operation is propagated to all backends in the chain. + * Test that the delete all operation is propagated to all backends in the chain. */ - public function testFlushPropagation() { + public function testDeleteAllPropagation() { // Set both expiring and permanent keys. $this->chain->set('test1', 1, CacheBackendInterface::CACHE_PERMANENT); $this->chain->set('test2', 3, time() + 1000); - $this->chain->flush(); + $this->chain->deleteAll(); - $this->assertTrue($this->firstBackend->isEmpty(), 'First backend is empty after flush.'); - $this->assertTrue($this->secondBackend->isEmpty(), 'Second backend is empty after flush.'); - $this->assertTrue($this->thirdBackend->isEmpty(), 'Third backend is empty after flush.'); + $this->assertTrue($this->firstBackend->isEmpty(), 'First backend is empty after delete all.'); + $this->assertTrue($this->secondBackend->isEmpty(), 'Second backend is empty after delete all.'); + $this->assertTrue($this->thirdBackend->isEmpty(), 'Third backend is empty after delete all.'); } /** - * Test that the tags cache clear operation is propagated to all backends + * Test that the delete tags operation is propagated to all backends * in the chain. */ - public function testClearTagsPropagation() { + public function testDeleteTagsPropagation() { // Create two cache entries with the same tag and tag value. $this->chain->set('test_cid_clear1', 'foo', CacheBackendInterface::CACHE_PERMANENT, array('test_tag' => 2)); $this->chain->set('test_cid_clear2', 'foo', CacheBackendInterface::CACHE_PERMANENT, array('test_tag' => 2)); @@ -246,7 +246,7 @@ class BackendChainImplementationUnitTest extends UnitTestBase { 'Two cache items were created in all backends.'); // Invalidate test_tag of value 1. This should invalidate both entries. - $this->chain->invalidateTags(array('test_tag' => 2)); + $this->chain->deleteTags(array('test_tag' => 2)); $this->assertIdentical(FALSE, $this->firstBackend->get('test_cid_clear1') && $this->firstBackend->get('test_cid_clear2') && $this->secondBackend->get('test_cid_clear1') @@ -267,7 +267,7 @@ class BackendChainImplementationUnitTest extends UnitTestBase { 'Two cache items were created in all backends.'); // Invalidate test_tag of value 1. This should invalidate both entries. - $this->chain->invalidateTags(array('test_tag' => array(1))); + $this->chain->deleteTags(array('test_tag' => array(1))); $this->assertIdentical(FALSE, $this->firstBackend->get('test_cid_clear1') && $this->firstBackend->get('test_cid_clear2') && $this->secondBackend->get('test_cid_clear1') @@ -291,7 +291,7 @@ class BackendChainImplementationUnitTest extends UnitTestBase { && $this->thirdBackend->get('test_cid_clear3'), 'Three cached items were created in all backends.'); - $this->chain->invalidateTags(array('test_tag_foo' => array(3))); + $this->chain->deleteTags(array('test_tag_foo' => array(3))); $this->assertNotIdentical(FALSE, $this->firstBackend->get('test_cid_clear1') && $this->firstBackend->get('test_cid_clear2') && $this->secondBackend->get('test_cid_clear1') diff --git a/core/modules/system/lib/Drupal/system/Tests/Cache/CacheTestBase.php b/core/modules/system/lib/Drupal/system/Tests/Cache/CacheTestBase.php index 25ab106..dd1dea9 100644 --- a/core/modules/system/lib/Drupal/system/Tests/Cache/CacheTestBase.php +++ b/core/modules/system/lib/Drupal/system/Tests/Cache/CacheTestBase.php @@ -99,6 +99,6 @@ function assertCacheRemoved($message, $cid = NULL, $bin = NULL) { $bin = $this->default_bin; } - cache($bin)->expire(); + cache($bin)->deleteExpired(); } } diff --git a/core/modules/system/lib/Drupal/system/Tests/Cache/GenericCacheBackendUnitTestBase.php b/core/modules/system/lib/Drupal/system/Tests/Cache/GenericCacheBackendUnitTestBase.php index 95805d7..7d68ce7 100644 --- a/core/modules/system/lib/Drupal/system/Tests/Cache/GenericCacheBackendUnitTestBase.php +++ b/core/modules/system/lib/Drupal/system/Tests/Cache/GenericCacheBackendUnitTestBase.php @@ -102,7 +102,7 @@ if (!isset($this->cachebackends[$bin])) { $this->cachebackends[$bin] = $this->createCacheBackend($bin); // Ensure the backend is empty. - $this->cachebackends[$bin]->flush(); + $this->cachebackends[$bin]->deleteAll(); } return $this->cachebackends[$bin]; } @@ -118,10 +118,10 @@ public function tearDown() { // Destruct the registered backend, each test will get a fresh instance, - // properly flushing it here ensure that on persistant data backends they + // properly emptying it here ensure that on persistant data backends they // will come up empty the next test. foreach ($this->cachebackends as $bin => $cachebackend) { - $this->cachebackends[$bin]->flush(); + $this->cachebackends[$bin]->deleteAll(); } unset($this->cachebackends); @@ -136,19 +136,31 @@ public function testSetGet() { $backend = $this->getCacheBackend(); - $data = 7; $this->assertIdentical(FALSE, $backend->get('test1'), "Backend does not contain data for cache id test1."); - $backend->set('test1', $data); + $backend->set('test1', 7); $cached = $backend->get('test1'); $this->assert(is_object($cached), "Backend returned an object for cache id test1."); - $this->assertIdentical($data, $cached->data); + $this->assertIdentical(7, $cached->data); + $this->assertTrue($cached->valid, 'Item is marked as valid.'); + $this->assertEqual($cached->created, REQUEST_TIME, 'Created time is correct.'); + $this->assertEqual($cached->expire, CacheBackendInterface::CACHE_PERMANENT, 'Expire time is correct.'); - $data = array('value' => 3); $this->assertIdentical(FALSE, $backend->get('test2'), "Backend does not contain data for cache id test2."); - $backend->set('test2', $data); + $backend->set('test2', array('value' => 3), REQUEST_TIME + 3); $cached = $backend->get('test2'); $this->assert(is_object($cached), "Backend returned an object for cache id test2."); - $this->assertIdentical($data, $cached->data); + $this->assertIdentical(array('value' => 3), $cached->data); + $this->assertTrue($cached->valid, 'Item is marked as valid.'); + $this->assertEqual($cached->created, REQUEST_TIME, 'Created time is correct.'); + $this->assertEqual($cached->expire, REQUEST_TIME + 3, 'Expire time is correct.'); + + $backend->set('test3', 'foobar', REQUEST_TIME - 3); + $this->assertFalse($backend->get('test3'), 'Invalid item not returned.'); + $cached = $backend->get('test3', TRUE); + $this->assert(is_object($cached), 'Backend returned an object for cache id test3.'); + $this->assertFalse($cached->valid, 'Item is marked as valid.'); + $this->assertEqual($cached->created, REQUEST_TIME, 'Created time is correct.'); + $this->assertEqual($cached->expire, REQUEST_TIME - 3, 'Expire time is correct.'); } /** @@ -186,7 +198,7 @@ 'test2' => '0', 'test3' => '', 'test4' => 12.64, - 'test5' => false, + 'test5' => FALSE, 'test6' => array(1,2,3), ); @@ -224,7 +236,7 @@ 'test7', 'test21', // Cid does not exist. 'test6', - 'test19', // Cid does not exist until added before second getMulitple(). + 'test19', // Cid does not exist until added before second getMultiple(). 'test2', ); @@ -235,6 +247,10 @@ $this->assert(isset($ret['test3']), "Existing cache id test3 is set."); $this->assert(isset($ret['test6']), "Existing cache id test6 is set."); $this->assert(isset($ret['test7']), "Existing cache id test7 is set."); + // Test return - ensure that objects has expected properties. + $this->assertTrue($ret['test2']->valid, 'Item is marked as valid.'); + $this->assertEqual($ret['test2']->created, REQUEST_TIME, 'Created time is correct.'); + $this->assertEqual($ret['test2']->expire, CacheBackendInterface::CACHE_PERMANENT, 'Expire time is correct.'); // Test return - ensure it does not contain nonexistent cache ids. $this->assertFalse(isset($ret['test19']), "Nonexistent cache id test19 is not set."); $this->assertFalse(isset($ret['test21']), "Nonexistent cache id test21 is not set."); @@ -298,7 +314,8 @@ } /** - * Tests Drupal\Core\Cache\CacheBackendInterface::deleteMultiple(). + * Test Drupal\Core\Cache\CacheBackendInterface::delete() and + * Drupal\Core\Cache\CacheBackendInterface::deleteMultiple(). */ public function testDeleteMultiple() { $backend = $this->getCacheBackend(); @@ -312,8 +329,9 @@ $backend->set('test6', 13); $backend->set('test7', 17); + $backend->delete('test1'); + $backend->delete('test23'); // Nonexistent key should not cause an error. $backend->deleteMultiple(array( - 'test1', 'test3', 'test5', 'test7', @@ -338,92 +356,155 @@ } /** - * Tests Drupal\Core\Cache\CacheBackendInterface::flush(). + * Tests Drupal\Core\Cache\CacheBackendInterface::deleteTags(). */ - public function testFlush() { + function testDeleteTags() { + $backend = $this->getCacheBackend(); + + // Create two cache entries with the same tag and tag value. + $backend->set('test_cid_invalidate1', $this->defaultValue, CacheBackendInterface::CACHE_PERMANENT, array('test_tag' => 2)); + $backend->set('test_cid_invalidate2', $this->defaultValue, CacheBackendInterface::CACHE_PERMANENT, array('test_tag' => 2)); + $this->assertTrue($backend->get('test_cid_invalidate1') && $backend->get('test_cid_invalidate2'), 'Two cache items were created.'); + + // Delete test_tag of value 1. This should delete both entries. + $backend->deleteTags(array('test_tag' => 2)); + $this->assertFalse($backend->get('test_cid_invalidate1') || $backend->get('test_cid_invalidate2'), 'Two cache items invalidated after deleting a cache tag.'); + $this->assertFalse($backend->get('test_cid_invalidate1', TRUE) || $backend->get('test_cid_invalidate2', TRUE), 'Two cache items deleted after deleting a cache tag.'); + + // Create two cache entries with the same tag and an array tag value. + $backend->set('test_cid_invalidate1', $this->defaultValue, CacheBackendInterface::CACHE_PERMANENT, array('test_tag' => array(1))); + $backend->set('test_cid_invalidate2', $this->defaultValue, CacheBackendInterface::CACHE_PERMANENT, array('test_tag' => array(1))); + $this->assertTrue($backend->get('test_cid_invalidate1') && $backend->get('test_cid_invalidate2'), 'Two cache items were created.'); + + // Delete test_tag of value 1. This should delete both entries. + $backend->deleteTags(array('test_tag' => array(1))); + $this->assertFalse($backend->get('test_cid_invalidate1') || $backend->get('test_cid_invalidate2'), 'Two cache items invalidated after deleted a cache tag.'); + $this->assertFalse($backend->get('test_cid_invalidate1', TRUE) || $backend->get('test_cid_invalidate2', TRUE), 'Two cache items deleted after deleting a cache tag.'); + + // Create three cache entries with a mix of tags and tag values. + $backend->set('test_cid_invalidate1', $this->defaultValue, CacheBackendInterface::CACHE_PERMANENT, array('test_tag' => array(1))); + $backend->set('test_cid_invalidate2', $this->defaultValue, CacheBackendInterface::CACHE_PERMANENT, array('test_tag' => array(2))); + $backend->set('test_cid_invalidate3', $this->defaultValue, CacheBackendInterface::CACHE_PERMANENT, array('test_tag_foo' => array(3))); + $this->assertTrue($backend->get('test_cid_invalidate1') && $backend->get('test_cid_invalidate2') && $backend->get('test_cid_invalidate3'), 'Three cached items were created.'); + $backend->deleteTags(array('test_tag_foo' => array(3))); + $this->assertTrue($backend->get('test_cid_invalidate1') && $backend->get('test_cid_invalidate2'), 'Cached items not matching the tag were not deleted.'); + $this->assertFalse($backend->get('test_cid_invalidated3', TRUE), 'Cache item matching the tag was deleted.'); + + // Create cache entry in multiple bins. Two cache entries + // (test_cid_invalidate1 and test_cid_invalidate2) still exist from previous + // tests. + $tags = array('test_tag' => array(1, 2, 3)); + $bins = array('path', 'bootstrap', 'page'); + foreach ($bins as $bin) { + $this->getCacheBackend($bin)->set('test', $this->defaultValue, CacheBackendInterface::CACHE_PERMANENT, $tags); + $this->assertTrue($this->getCacheBackend($bin)->get('test'), 'Cache item was set in bin.'); + } + + // Delete tag in mulitple bins. + foreach ($bins as $bin) { + $this->getCacheBackend($bin)->deleteTags(array('test_tag' => array(2))); + } + + // Test that cache entry has been deleted in multple bins. + foreach ($bins as $bin) { + $this->assertFalse($this->getCacheBackend($bin)->get('test', TRUE), 'Tag deletion affected item in bin.'); + } + // Test that the cache entry with a matching tag has been invalidated. + $this->assertFalse($this->getCacheBackend($bin)->get('test_cid_invalidate2', TRUE), 'Cache items matching tag were deleted.'); + // Test that the cache entry with without a matching tag still exists. + $this->assertTrue($this->getCacheBackend($bin)->get('test_cid_invalidate1'), 'Cache items not matching tag were not invalidated.'); + } + + /** + * Test Drupal\Core\Cache\CacheBackendInterface::deleteAll(). + */ + public function testDeleteAll() { $backend = $this->getCacheBackend(); // Set both expiring and permanent keys. $backend->set('test1', 1, CacheBackendInterface::CACHE_PERMANENT); $backend->set('test2', 3, time() + 1000); - $backend->flush(); + $backend->deleteAll(); - $this->assertTrue($backend->isEmpty(), "Backend is empty after flush."); + $this->assertTrue($backend->isEmpty(), "Backend is empty after deleteAll()."); - $this->assertIdentical(FALSE, $backend->get('test1'), "First key has been flushed."); - $this->assertIdentical(FALSE, $backend->get('test2'), "Second key has been flushed."); + $this->assertIdentical(FALSE, $backend->get('test1'), "First key has been deleted."); + $this->assertIdentical(FALSE, $backend->get('test2'), "Second key has been deleted."); } /** - * Checks whether or not a cache entry exists. - * - * @param $cid - * The cache id. - * @param $bin - * The cache bin to use. If not provided the default test bin wil be used. - * - * @return - * TRUE on pass, FALSE on fail. + * Test Drupal\Core\Cache\CacheBackendInterface::invalidate() and + * Drupal\Core\Cache\CacheBackendInterface::invalidateMultiple(). */ - protected function checkCacheExists($cid, $bin = null) { - $cached = $this->getCacheBackend($bin)->get($cid); - return isset($cached->data); + function testInvalidate() { + $backend = $this->getCacheBackend(); + $backend->set('test1', 1); + $backend->set('test2', 2); + $backend->set('test3', 2); + $backend->set('test4', 2); + + $reference = array('test1', 'test2', 'test3', 'test4'); + + $cids = $reference; + $ret = $backend->getMultiple($cids); + $this->assertEqual(count($ret), 4, 'Four items returned.'); + + $backend->invalidate('test1'); + $backend->invalidateMultiple(array('test2', 'test3')); + + $cids = $reference; + $ret = $backend->getMultiple($cids); + $this->assertEqual(count($ret), 1, 'Only one item element returned.'); + + $cids = $reference; + $ret = $backend->getMultiple($cids, TRUE); + $this->assertEqual(count($ret), 4, 'Four items returned.'); } /** * Tests Drupal\Core\Cache\CacheBackendInterface::invalidateTags(). */ - function testClearTags() { + function testInvalidateTags() { $backend = $this->getCacheBackend(); // Create two cache entries with the same tag and tag value. - $backend->set('test_cid_clear1', $this->defaultValue, CacheBackendInterface::CACHE_PERMANENT, array('test_tag' => 2)); - $backend->set('test_cid_clear2', $this->defaultValue, CacheBackendInterface::CACHE_PERMANENT, array('test_tag' => 2)); - $this->assertTrue($this->checkCacheExists('test_cid_clear1') - && $this->checkCacheExists('test_cid_clear2'), - 'Two cache items were created.'); + $backend->set('test_cid_invalidate1', $this->defaultValue, CacheBackendInterface::CACHE_PERMANENT, array('test_tag' => 2)); + $backend->set('test_cid_invalidate2', $this->defaultValue, CacheBackendInterface::CACHE_PERMANENT, array('test_tag' => 2)); + $this->assertTrue($backend->get('test_cid_invalidate1') && $backend->get('test_cid_invalidate2'), 'Two cache items were created.'); + // Invalidate test_tag of value 1. This should invalidate both entries. $backend->invalidateTags(array('test_tag' => 2)); - $this->assertFalse($this->checkCacheExists('test_cid_clear1') - || $this->checkCacheExists('test_cid_clear2'), - 'Two caches removed after clearing a cache tag.'); + $this->assertFalse($backend->get('test_cid_invalidate1') || $backend->get('test_cid_invalidate2'), 'Two cache items invalidated after invalidating a cache tag.'); + $this->assertTrue($backend->get('test_cid_invalidate1', TRUE) && $backend->get('test_cid_invalidate2', TRUE), 'Cache items not deleted after invalidating a cache tag.'); // Create two cache entries with the same tag and an array tag value. - $backend->set('test_cid_clear1', $this->defaultValue, CacheBackendInterface::CACHE_PERMANENT, array('test_tag' => array(1))); - $backend->set('test_cid_clear2', $this->defaultValue, CacheBackendInterface::CACHE_PERMANENT, array('test_tag' => array(1))); - $this->assertTrue($this->checkCacheExists('test_cid_clear1') - && $this->checkCacheExists('test_cid_clear2'), - 'Two cache items were created.'); + $backend->set('test_cid_invalidate1', $this->defaultValue, CacheBackendInterface::CACHE_PERMANENT, array('test_tag' => array(1))); + $backend->set('test_cid_invalidate2', $this->defaultValue, CacheBackendInterface::CACHE_PERMANENT, array('test_tag' => array(1))); + $this->assertTrue($backend->get('test_cid_invalidate1') && $backend->get('test_cid_invalidate2'), 'Two cache items were created.'); + // Invalidate test_tag of value 1. This should invalidate both entries. $backend->invalidateTags(array('test_tag' => array(1))); - $this->assertFalse($this->checkCacheExists('test_cid_clear1') - || $this->checkCacheExists('test_cid_clear2'), - 'Two caches removed after clearing a cache tag.'); + $this->assertFalse($backend->get('test_cid_invalidate1') || $backend->get('test_cid_invalidate2'), 'Two caches removed after invalidating a cache tag.'); + $this->assertTrue($backend->get('test_cid_invalidate1', TRUE) && $backend->get('test_cid_invalidate2', TRUE), 'Cache items not deleted after invalidating a cache tag.'); // Create three cache entries with a mix of tags and tag values. - $backend->set('test_cid_clear1', $this->defaultValue, CacheBackendInterface::CACHE_PERMANENT, array('test_tag' => array(1))); - $backend->set('test_cid_clear2', $this->defaultValue, CacheBackendInterface::CACHE_PERMANENT, array('test_tag' => array(2))); - $backend->set('test_cid_clear3', $this->defaultValue, CacheBackendInterface::CACHE_PERMANENT, array('test_tag_foo' => array(3))); - $this->assertTrue($this->checkCacheExists('test_cid_clear1') - && $this->checkCacheExists('test_cid_clear2') - && $this->checkCacheExists('test_cid_clear3'), - 'Two cached items were created.'); + $backend->set('test_cid_invalidate1', $this->defaultValue, CacheBackendInterface::CACHE_PERMANENT, array('test_tag' => array(1))); + $backend->set('test_cid_invalidate2', $this->defaultValue, CacheBackendInterface::CACHE_PERMANENT, array('test_tag' => array(2))); + $backend->set('test_cid_invalidate3', $this->defaultValue, CacheBackendInterface::CACHE_PERMANENT, array('test_tag_foo' => array(3))); + $this->assertTrue($backend->get('test_cid_invalidate1') && $backend->get('test_cid_invalidate2') && $backend->get('test_cid_invalidate3'), 'Three cached items were created.'); $backend->invalidateTags(array('test_tag_foo' => array(3))); - $this->assertTrue($this->checkCacheExists('test_cid_clear1') - && $this->checkCacheExists('test_cid_clear2'), - 'Cached items not matching the tag were not cleared.'); - - $this->assertFalse($this->checkCacheExists('test_cid_clear3'), - 'Cached item matching the tag was removed.'); + $this->assertTrue($backend->get('test_cid_invalidate1') && $backend->get('test_cid_invalidate2'), 'Cache items not matching the tag were not invalidated.'); + $this->assertFalse($backend->get('test_cid_invalidated3'), 'Cached item matching the tag was removed.'); - // Create cache entry in multiple bins. Two cache entries (test_cid_clear1 - // and test_cid_clear2) still exist from previous tests. + // Create cache entry in multiple bins. Two cache entries + // (test_cid_invalidate1 and test_cid_invalidate2) still exist from previous + // tests. $tags = array('test_tag' => array(1, 2, 3)); $bins = array('path', 'bootstrap', 'page'); foreach ($bins as $bin) { $this->getCacheBackend($bin)->set('test', $this->defaultValue, CacheBackendInterface::CACHE_PERMANENT, $tags); - $this->assertTrue($this->checkCacheExists('test', $bin), 'Cache item was set in bin.'); + $this->assertTrue($this->getCacheBackend($bin)->get('test'), 'Cache item was set in bin.'); } // Invalidate tag in mulitple bins. @@ -433,11 +514,11 @@ function testClearTags() { // Test that cache entry has been invalidated in multple bins. foreach ($bins as $bin) { - $this->assertFalse($this->checkCacheExists('test', $bin), 'Tag expire affected item in bin.'); + $this->assertFalse($this->getCacheBackend($bin)->get('test'), 'Tag invalidation affected item in bin.'); } // Test that the cache entry with a matching tag has been invalidated. - $this->assertFalse($this->checkCacheExists('test_cid_clear2', $bin), 'Cached items matching tag were cleared.'); + $this->assertFalse($this->getCacheBackend($bin)->get('test_cid_invalidate2'), 'Cache items matching tag were invalidated.'); // Test that the cache entry with without a matching tag still exists. - $this->assertTrue($this->checkCacheExists('test_cid_clear1', $bin), 'Cached items not matching tag were not cleared.'); + $this->assertTrue($this->getCacheBackend($bin)->get('test_cid_invalidate1'), 'Cache items not matching tag were not invalidated.'); } } diff --git a/core/modules/system/lib/Drupal/system/Tests/Cache/InstallTest.php b/core/modules/system/lib/Drupal/system/Tests/Cache/InstallTest.php index 58078cd..65cb303 100644 --- a/core/modules/system/lib/Drupal/system/Tests/Cache/InstallTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/Cache/InstallTest.php @@ -85,7 +85,7 @@ function testCacheInstall() { $database_cache->set('cache_two', 'Two'); $this->assertEqual($database_cache->get('cache_one')->data, 'One'); $this->assertEqual($database_cache->get('cache_two')->data, 'Two'); - $install_cache->flush(); + $install_cache->deleteAll(); $this->assertFalse($database_cache->get('cache_one')); $this->assertFalse($database_cache->get('cache_two')); @@ -106,8 +106,8 @@ function testCacheInstall() { $install_cache->isEmpty(); $install_cache->delete('cache_one'); $install_cache->deleteMultiple(array('cache_one', 'cache_two')); - $install_cache->flush(); - $install_cache->expire(); + $install_cache->deleteAll(); + $install_cache->deleteExpired(); $install_cache->garbageCollection(); $install_cache->invalidateTags(array('tag')); $this->pass("The installer's cache backend can be used even when the cache database tables are unavailable."); diff --git a/core/modules/system/system.admin.inc b/core/modules/system/system.admin.inc index d08c4ce..9c21924 100644 --- a/core/modules/system/system.admin.inc +++ b/core/modules/system/system.admin.inc @@ -735,7 +735,7 @@ function system_theme_settings_submit($form, &$form_state) { variable_set($key, $values); drupal_set_message(t('The configuration options have been saved.')); - cache_invalidate(array('content' => TRUE)); + cache_invalidate_tags(array('content' => TRUE)); } /** @@ -1714,7 +1714,7 @@ function system_clear_cache_submit($form, &$form_state) { * @ingroup forms */ function system_clear_page_cache_submit($form, &$form_state) { - cache('page')->flush(); + cache('page')->deleteAll(); } /** diff --git a/core/modules/system/system.install b/core/modules/system/system.install index d7ceb4d..8a3e36a 100644 --- a/core/modules/system/system.install +++ b/core/modules/system/system.install @@ -596,6 +596,12 @@ function system_schema() { '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'), ); @@ -641,12 +647,18 @@ function system_schema() { 'size' => 'big', 'not null' => FALSE, ), - 'checksum' => array( + 'checksum_invalidations' => array( 'description' => 'The tag invalidation sum when this entry was saved.', 'type' => 'int', 'not null' => TRUE, 'default' => 0, ), + 'checksum_deletions' => array( + 'description' => 'The tag deletion sum when this entry was saved.', + 'type' => 'int', + 'not null' => TRUE, + 'default' => 0, + ), ), 'indexes' => array( 'expire' => array('expire'), @@ -1505,12 +1517,18 @@ function system_schema_cache_8007() { 'size' => 'big', 'not null' => FALSE, ), - 'checksum' => array( + 'checksum_invalidations' => array( 'description' => 'The tag invalidation sum when this entry was saved.', 'type' => 'int', 'not null' => TRUE, 'default' => 0, ), + 'checksum_deletions' => array( + 'description' => 'The tag deletion sum when this entry was saved.', + 'type' => 'int', + 'not null' => TRUE, + 'default' => 0, + ), ), 'indexes' => array( 'expire' => array('expire'), @@ -1631,12 +1649,18 @@ function system_update_8003() { 'size' => 'big', 'not null' => FALSE, ), - 'checksum' => array( + 'checksum_invalidations' => array( 'description' => 'The tag invalidation sum when this entry was saved.', 'type' => 'int', 'not null' => TRUE, 'default' => 0, ), + 'checksum_deletions' => array( + 'description' => 'The tag deletion sum when this entry was saved.', + 'type' => 'int', + 'not null' => TRUE, + 'default' => 0, + ), ), 'indexes' => array( 'expire' => array('expire'), @@ -1707,6 +1731,12 @@ function system_update_8006() { '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'), ); @@ -1730,12 +1760,18 @@ function system_update_8007() { 'size' => 'big', 'not null' => FALSE, )); - db_add_field($table, 'checksum', array( + db_add_field($table, 'checksum_invalidations', array( 'description' => 'The tag invalidation sum when this entry was saved.', 'type' => 'int', 'not null' => TRUE, 'default' => 0, )); + db_add_field($table, 'checksum_deletions', array( + 'description' => 'The tag deletion sum when this entry was saved.', + 'type' => 'int', + 'not null' => TRUE, + 'default' => 0, + )); } } } diff --git a/core/modules/system/system.module b/core/modules/system/system.module index 950fee6..3397410 100644 --- a/core/modules/system/system.module +++ b/core/modules/system/system.module @@ -3490,7 +3490,7 @@ function system_cron() { $cache_bins = array_merge(module_invoke_all('cache_flush'), array('form', 'menu')); foreach ($cache_bins as $bin) { - cache($bin)->expire(); + cache($bin)->deleteExpired(); } // Cleanup the batch table and the queue for failed batches. diff --git a/core/modules/taxonomy/lib/Drupal/taxonomy/TermFormController.php b/core/modules/taxonomy/lib/Drupal/taxonomy/TermFormController.php index f81b9e5..2de222c 100644 --- a/core/modules/taxonomy/lib/Drupal/taxonomy/TermFormController.php +++ b/core/modules/taxonomy/lib/Drupal/taxonomy/TermFormController.php @@ -168,7 +168,7 @@ class TermFormController extends EntityFormController { drupal_set_message(t('Updated term %term.', array('%term' => $term->label()))); watchdog('taxonomy', 'Updated term %term.', array('%term' => $term->label()), WATCHDOG_NOTICE, l(t('edit'), 'taxonomy/term/' . $term->tid . '/edit')); // Clear the page and block caches to avoid stale data. - cache_invalidate(array('content' => TRUE)); + cache_invalidate_tags(array('content' => TRUE)); break; } diff --git a/core/modules/taxonomy/lib/Drupal/taxonomy/VocabularyStorageController.php b/core/modules/taxonomy/lib/Drupal/taxonomy/VocabularyStorageController.php index dd5b478..9f246e8 100644 --- a/core/modules/taxonomy/lib/Drupal/taxonomy/VocabularyStorageController.php +++ b/core/modules/taxonomy/lib/Drupal/taxonomy/VocabularyStorageController.php @@ -84,6 +84,6 @@ class VocabularyStorageController extends DatabaseStorageController { public function resetCache(array $ids = NULL) { drupal_static_reset('taxonomy_vocabulary_get_names'); parent::resetCache($ids); - cache_invalidate(array('content' => TRUE)); + cache_invalidate_tags(array('content' => TRUE)); } } diff --git a/core/modules/taxonomy/taxonomy.admin.inc b/core/modules/taxonomy/taxonomy.admin.inc index 6a14e3c..74af1d5 100644 --- a/core/modules/taxonomy/taxonomy.admin.inc +++ b/core/modules/taxonomy/taxonomy.admin.inc @@ -582,7 +582,7 @@ function taxonomy_term_confirm_delete_submit($form, &$form_state) { if (!isset($_GET['destination'])) { $form_state['redirect'] = 'admin/structure/taxonomy'; } - cache_invalidate(array('content' => TRUE)); + cache_invalidate_tags(array('content' => TRUE)); return; } @@ -622,7 +622,7 @@ function taxonomy_vocabulary_confirm_delete_submit($form, &$form_state) { drupal_set_message(t('Deleted vocabulary %name.', array('%name' => $form_state['values']['name']))); watchdog('taxonomy', 'Deleted vocabulary %name.', array('%name' => $form_state['values']['name']), WATCHDOG_NOTICE); $form_state['redirect'] = 'admin/structure/taxonomy'; - cache_invalidate(array('content' => TRUE)); + cache_invalidate_tags(array('content' => TRUE)); return; } diff --git a/core/modules/user/lib/Drupal/user/ProfileFormController.php b/core/modules/user/lib/Drupal/user/ProfileFormController.php index 5584e63..c2b139d 100644 --- a/core/modules/user/lib/Drupal/user/ProfileFormController.php +++ b/core/modules/user/lib/Drupal/user/ProfileFormController.php @@ -49,7 +49,7 @@ class ProfileFormController extends AccountFormController { // Clear the page cache because pages can contain usernames and/or profile // information: - cache_invalidate(array('content' => TRUE)); + cache_invalidate_tags(array('content' => TRUE)); drupal_set_message(t('The changes have been saved.')); } diff --git a/core/modules/user/user.admin.inc b/core/modules/user/user.admin.inc index bab307b..49c543e 100644 --- a/core/modules/user/user.admin.inc +++ b/core/modules/user/user.admin.inc @@ -805,7 +805,7 @@ function user_admin_permissions_submit($form, &$form_state) { drupal_set_message(t('The changes have been saved.')); // Clear the cached pages and blocks. - cache_invalidate(array('content' => TRUE)); + cache_invalidate_tags(array('content' => TRUE)); } /** diff --git a/core/modules/user/user.module b/core/modules/user/user.module index 7403177..b7e4d02 100644 --- a/core/modules/user/user.module +++ b/core/modules/user/user.module @@ -1980,7 +1980,7 @@ function _user_cancel($edit, $account, $method) { } // Clear the cache for anonymous users. - cache_invalidate(array('content' => TRUE)); + cache_invalidate_tags(array('content' => TRUE)); } /** diff --git a/core/modules/views/lib/Drupal/views/Plugin/views/cache/CachePluginBase.php b/core/modules/views/lib/Drupal/views/Plugin/views/cache/CachePluginBase.php index 618de3c..83fb224 100644 --- a/core/modules/views/lib/Drupal/views/Plugin/views/cache/CachePluginBase.php +++ b/core/modules/views/lib/Drupal/views/Plugin/views/cache/CachePluginBase.php @@ -196,7 +196,7 @@ function cache_get($type) { * to be sure that we catch everything. Maybe that's a bad idea. */ function cache_flush() { - cache($this->table)->invalidateTags(array($this->view->storage->get('name') => TRUE)); + cache($this->table)->deleteTags(array($this->view->storage->get('name') => TRUE)); } /** diff --git a/core/modules/views/views.module b/core/modules/views/views.module index 6347df6..adb2ffc 100644 --- a/core/modules/views/views.module +++ b/core/modules/views/views.module @@ -936,24 +936,24 @@ function views_cache_flush() { * Implements hook_field_create_instance. */ function views_field_create_instance($instance) { - cache('views_info')->flush(); - cache('views_results')->flush(); + cache('views_info')->deleteAll(); + cache('views_results')->deleteAll(); } /** * Implements hook_field_update_instance. */ function views_field_update_instance($instance, $prior_instance) { - cache('views_info')->flush(); - cache('views_results')->flush(); + cache('views_info')->deleteAll(); + cache('views_results')->deleteAll(); } /** * Implements hook_field_delete_instance. */ function views_field_delete_instance($instance) { - cache('views_info')->flush(); - cache('views_results')->flush(); + cache('views_info')->deleteAll(); + cache('views_results')->deleteAll(); } /** @@ -961,10 +961,10 @@ function views_field_delete_instance($instance) { */ function views_invalidate_cache() { // Clear the views cache. - cache('views_info')->flush(); + cache('views_info')->deleteAll(); // Clear the page and block cache. - cache_invalidate(array('content' => TRUE)); + cache_delete_tags(array('content' => TRUE)); // Set the menu as needed to be rebuilt. state()->set('menu_rebuild_needed', TRUE); diff --git a/core/update.php b/core/update.php index 98296bb..40b7970 100644 --- a/core/update.php +++ b/core/update.php @@ -268,7 +268,7 @@ function update_info_page() { _drupal_flush_css_js(); // Flush the cache of all data for the update status module. if (db_table_exists('cache_update')) { - cache('update')->flush(); + cache('update')->deleteAll(); } update_task_list('info');