8000 bug #45896 [Cache] Optimize caching of tags (sbelyshkin) · symfony/symfony@fa636c0 · GitHub
[go: up one dir, main page]

Skip to content

Commit fa636c0

Browse files
bug #45896 [Cache] Optimize caching of tags (sbelyshkin)
This PR was merged into the 6.1 branch. Discussion ---------- [Cache] Optimize caching of tags | Q | A | ------------- | --- | Branch? | 6.1 | Bug fix? | no | New feature? | no <!-- please update src/**/CHANGELOG.md files --> | Deprecations? | no <!-- please update UPGRADE-*.md and src/**/CHANGELOG.md files --> | Tickets | - <!-- prefix each issue number with "Fix #", no need to create an issue if none exist, explain below instead --> | License | MIT | Doc PR | - <!-- required for new features --> It's the follow-up to #42997. 1. Forcing the adapter to fetch tags on item commits makes the algorithm straightforward and a bit more optimized. 2. Caching tag versions only when they are retrieved from or being persisted to the pool ensures that any tagged item will be rejected when there is no related tags in the pool. 3. Using FIFO instead of LRU for known tag versions allows to use all cached tags until expiration and still be able to prevent memory leak. <!-- Replace this notice by a short README for your feature/bugfix. This will help reviewers and should be a good start for the documentation. Additionally (see https://symfony.com/releases): - Always add tests and ensure they pass. - Bug fixes must be submitted against the lowest maintained branch where they apply (lowest branches are regularly merged to upper ones so they get the fixes too.) - Features and deprecations must be submitted against the latest branch. - Changelog entry should follow https://symfony.com/doc/current/contributing/code/conventions.html#writing-a-changelog-entry - Never break backward compatibility (see https://symfony.com/bc). --> Commits ------- 68f309b [Cache] Optimize caching of tags
2 parents 6bc0882 + 68f309b commit fa636c0

File tree

2 files changed

+26
-24
lines changed

2 files changed

+26
-24
lines changed

src/Symfony/Component/Cache/Adapter/TagAwareAdapter.php

+18-18
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,6 @@ class TagAwareAdapter implements TagAwareAdapterInterface, TagAwareCacheInterfac
3939
use LoggerAwareTrait;
4040

4141
public const TAGS_PREFIX = "\0tags\0";
42-
private const MAX_NUMBER_OF_KNOWN_TAG_VERSIONS = 1000;
4342

