Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions src/Illuminate/Cache/RedisTaggedCache.php
Original file line number Diff line number Diff line change
Expand Up @@ -126,4 +126,14 @@ public function flushStale()

return true;
}

protected function onKeyWritten(string $key): void
{
// No need to do anything as the Redis store manages the entry list internally.
}

protected function onKeyForgotten(string $key): void
{
// No need to do anything as the Redis store manages the entry list internally.
}
}
58 changes: 58 additions & 0 deletions src/Illuminate/Cache/TaggedCache.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

namespace Illuminate\Cache;

use Illuminate\Cache\Events\KeyForgotten;
use Illuminate\Cache\Events\KeyWritten;
use Illuminate\Contracts\Cache\Store;

class TaggedCache extends Repository
Expand Down Expand Up @@ -78,6 +80,10 @@ public function decrement($key, $value = 1)
*/
public function flush()
{
foreach ($this->getItemKeys() as $key) {
$this->store->forget($key);
}
$this->store->forget($this->getMetadataKey());
$this->tags->reset();

return true;
Expand Down Expand Up @@ -110,9 +116,61 @@ public function taggedItemKey($key)
*/
protected function event($event)
{
$itemKey = $this->itemKey($event->key);
if ($itemKey !== $this->getMetadataKey()) {
if ($event instanceof KeyWritten) {
$this->onKeyWritten($itemKey);
} elseif ($event instanceof KeyForgotten) {
$this->onKeyForgotten($itemKey);
}
}
parent::event($event->setTags($this->tags->getNames()));
}

protected function onKeyWritten(string $key): void
{
$itemKeys = $this->getItemKeys();
if (! in_array($key, $itemKeys)) {
$itemKeys[] = $key;
$this->putItemKeys($itemKeys);
}
}

protected function onKeyForgotten(string $key): void
{
$itemKeys = $this->getItemKeys();
if (in_array($key, $itemKeys)) {
$itemKeys = array_values(
array_filter($itemKeys, function ($k) use ($key) {
return $k !== $key;
})
);
$this->putItemKeys($itemKeys);
}
}

private function getMetadataKey(): string
{
return $this->itemKey('meta:entries');
}

private function getItemKeys(): array
{
$metadataKey = $this->getMetadataKey();
$keys = $this->store->get($metadataKey);
if (! is_array($keys)) {
$keys = [];
}

return $keys;
}

private function putItemKeys(array $keys): void
{
$metadataKey = $this->getMetadataKey();
$this->store->forever($metadataKey, $keys);
}

/**
* Get the tag set instance.
*
Expand Down
16 changes: 16 additions & 0 deletions tests/Cache/CacheTaggedCacheTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,22 @@ public function testTagsCacheForever()
$this->assertSame('bar', $store->tags($tags)->get('foo'));
}

public function testFlushFunctionDoesntLeakMemory()
{
$store = new ArrayStore;
$tags = ['bop'];
$before = memory_get_usage(true);
// Store a 5MB cache value then flush it 100 times, then verify the overall memory usage did not increase
for ($i = 0; $i < 100; $i++) {
$key = str_replace('.', '', uniqid());
$value = bin2hex(random_bytes(1024 * 5));
$store->tags($tags)->forever($key, $value);
$store->tags($tags)->flush();
}
$after = memory_get_usage(true);
$this->assertSame($before, $after);
}

private function getTestCacheStoreWithTagValues(): ArrayStore
{
$store = new ArrayStore;
Expand Down