From 36abed903fabaeb5f48620b7f83f40b459508ec5 Mon Sep 17 00:00:00 2001 From: Matt Stephens Date: Thu, 4 Feb 2016 16:16:39 +0000 Subject: [PATCH 1/2] Implemented fix for #9967 that removes standard cache items aswell as forever items from Redis tag cache --- src/Illuminate/Cache/RedisTaggedCache.php | 101 +++++++++++++++++++--- tests/Cache/CacheTaggedCacheTest.php | 29 ++++++- 2 files changed, 117 insertions(+), 13 deletions(-) diff --git a/src/Illuminate/Cache/RedisTaggedCache.php b/src/Illuminate/Cache/RedisTaggedCache.php index 349627e34b18..cf2008778117 100644 --- a/src/Illuminate/Cache/RedisTaggedCache.php +++ b/src/Illuminate/Cache/RedisTaggedCache.php @@ -4,6 +4,19 @@ class RedisTaggedCache extends TaggedCache { + /** + * Forever reference key. + * + * @var string + */ + const REFERENCE_KEY_FOREVER = 'forever'; + /** + * Standard reference key. + * + * @var string + */ + const REFERENCE_KEY_STANDARD = 'standard'; + /** * Store an item in the cache indefinitely. * @@ -18,6 +31,21 @@ public function forever($key, $value) parent::forever($key, $value); } + /** + * Store an item in the cache. + * + * @param string $key + * @param mixed $value + * @param \DateTime|int $minutes + * @return void + */ + public function put($key, $value, $minutes = null) + { + $this->pushStandardKeys($this->tags->getNamespace(), $key); + + parent::put($key, $value, $minutes); + } + /** * Remove all items from the cache. * @@ -26,23 +54,49 @@ public function forever($key, $value) public function flush() { $this->deleteForeverKeys(); + $this->deleteStandardKeys(); parent::flush(); } /** - * Store a copy of the full key for each namespace segment. + * Store forever key references into store. * * @param string $namespace * @param string $key * @return void */ protected function pushForeverKeys($namespace, $key) + { + $this->pushKeys($namespace, $key, self::REFERENCE_KEY_FOREVER); + } + + /** + * Store standard key references into store. + * + * @param string $namespace + * @param string $key + * @return void + */ + protected function pushStandardKeys($namespace, $key) + { + $this->pushKeys($namespace, $key, self::REFERENCE_KEY_STANDARD); + } + + /** + * Store a reference to the cache key against the reference key. + * + * @param string $namespace + * @param string $key + * @param string $reference + * @return void + */ + protected function pushKeys($namespace, $key, $reference) { $fullKey = $this->getPrefix().sha1($namespace).':'.$key; foreach (explode('|', $namespace) as $segment) { - $this->store->connection()->lpush($this->foreverKey($segment), $fullKey); + $this->store->connection()->lpush($this->referenceKey($segment, $reference), $fullKey); } } @@ -52,37 +106,60 @@ protected function pushForeverKeys($namespace, $key) * @return void */ protected function deleteForeverKeys() + { + $this->deleteKeysByReference(self::REFERENCE_KEY_FOREVER); + } + + /** + * Delete all standard items. + * + * @return void + */ + protected function deleteStandardKeys() + { + $this->deleteKeysByReference(self::REFERENCE_KEY_STANDARD); + } + + /** + * Find and delete all of the items that were stored against a reference. + * + * @param string $reference + * @return void + */ + protected function deleteKeysByReference($reference) { foreach (explode('|', $this->tags->getNamespace()) as $segment) { - $this->deleteForeverValues($segment = $this->foreverKey($segment)); + // Now we've found the reference, delete it's items + $this->deleteValues($segment = $this->referenceKey($segment, $reference)); $this->store->connection()->del($segment); } } /** - * Delete all of the keys that have been stored forever. + * Delete item keys that have been stored against a reference. * - * @param string $foreverKey + * @param string $referenceKey * @return void */ - protected function deleteForeverValues($foreverKey) + protected function deleteValues($referenceKey) { - $forever = array_unique($this->store->connection()->lrange($foreverKey, 0, -1)); + $values = array_unique($this->store->connection()->lrange($referenceKey, 0, -1)); - if (count($forever) > 0) { - call_user_func_array([$this->store->connection(), 'del'], $forever); + if (count($values) > 0) { + call_user_func_array([$this->store->connection(), 'del'], $values); } } /** - * Get the forever reference key for the segment. + * Get the reference key for the segment. * * @param string $segment + * @param string $suffix * @return string */ - protected function foreverKey($segment) + protected function referenceKey($segment, $suffix) { - return $this->getPrefix().$segment.':forever'; + return $this->getPrefix().$segment.':'.$suffix; } } diff --git a/tests/Cache/CacheTaggedCacheTest.php b/tests/Cache/CacheTaggedCacheTest.php index a99177bc63ad..7e18ce9aaf29 100644 --- a/tests/Cache/CacheTaggedCacheTest.php +++ b/tests/Cache/CacheTaggedCacheTest.php @@ -81,7 +81,23 @@ public function testRedisCacheTagsPushForeverKeysCorrectly() $redis->forever('key1', 'key1:value'); } - public function testRedisCacheForeverTagsCanBeFlushed() + public function testRedisCacheTagsPushStandardKeysCorrectly() + { + $store = m::mock('Illuminate\Contracts\Cache\Store'); + $tagSet = m::mock('Illuminate\Cache\TagSet', [$store, ['foo', 'bar']]); + $tagSet->shouldReceive('getNamespace')->andReturn('foo|bar'); + $tagSet->shouldReceive('getNames')->andReturn(['foo', 'bar']); + $redis = new Illuminate\Cache\RedisTaggedCache($store, $tagSet); + $store->shouldReceive('getPrefix')->andReturn('prefix:'); + $store->shouldReceive('connection')->andReturn($conn = m::mock('StdClass')); + $conn->shouldReceive('lpush')->once()->with('prefix:foo:standard', 'prefix:'.sha1('foo|bar').':key1'); + $conn->shouldReceive('lpush')->once()->with('prefix:bar:standard', 'prefix:'.sha1('foo|bar').':key1'); + $store->shouldReceive('push')->with(sha1('foo|bar').':key1', 'key1:value'); + + $redis->put('key1', 'key1:value'); + } + + public function testRedisCacheTagsCanBeFlushed() { $store = m::mock('Illuminate\Contracts\Cache\Store'); $tagSet = m::mock('Illuminate\Cache\TagSet', [$store, ['foo', 'bar']]); @@ -89,12 +105,23 @@ public function testRedisCacheForeverTagsCanBeFlushed() $redis = new Illuminate\Cache\RedisTaggedCache($store, $tagSet); $store->shouldReceive('getPrefix')->andReturn('prefix:'); $store->shouldReceive('connection')->andReturn($conn = m::mock('StdClass')); + + // Forever tag keys $conn->shouldReceive('lrange')->once()->with('prefix:foo:forever', 0, -1)->andReturn(['key1', 'key2']); $conn->shouldReceive('lrange')->once()->with('prefix:bar:forever', 0, -1)->andReturn(['key3']); $conn->shouldReceive('del')->once()->with('key1', 'key2'); $conn->shouldReceive('del')->once()->with('key3'); $conn->shouldReceive('del')->once()->with('prefix:foo:forever'); $conn->shouldReceive('del')->once()->with('prefix:bar:forever'); + + // Standard tag keys + $conn->shouldReceive('lrange')->once()->with('prefix:foo:standard', 0, -1)->andReturn(['key4', 'key5']); + $conn->shouldReceive('lrange')->once()->with('prefix:bar:standard', 0, -1)->andReturn(['key6']); + $conn->shouldReceive('del')->once()->with('key4', 'key5'); + $conn->shouldReceive('del')->once()->with('key6'); + $conn->shouldReceive('del')->once()->with('prefix:foo:standard'); + $conn->shouldReceive('del')->once()->with('prefix:bar:standard'); + $tagSet->shouldReceive('reset')->once(); $redis->flush(); From 925b9e50413475df74286bcd6110e90cc32f0196 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Fri, 5 Feb 2016 08:57:16 -0600 Subject: [PATCH 2/2] fix formatting --- src/Illuminate/Cache/RedisTaggedCache.php | 31 +++++++++++------------ 1 file changed, 15 insertions(+), 16 deletions(-) diff --git a/src/Illuminate/Cache/RedisTaggedCache.php b/src/Illuminate/Cache/RedisTaggedCache.php index cf2008778117..faf7d7ebea68 100644 --- a/src/Illuminate/Cache/RedisTaggedCache.php +++ b/src/Illuminate/Cache/RedisTaggedCache.php @@ -18,32 +18,32 @@ class RedisTaggedCache extends TaggedCache const REFERENCE_KEY_STANDARD = 'standard'; /** - * Store an item in the cache indefinitely. + * Store an item in the cache. * * @param string $key * @param mixed $value + * @param \DateTime|int $minutes * @return void */ - public function forever($key, $value) + public function put($key, $value, $minutes = null) { - $this->pushForeverKeys($this->tags->getNamespace(), $key); + $this->pushStandardKeys($this->tags->getNamespace(), $key); - parent::forever($key, $value); + parent::put($key, $value, $minutes); } /** - * Store an item in the cache. + * Store an item in the cache indefinitely. * * @param string $key * @param mixed $value - * @param \DateTime|int $minutes * @return void */ - public function put($key, $value, $minutes = null) + public function forever($key, $value) { - $this->pushStandardKeys($this->tags->getNamespace(), $key); + $this->pushForeverKeys($this->tags->getNamespace(), $key); - parent::put($key, $value, $minutes); + parent::forever($key, $value); } /** @@ -60,27 +60,27 @@ public function flush() } /** - * Store forever key references into store. + * Store standard key references into store. * * @param string $namespace * @param string $key * @return void */ - protected function pushForeverKeys($namespace, $key) + protected function pushStandardKeys($namespace, $key) { - $this->pushKeys($namespace, $key, self::REFERENCE_KEY_FOREVER); + $this->pushKeys($namespace, $key, self::REFERENCE_KEY_STANDARD); } /** - * Store standard key references into store. + * Store forever key references into store. * * @param string $namespace * @param string $key * @return void */ - protected function pushStandardKeys($namespace, $key) + protected function pushForeverKeys($namespace, $key) { - $this->pushKeys($namespace, $key, self::REFERENCE_KEY_STANDARD); + $this->pushKeys($namespace, $key, self::REFERENCE_KEY_FOREVER); } /** @@ -129,7 +129,6 @@ protected function deleteStandardKeys() protected function deleteKeysByReference($reference) { foreach (explode('|', $this->tags->getNamespace()) as $segment) { - // Now we've found the reference, delete it's items $this->deleteValues($segment = $this->referenceKey($segment, $reference)); $this->store->connection()->del($segment);