4443
private array $deferred = [];
4544
private AdapterInterface $pool;
@@ -79,8 +78,7 @@ static function (array $items, array $itemTags) {
7978
);
8079
self::$setTagVersions ??= \Closure::bind(
8180
static function (array $items, array $tagVersions) {
82-
$now = null;
83-
foreach ($items as $key => $item) {
81+
foreach ($items as $item) {
8482
$item->newMetadata[CacheItem::METADATA_TAGS] = array_intersect_key($tagVersions, $item->newMetadata[CacheItem::METADATA_TAGS] ?? []);
8583
}
8684
},
@@ -343,14 +341,16 @@ public function __destruct()
343341
private function getTagVersions(array $tagsByKey, bool $persistTags): array
344342
{
345343
$tagVersions = [];
346-
$fetchTagVersions = false;
344+
$fetchTagVersions = $persistTags;
347345

348346
foreach ($tagsByKey as $tags) {
349347
$tagVersions += $tags;
350-
348+
if ($fetchTagVersions) {
349+
continue;
350+
}
351351
foreach ($tags as $tag => $version) {
352352
if ($tagVersions[$tag] !== $version) {
353-
unset($this->knownTagVersions[$tag]);
353+
$fetchTagVersions = true;
354354
}
355355
}
356356
}
@@ -364,14 +364,10 @@ private function getTagVersions(array $tagsByKey, bool $persistTags): array
364364
foreach ($tagVersions as $tag => $version) {
365365
$tags[$tag.static::TAGS_PREFIX] = $tag;
366366
$knownTagVersion = $this->knownTagVersions[$tag] ?? [0, null];
367-
if ($fetchTagVersions || $knownTagVersion[1] !== $version || $now - $knownTagVersion[0] >= $this->knownTagVersionsTtl) {
368-
// reuse previously fetched tag versions up to the ttl
367+
if ($fetchTagVersions || $now > $knownTagVersion[0] || $knownTagVersion[1] !== $version) {
368+
// reuse previously fetched tag versions until the expiration
369369
$fetchTagVersions = true;
370370
}
371-
unset($this->knownTagVersions[$tag]); // For LRU tracking
372-
if ([0, null] !== $knownTagVersion) {
373-
$this->knownTagVersions[$tag] = $knownTagVersion;
374-
}
375371
}
376372

377373
if (!$fetchTagVersions) {
@@ -380,20 +376,24 @@ private function getTagVersions(array $tagsByKey, bool $persistTags): array
380376

381377
$newTags = [];
382378
$newVersion = null;
379+
$expiration = $now + $this->knownTagVersionsTtl;
383380
foreach ($this->tags->getItems(array_keys($tags)) as $tag => $version) {
384-
if (!$version->isHit()) {
381+
unset($this->knownTagVersions[$tag = $tags[$tag]]); // update FIFO
382+
if (null !== $tagVersions[$tag] = $version->get()) {
383+
$this->knownTagVersions[$tag] = [$expiration, $tagVersions[$tag]];
384+
} elseif ($persistTags) {
385385
$newTags[$tag] = $version->set($newVersion ??= random_bytes(6));
386+
$tagVersions[$tag] = $newVersion;
387+
$this->knownTagVersions[$tag] = [$expiration, $newVersion];
386388
}
387-
$tagVersions[$tag = $tags[$tag]] = $version->get();
388-
$this->knownTagVersions[$tag] = [$now, $tagVersions[$tag]];
389389
}
390390

391-
if ($newTags && $persistTags) {
391+
if ($newTags) {
392392
(self::$saveTags)($this->tags, $newTags);
393393
}
394394

395-
if (\count($this->knownTagVersions) > $maxTags = max(self::MAX_NUMBER_OF_KNOWN_TAG_VERSIONS, \count($newTags) << 1)) {
396-
array_splice($this->knownTagVersions, 0, $maxTags >> 1);
395+
while ($now > ($this->knownTagVersions[$tag = array_key_first($this->knownTagVersions)][0] ?? \INF)) {
396+
unset($this->knownTagVersions[$tag]);
397397
}
398398

399399
return $tagVersions;

src/Symfony/Component/Cache/Tests/Adapter/TagAwareAdapterTest.php

+8-6
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@
1212
namespace Symfony\Component\Cache\Tests\Adapter;
1313

1414
use PHPUnit\Framework\MockObject\MockObject;
15-
use Psr\Cache\CacheItemInterface;
1615
use Psr\Cache\CacheItemPoolInterface;
1716
use Symfony\Component\Cache\Adapter\AdapterInterface;
1817
use Symfony\Component\Cache\Adapter\ArrayAdapter;
@@ -67,15 +66,18 @@ public function testKnownTagVersionsTtl()
6766

6867
$pool->save($item);
6968
$this->assertTrue($pool->getItem('foo')->isHit());
70-
$this->assertTrue($pool->getItem('foo')->isHit());
7169

72-
sleep(20);
70+
$tagsPool->deleteItem('baz'.TagAwareAdapter::TAGS_PREFIX); // tag invalidation
7371

74-
$this->assertTrue($pool->getItem('foo')->isHit());
72+
$this->assertTrue($pool->getItem('foo')->isHit()); // known tag version is used
7573

76-
sleep(5);
74+
sleep(10);
7775

78-
$this->assertTrue($pool->getItem('foo')->isHit());
76+
$this->assertTrue($pool->getItem('foo')->isHit()); // known tag version is still used
77+
78+
sleep(1);
79+
80+
$this->assertFalse($pool->getItem('foo')->isHit()); // known tag version has expired
7981
}
8082

8183
public function testInvalidateTagsWithArrayAdapter()

0 commit comments

Comments
 (0)
0