From 68f309b6782057d1eeb8e9ab5c397e8f8949be8b Mon Sep 17 00:00:00 2001 From: Sergey Belyshkin Date: Wed, 30 Mar 2022 18:52:37 +0700 Subject: [PATCH] [Cache] Optimize caching of tags --- .../Cache/Adapter/TagAwareAdapter.php | 36 +++++++++---------- .../Tests/Adapter/TagAwareAdapterTest.php | 14 ++++---- 2 files changed, 26 insertions(+), 24 deletions(-) diff --git a/src/Symfony/Component/Cache/Adapter/TagAwareAdapter.php b/src/Symfony/Component/Cache/Adapter/TagAwareAdapter.php index 7e7395b71281a..732d024290982 100644 --- a/src/Symfony/Component/Cache/Adapter/TagAwareAdapter.php +++ b/src/Symfony/Component/Cache/Adapter/TagAwareAdapter.php @@ -39,7 +39,6 @@ class TagAwareAdapter implements TagAwareAdapterInterface, TagAwareCacheInterfac use LoggerAwareTrait; public const TAGS_PREFIX = "\0tags\0"; - private const MAX_NUMBER_OF_KNOWN_TAG_VERSIONS = 1000; private array $deferred = []; private AdapterInterface $pool; @@ -79,8 +78,7 @@ static function (array $items, array $itemTags) { ); self::$setTagVersions ??= \Closure::bind( static function (array $items, array $tagVersions) { - $now = null; - foreach ($items as $key => $item) { + foreach ($items as $item) { $item->newMetadata[CacheItem::METADATA_TAGS] = array_intersect_key($tagVersions, $item->newMetadata[CacheItem::METADATA_TAGS] ?? []); } }, @@ -343,14 +341,16 @@ public function __destruct() private function getTagVersions(array $tagsByKey, bool $persistTags): array { $tagVersions = []; - $fetchTagVersions = false; + $fetchTagVersions = $persistTags; foreach ($tagsByKey as $tags) { $tagVersions += $tags; - + if ($fetchTagVersions) { + continue; + } foreach ($tags as $tag => $version) { if ($tagVersions[$tag] !== $version) { - unset($this->knownTagVersions[$tag]); + $fetchTagVersions = true; } } } @@ -364,14 +364,10 @@ private function getTagVersions(array $tagsByKey, bool $persistTags): array foreach ($tagVersions as $tag => $version) { $tags[$tag.static::TAGS_PREFIX] = $tag; $knownTagVersion = $this->knownTagVersions[$tag] ?? [0, null]; - if ($fetchTagVersions || $knownTagVersion[1] !== $version || $now - $knownTagVersion[0] >= $this->knownTagVersionsTtl) { - // reuse previously fetched tag versions up to the ttl + if ($fetchTagVersions || $now > $knownTagVersion[0] || $knownTagVersion[1] !== $version) { + // reuse previously fetched tag versions until the expiration $fetchTagVersions = true; } - unset($this->knownTagVersions[$tag]); // For LRU tracking - if ([0, null] !== $knownTagVersion) { - $this->knownTagVersions[$tag] = $knownTagVersion; - } } if (!$fetchTagVersions) { @@ -380,20 +376,24 @@ private function getTagVersions(array $tagsByKey, bool $persistTags): array $newTags = []; $newVersion = null; + $expiration = $now + $this->knownTagVersionsTtl; foreach ($this->tags->getItems(array_keys($tags)) as $tag => $version) { - if (!$version->isHit()) { + unset($this->knownTagVersions[$tag = $tags[$tag]]); // update FIFO + if (null !== $tagVersions[$tag] = $version->get()) { + $this->knownTagVersions[$tag] = [$expiration, $tagVersions[$tag]]; + } elseif ($persistTags) { $newTags[$tag] = $version->set($newVersion ??= random_bytes(6)); + $tagVersions[$tag] = $newVersion; + $this->knownTagVersions[$tag] = [$expiration, $newVersion]; } - $tagVersions[$tag = $tags[$tag]] = $version->get(); - $this->knownTagVersions[$tag] = [$now, $tagVersions[$tag]]; } - if ($newTags && $persistTags) { + if ($newTags) { (self::$saveTags)($this->tags, $newTags); } - if (\count($this->knownTagVersions) > $maxTags = max(self::MAX_NUMBER_OF_KNOWN_TAG_VERSIONS, \count($newTags) << 1)) { - array_splice($this->knownTagVersions, 0, $maxTags >> 1); + while ($now > ($this->knownTagVersions[$tag = array_key_first($this->knownTagVersions)][0] ?? \INF)) { + unset($this->knownTagVersions[$tag]); } return $tagVersions; diff --git a/src/Symfony/Component/Cache/Tests/Adapter/TagAwareAdapterTest.php b/src/Symfony/Component/Cache/Tests/Adapter/TagAwareAdapterTest.php index 5d6c52892b45f..c242c0dadaa3c 100644 --- a/src/Symfony/Component/Cache/Tests/Adapter/TagAwareAdapterTest.php +++ b/src/Symfony/Component/Cache/Tests/Adapter/TagAwareAdapterTest.php @@ -12,7 +12,6 @@ namespace Symfony\Component\Cache\Tests\Adapter; use PHPUnit\Framework\MockObject\MockObject; -use Psr\Cache\CacheItemInterface; use Psr\Cache\CacheItemPoolInterface; use Symfony\Component\Cache\Adapter\AdapterInterface; use Symfony\Component\Cache\Adapter\ArrayAdapter; @@ -67,15 +66,18 @@ public function testKnownTagVersionsTtl() $pool->save($item); $this->assertTrue($pool->getItem('foo')->isHit()); - $this->assertTrue($pool->getItem('foo')->isHit()); - sleep(20); + $tagsPool->deleteItem('baz'.TagAwareAdapter::TAGS_PREFIX); // tag invalidation - $this->assertTrue($pool->getItem('foo')->isHit()); + $this->assertTrue($pool->getItem('foo')->isHit()); // known tag version is used - sleep(5); + sleep(10); - $this->assertTrue($pool->getItem('foo')->isHit()); + $this->assertTrue($pool->getItem('foo')->isHit()); // known tag version is still used + + sleep(1); + + $this->assertFalse($pool->getItem('foo')->isHit()); // known tag version has expired } public function testInvalidateTagsWithArrayAdapter()