From a568510c2d5a578fa929c8f4e7dd9738b37123d6 Mon Sep 17 00:00:00 2001 From: Sergey Belyshkin Date: Fri, 17 Sep 2021 15:41:12 +0700 Subject: [PATCH 1/3] [Cache] Decrease the probability of invalidation loss after tag eviction [Cache] Use gettimeofday() as a source of time --- .../Cache/Adapter/TagAwareAdapter.php | 25 +++++++++++++------ .../Tests/Adapter/TagAwareAdapterTest.php | 1 + 2 files changed, 19 insertions(+), 7 deletions(-) diff --git a/src/Symfony/Component/Cache/Adapter/TagAwareAdapter.php b/src/Symfony/Component/Cache/Adapter/TagAwareAdapter.php index cd0eaa774bc49..808c939f3be69 100644 --- a/src/Symfony/Component/Cache/Adapter/TagAwareAdapter.php +++ b/src/Symfony/Component/Cache/Adapter/TagAwareAdapter.php @@ -143,10 +143,6 @@ public function invalidateTags(array $tags) } $ok = $this->pool->commit() && $ok; - if ($invalidatedTags) { - $ok = (self::$invalidateTags)($this->tags, $invalidatedTags) && $ok; - } - return $ok; } @@ -373,7 +369,7 @@ private function generateItems(iterable $items, array $tagKeys): \Generator } } - private function getTagVersions(array $tagsByKey, array &$invalidatedTags = []) + private function getTagVersions(array $tagsByKey, array $invalidatedTags = []) { $tagVersions = $invalidatedTags; @@ -416,14 +412,29 @@ private function getTagVersions(array $tagsByKey, array &$invalidatedTags = []) return $tagVersions; } + $tod = gettimeofday(); + $newVersion = $tod['sec'] * 1000000 + $tod['usec']; + $updatedTags = []; foreach ($this->tags->getItems(array_keys($tags)) as $tag => $version) { - $tagVersions[$tag = $tags[$tag]] = $version->get() ?: 0; + $tag = $tags[$tag]; + + if ($version->isHit()) { + $tagVersions[$tag] = $version->get(); + } else { + $tagVersions[$tag] = $newVersion; + $updatedTags[$tag] = $version->set($newVersion); + } + if (isset($invalidatedTags[$tag])) { - $invalidatedTags[$tag] = $version->set(++$tagVersions[$tag]); + $updatedTags[$tag] = $version->set(++$tagVersions[$tag]); } $this->knownTagVersions[$tag] = [$now, $tagVersions[$tag]]; } + if ($updatedTags) { + (self::$invalidateTags)($this->tags, $updatedTags); + } + return $tagVersions; } } diff --git a/src/Symfony/Component/Cache/Tests/Adapter/TagAwareAdapterTest.php b/src/Symfony/Component/Cache/Tests/Adapter/TagAwareAdapterTest.php index 7e69001648fcc..1a990972e569d 100644 --- a/src/Symfony/Component/Cache/Tests/Adapter/TagAwareAdapterTest.php +++ b/src/Symfony/Component/Cache/Tests/Adapter/TagAwareAdapterTest.php @@ -83,6 +83,7 @@ public function testKnownTagVersionsTtl() $item->expiresAfter(100); $tag = $this->createMock(CacheItemInterface::class); + $tag->expects(self::exactly(2))->method('isHit')->willReturn(true); $tag->expects(self::exactly(2))->method('get')->willReturn(10); $tagsPool->expects(self::exactly(2))->method('getItems')->willReturn([ From 2abdaf24f51b5acf21b606e6e8545ba0192823e0 Mon Sep 17 00:00:00 2001 From: Sergey Belyshkin Date: Thu, 7 Oct 2021 15:10:48 +0700 Subject: [PATCH 2/3] [Cache] Use random numbers for initialization of tag version --- .../Component/Cache/Adapter/TagAwareAdapter.php | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/Symfony/Component/Cache/Adapter/TagAwareAdapter.php b/src/Symfony/Component/Cache/Adapter/TagAwareAdapter.php index 808c939f3be69..e4c5acfab060c 100644 --- a/src/Symfony/Component/Cache/Adapter/TagAwareAdapter.php +++ b/src/Symfony/Component/Cache/Adapter/TagAwareAdapter.php @@ -412,8 +412,6 @@ private function getTagVersions(array $tagsByKey, array $invalidatedTags = []) return $tagVersions; } - $tod = gettimeofday(); - $newVersion = $tod['sec'] * 1000000 + $tod['usec']; $updatedTags = []; foreach ($this->tags->getItems(array_keys($tags)) as $tag => $version) { $tag = $tags[$tag]; @@ -421,12 +419,12 @@ private function getTagVersions(array $tagsByKey, array $invalidatedTags = []) if ($version->isHit()) { $tagVersions[$tag] = $version->get(); } else { - $tagVersions[$tag] = $newVersion; - $updatedTags[$tag] = $version->set($newVersion); + $tagVersions[$tag] = mt_rand(0, \PHP_INT_MAX); + $updatedTags[$tag] = $version->set($tagVersions[$tag]); } - if (isset($invalidatedTags[$tag])) { - $updatedTags[$tag] = $version->set(++$tagVersions[$tag]); + if (isset($invalidatedTags[$tag]) && !isset($updatedTags[$tag])) { + $updatedTags[$tag] = $version->set(\PHP_INT_MAX === $tagVersions[$tag] ? 0 : ++$tagVersions[$tag]); } $this->knownTagVersions[$tag] = [$now, $tagVersions[$tag]]; } From 4e7c4fb22f382bff789a9d71fdff7d19359710b7 Mon Sep 17 00:00:00 2001 From: Sergey Belyshkin Date: Fri, 8 Oct 2021 20:39:48 +0700 Subject: [PATCH 3/3] [Cache] Expand the range of allowed version numbers --- .../Cache/Adapter/TagAwareAdapter.php | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/src/Symfony/Component/Cache/Adapter/TagAwareAdapter.php b/src/Symfony/Component/Cache/Adapter/TagAwareAdapter.php index e4c5acfab060c..baca049b183df 100644 --- a/src/Symfony/Component/Cache/Adapter/TagAwareAdapter.php +++ b/src/Symfony/Component/Cache/Adapter/TagAwareAdapter.php @@ -171,7 +171,7 @@ public function hasItem($key) } foreach ($this->getTagVersions([$itemTags]) as $tag => $version) { - if ($itemTags[$tag] !== $version && 1 !== $itemTags[$tag] - $version) { + if ($itemTags[$tag] !== $version && $itemTags[$tag] !== $version + 1 && (\PHP_INT_MIN !== $itemTags[$tag] || \PHP_INT_MAX !== $version)) { return false; } } @@ -353,7 +353,7 @@ private function generateItems(iterable $items, array $tagKeys): \Generator foreach ($itemTags as $key => $tags) { foreach ($tags as $tag => $version) { - if ($tagVersions[$tag] !== $version && 1 !== $version - $tagVersions[$tag]) { + if ($tagVersions[$tag] !== $version && $tagVersions[$tag] + 1 !== $version && (\PHP_INT_MAX !== $tagVersions[$tag] || \PHP_INT_MIN !== $version)) { unset($itemTags[$key]); continue 2; } @@ -384,7 +384,7 @@ private function getTagVersions(array $tagsByKey, array $invalidatedTags = []) if (!$fetchTagVersions = 1 !== \func_num_args()) { foreach ($tagsByKey as $tags) { foreach ($tags as $tag => $version) { - if ($tagVersions[$tag] > $version) { + if ($tagVersions[$tag] > $version || $version > 0 && $version - $tagVersions[$tag] > \PHP_INT_MAX) { $tagVersions[$tag] = $version; } } @@ -399,12 +399,14 @@ private function getTagVersions(array $tagsByKey, array $invalidatedTags = []) $fetchTagVersions = true; continue; } - $version -= $this->knownTagVersions[$tag][1]; - if ((0 !== $version && 1 !== $version) || $now - $this->knownTagVersions[$tag][0] >= $this->knownTagVersionsTtl) { + + if (($knownTagVersion = $this->knownTagVersions[$tag][1]) !== $version && $knownTagVersion + 1 !== $version && (\PHP_INT_MAX !== $knownTagVersion || \PHP_INT_MIN !== $version) + || $now - $this->knownTagVersions[$tag][0] >= $this->knownTagVersionsTtl + ) { // reuse previously fetched tag versions up to the ttl, unless we are storing items or a potential miss arises $fetchTagVersions = true; } else { - $this->knownTagVersions[$tag][1] += $version; + $this->knownTagVersions[$tag][1] = $version; } } @@ -419,12 +421,13 @@ private function getTagVersions(array $tagsByKey, array $invalidatedTags = []) if ($version->isHit()) { $tagVersions[$tag] = $version->get(); } else { - $tagVersions[$tag] = mt_rand(0, \PHP_INT_MAX); + $tagVersions[$tag] = mt_rand(\PHP_INT_MIN, \PHP_INT_MAX); $updatedTags[$tag] = $version->set($tagVersions[$tag]); } if (isset($invalidatedTags[$tag]) && !isset($updatedTags[$tag])) { - $updatedTags[$tag] = $version->set(\PHP_INT_MAX === $tagVersions[$tag] ? 0 : ++$tagVersions[$tag]); + $tagVersions[$tag] = $tagVersions[$tag] < \PHP_INT_MAX ? 1 + $tagVersions[$tag] : \PHP_INT_MIN; + $updatedTags[$tag] = $version->set($tagVersions[$tag]); } $this->knownTagVersions[$tag] = [$now, $tagVersions[$tag]]; }