diff --git a/README.md b/README.md index 0f1dcda..ad3f9d0 100644 --- a/README.md +++ b/README.md @@ -16,10 +16,12 @@ installed using Composer: composer require wieni/wmpage_cache_redis ``` -To enable this cache storage, change the `wmpage_cache.storage` container parameter: +To enable this cache storage, change the following container parameters: ```yaml parameters: wmpage_cache.storage: wmpage_cache.storage.redis + wmpage_cache.invalidator: wmpage_cache_redis.checksum + wmpage_cache.checksum: wmpage_cache_redis.checksum wmpage_cache.redis.prefix: 'wmpage_cache:' ``` diff --git a/src/RedisCacheTagsChecksum.php b/src/RedisCacheTagsChecksum.php new file mode 100644 index 0000000..9da0a9c --- /dev/null +++ b/src/RedisCacheTagsChecksum.php @@ -0,0 +1,78 @@ +redisClientFactory = $redisClientFactory; + } + + public function getCurrentChecksum(array $tags) + { + if ($decorated = $this->decorated()) { + return $decorated->getCurrentChecksum($tags); + } + + return 'noop'; + } + + public function isValid($checksum, array $tags) + { + if ($decorated = $this->decorated()) { + return $decorated->isValid($checksum, $tags); + } + + return false; + } + + public function reset() + { + if ($decorated = $this->decorated()) { + $decorated->reset(); + } + } + + public function invalidateCacheTags(array $tags): void + { + // noop + // We use Drupal's cache tag invalidation system to invalidate the cache + } + + public function invalidateTags(array $tags) + { + if ($decorated = $this->decorated()) { + $decorated->invalidateTags($tags); + } + } + + /** @return CacheTagsChecksumInterface|CacheTagsInvalidatorInterface|null */ + protected function decorated() + { + if (isset($this->redisCacheTagsChecksum)) { + return $this->redisCacheTagsChecksum ?: null; + } + + $decorated = false; + try { + $client = $this->redisClientFactory::getClient(); + if ($client instanceof \Redis) { + $decorated = new RedisCacheTagsChecksum($this->redisClientFactory); + } + } catch (\Exception $e) { + watchdog_exception('wmcontroller.redis', $e); + } + + return $this->redisCacheTagsChecksum = $decorated; + } +} diff --git a/src/RedisCacheTagsChecksumClientFactory.php b/src/RedisCacheTagsChecksumClientFactory.php new file mode 100644 index 0000000..6184886 --- /dev/null +++ b/src/RedisCacheTagsChecksumClientFactory.php @@ -0,0 +1,13 @@ +redis = null; watchdog_exception('wmpage_cache_redis', $e); } + $this->checksumProvider = $checksumProvider; $this->serializer = $serializer; $this->prefix = $prefix; } @@ -60,11 +65,29 @@ public function loadMultiple(array $ids, bool $includeBody = true): \Iterator $rows = $this->redis->mget( $this->prefix($chunk, $includeBody ? 'body' : '') ); + $checksums = $this->redis->mget( + $this->prefix($chunk, 'checksum') + ); - foreach (array_filter($rows) as $rowJson) { + foreach ($rows as $i => $rowJson) { + if (!$rowJson) { + continue; + } $row = json_decode($rowJson, true); $item = $this->serializer->denormalize($row); - if ($item->getExpiry() > $time) { + $tags = $this->redis->sMembers( + $this->prefix($item->getId(), 'tags') + ); + if ( + $item->getExpiry() > $time + && ( + !isset($checksums[$i]) + || $this->checksumProvider->isValid( + $checksums[$i], + $tags + ) + ) + ) { yield $item; } } @@ -91,6 +114,11 @@ public function set(Cache $item, array $tags): void json_encode($this->serializer->normalize($item, true)), ($item->getExpiry() - $time) ); + $tx->set( + $this->prefix($id, 'checksum'), + $this->checksumProvider->getCurrentChecksum($tags), + ($item->getExpiry() - $time) + ); $tx->zAdd( $this->prefix('expiries'), $item->getExpiry(), @@ -155,6 +183,7 @@ public function remove(array $ids): void $tx->del($this->prefix($id)); $tx->del($this->prefix($id, 'body')); $tx->del($this->prefix($id, 'tags')); + $tx->del($this->prefix($id, 'checksum')); $tx->del($this->prefix($id, 'path')); $tx->zRem($this->prefix('expiries'), $id); diff --git a/wmpage_cache_redis.services.yml b/wmpage_cache_redis.services.yml index be99074..2867517 100644 --- a/wmpage_cache_redis.services.yml +++ b/wmpage_cache_redis.services.yml @@ -9,5 +9,15 @@ services: class: Drupal\wmpage_cache_redis\Storage\RedisStorage arguments: - '@wmpage_cache.redis.factory' + - '@wmpage_cache.checksum' - '@wmpage_cache.serializer' - '%wmpage_cache.redis.prefix%' + + wmpage_cache_redis.checksum_factory: + class: Drupal\wmpage_cache_redis\RedisCacheTagsChecksumClientFactory + + wmpage_cache_redis.checksum: + class: Drupal\wmpage_cache_redis\RedisCacheTagsChecksum + arguments: + - '@wmpage_cache_redis.checksum_factory' + tags: [{ name: cache_tags_invalidator }]