diff --git a/README.txt b/README.txt index 6705434..376d7c2 100644 --- a/README.txt +++ b/README.txt @@ -178,11 +178,6 @@ if ($memcache_exists || $memcached_exists) { $settings['bootstrap_container_definition'] = [ 'parameters' => [], 'services' => [ - 'database' => [ - 'class' => 'Drupal\Core\Database\Connection', - 'factory' => 'Drupal\Core\Database\Database::getConnection', - 'arguments' => ['default'], - ], 'settings' => [ 'class' => 'Drupal\Core\Site\Settings', 'factory' => 'Drupal\Core\Site\Settings::getInstance', @@ -205,8 +200,8 @@ if ($memcache_exists || $memcached_exists) { 'arguments' => ['container', '@memcache.backend.cache.container'], ], 'cache_tags_provider.container' => [ - 'class' => 'Drupal\Core\Cache\DatabaseCacheTagsChecksum', - 'arguments' => ['@database'], + 'class' => 'Drupal\memcache\MemcacheCacheTagsChecksum', + 'arguments' => ['cache_tags', '@memcache.backend.cache.factory'], ], 'cache.container' => [ 'class' => 'Drupal\memcache\MemcacheBackend', diff --git a/example.services.yml b/example.services.yml new file mode 100644 index 0000000..a5f25f8 --- /dev/null +++ b/example.services.yml @@ -0,0 +1,27 @@ +# This file contains example services overrides. +# +# Enable with this line in settings.php +# $settings['container_yamls'][] = 'modules/memcache/example.services.yml'; +# +# Or copy & paste the desired services into sites/default/services.yml. +# +# Note that the memcache module must be enabled for this to work. + +services: + # Cache tag checksum backend. Used by memcache and most other cache backends + # to deal with cache tag invalidations. + cache_tags.invalidator.checksum: + class: Drupal\memcache\MemcacheCacheTagsChecksum + arguments: ['cache_tags', '@memcache.factory'] + tags: + - { name: cache_tags_invalidator } + + # Replaces the default lock backend with a memcache implementation. + lock: + class: Drupal\Core\Lock\LockBackendInterface + factory: ['@memcache.lock.factory', get] + + # Replaces the default persistent lock backend with a memcache implementation. + lock.persistent: + class: Drupal\Core\Lock\LockBackendInterface + factory: ['@memcache.lock.factory', getPersistent] diff --git a/src/DrupalMemcacheBase.php b/src/DrupalMemcacheBase.php index 7c2bf39..45a500c 100644 --- a/src/DrupalMemcacheBase.php +++ b/src/DrupalMemcacheBase.php @@ -67,6 +67,22 @@ abstract class DrupalMemcacheBase implements DrupalMemcacheInterface { /** * {@inheritdoc} */ + public function increment($key, $value = 1) { + // Ensure item exists, since increment won't create it. + $this->memcache->add($key, (int)0); + return $this->memcache->increment($key, $value); + } + + /** + * {@inheritdoc} + */ + public function decrement($key, $value = 1) { + return $this->memcache->decrement($key, $value); + } + + /** + * {@inheritdoc} + */ public function key($key) { $full_key = urlencode($this->prefix . '-' . $key); diff --git a/src/DrupalMemcacheInterface.php b/src/DrupalMemcacheInterface.php index 530ba6b..c8c9b45 100644 --- a/src/DrupalMemcacheInterface.php +++ b/src/DrupalMemcacheInterface.php @@ -47,6 +47,34 @@ interface DrupalMemcacheInterface { public function get($key); /** + * Increment an integer value in Memcache. + * + * @param string $key + * A key which is either not set or has an integer value. + * + * @param int $value + * Amount to increment by. + * + * @return mixed + * The item's new value, or FALSE on failure. + */ + public function increment($key, $value = 1); + + /** + * Decrement an integer value in Memcache. + * + * @param string $key + * A key storing an integer item. + * + * @param int $value + * Amount to decrement by. + * + * @return mixed + * The item's new value, or FALSE on failure. + */ + public function decrement($key, $value = 1); + + /** * Retrieves multiple values from Memcache. * * @param array $keys diff --git a/src/MemcacheCacheTagsChecksum.php b/src/MemcacheCacheTagsChecksum.php new file mode 100644 index 0000000..a37b5fa --- /dev/null +++ b/src/MemcacheCacheTagsChecksum.php @@ -0,0 +1,144 @@ +bin = $bin; + $this->memcache = $factory->get($bin); + } + + /** + * {@inheritdoc} + */ + public function invalidateTags(array $tags) { + foreach ($tags as $tag) { + // Only invalidate tags once per request unless they are written again. + if (isset($this->invalidatedTags[$tag])) { + continue; + } + $this->invalidatedTags[$tag] = TRUE; + unset($this->tagCache[$tag]); + $this->memcache->increment($this->getKey($tag)); + } + } + + /** + * {@inheritdoc} + */ + public function getCurrentChecksum(array $tags) { + // Remove tags that were already invalidated during this request from the + // static caches so that another invalidation can occur later in the same + // request. Without that, written cache items would not be invalidated + // correctly. + foreach ($tags as $tag) { + unset($this->invalidatedTags[$tag]); + } + return $this->calculateChecksum($tags); + } + + /** + * {@inheritdoc} + */ + public function isValid($checksum, array $tags) { + return $checksum == $this->calculateChecksum($tags); + } + + /** + * Calculates the current checksum for a given set of tags. + * + * @param array $tags + * The array of tags to calculate the checksum for. + * + * @return int + * The calculated checksum. + */ + protected function calculateChecksum(array $tags) { + $checksum = 0; + + $query_tags = array_diff($tags, array_keys($this->tagCache)); + if ($query_tags) { + $memcache_tags = $this->memcache->getMulti($query_tags); + $this->tagCache += $memcache_tags; + // Fill static cache with empty objects for tags not found in the database. + $this->tagCache += array_fill_keys(array_diff($query_tags, array_keys($memcache_tags)), 0); + } + + foreach ($tags as $tag) { + $checksum += $this->tagCache[$tag]; + } + + return $checksum; + } + + /** + * {@inheritdoc} + */ + public function reset() { + $this->tagCache = []; + $this->invalidatedTags = []; + } + + /** + * Gets a storage key based on the tag name. + * + * @param $name string + * Tag name. + * + * @return string + */ + protected function getKey($name) { + //$this->memcache->key() something? + // @todo Maybe hash the key? + return 'tag:' . $this->bin . ':' . $name; + } + +}