Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 4cfc727

Browse files
[Cache] Decrease the probability of invalidation loss on tag eviction
1 parent 8eed84b commit 4cfc727

File tree

2 files changed

+55
-78
lines changed

2 files changed

+55
-78
lines changed

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

Lines changed: 54 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ class TagAwareAdapter implements TagAwareAdapterInterface, TagAwareCacheInterfac
4141
private static $createCacheItem;
4242
private static $setCacheItemTags;
4343
private static $getTagsByKey;
44-
private static $invalidateTags;
44+
private static $saveTags;
4545

4646
public function __construct(AdapterInterface $itemsPool, AdapterInterface $tagsPool = null, float $knownTagVersionsTtl = 0.15)
4747
{
@@ -95,8 +95,10 @@ static function ($deferred) {
9595
null,
9696
CacheItem::class
9797
);
98-
self::$invalidateTags ?? self::$invalidateTags = \Closure::bind(
98+
self::$saveTags ?? self::$saveTags = \Closure::bind(
9999
static function (AdapterInterface $tagsAdapter, array $tags) {
100+
ksort($tags);
101+
100102
foreach ($tags as $v) {
101103
$v->expiry = 0;
102104
$tagsAdapter->saveDeferred($v);
@@ -114,40 +116,14 @@ static function (AdapterInterface $tagsAdapter, array $tags) {
114116
*/
115117
public function invalidateTags(array $tags)
116118
{
117-
$ok = true;
118-
$tagsByKey = [];
119-
$invalidatedTags = [];
119+
$ids = [];
120120
foreach ($tags as $tag) {
121121
\assert('' !== CacheItem::validateKey($tag));
122-
$invalidatedTags[$tag] = 0;
123-
}
124-
125-
if ($this->deferred) {
126-
$items = $this->deferred;
127-
foreach ($items as $key => $item) {
128-
if (!$this->pool->saveDeferred($item)) {
129-
unset($this->deferred[$key]);
130-
$ok = false;
131-
}
132-
}
133-
134-
$tagsByKey = (self::$getTagsByKey)($items);
135-
$this->deferred = [];
136-
}
137-
138-
$tagVersions = $this->getTagVersions($tagsByKey, $invalidatedTags);
139-
$f = self::$createCacheItem;
140-
141-
foreach ($tagsByKey as $key => $tags) {
142-
$this->pool->saveDeferred($f(static::TAGS_PREFIX.$key, array_intersect_key($tagVersions, $tags), $items[$key]));
143-
}
144-
$ok = $this->pool->commit() && $ok;
145-
146-
if ($invalidatedTags) {
147-
$ok = (self::$invalidateTags)($this->tags, $invalidatedTags) && $ok;
122+
unset($this->knownTagVersions[$tag]);
123+
$ids[] = $tag.static::TAGS_PREFIX;
148124
}
149125

150-
return $ok;
126+
return !$tags || $this->tags->deleteItems($ids);
151127
}
152128

153129
/**
@@ -176,7 +152,7 @@ public function hasItem($key)
176152
}
177153

178154
foreach ($this->getTagVersions([$itemTags]) as $tag => $version) {
179-
if ($itemTags[$tag] !== $version && 1 !== $itemTags[$tag] - $version) {
155+
if ($itemTags[$tag] !== $version) {
180156
return false;
181157
}
182158
}
@@ -314,7 +290,30 @@ public function saveDeferred(CacheItemInterface $item)
314290
*/
315291
public function commit()
316292
{
317-
return $this->invalidateTags([]);
293+
if (!$this->deferred) {
294+
return true;
295+
}
296+
297+
$ok = true;
298+
foreach ($this->deferred as $key => $item) {
299+
if (!$this->pool->saveDeferred($item)) {
300+
unset($this->deferred[$key]);
301+
$ok = false;
302+
}
303+
}
304+
305+
$items = $this->deferred;
306+
$tagsByKey = (self::$getTagsByKey)($items);
307+
$this->deferred = [];
308+
309+
$tagVersions = $this->getTagVersions($tagsByKey);
310+
$f = self::$createCacheItem;
311+
312+
foreach ($tagsByKey as $key => $tags) {
313+
$this->pool->saveDeferred($f(static::TAGS_PREFIX.$key, array_intersect_key($tagVersions, $tags), $items[$key]));
314+
}
315+
316+
return $this->pool->commit() && $ok;
318317
}
319318

320319
/**
@@ -361,7 +360,7 @@ private function generateItems(iterable $items, array $tagKeys): \Generator
361360

362361
foreach ($itemTags as $key => $tags) {
363362
foreach ($tags as $tag => $version) {
364-
if ($tagVersions[$tag] !== $version && 1 !== $version - $tagVersions[$tag]) {
363+
if ($tagVersions[$tag] !== $version) {
365364
unset($itemTags[$key]);
366365
continue 2;
367366
}
@@ -377,57 +376,53 @@ private function generateItems(iterable $items, array $tagKeys): \Generator
377376
}
378377
}
379378

380-
private function getTagVersions(array $tagsByKey, array &$invalidatedTags = [])
379+
private function getTagVersions(array $tagsByKey)
381380
{
382-
$tagVersions = $invalidatedTags;
381+
$tagVersions = [];
382+
$fetchTagVersions = false;
383383

384384
foreach ($tagsByKey as $tags) {
385385
$tagVersions += $tags;
386+
387+
foreach ($tags as $tag => $version) {
388+
if ($tagVersions[$tag] !== $version) {
389+
unset($this->knownTagVersions[$tag]);
390+
}
391+
}
386392
}
387393

388394
if (!$tagVersions) {
389395
return [];
390396
}
391397

392-
if (!$fetchTagVersions = 1 !== \func_num_args()) {
393-
foreach ($tagsByKey as $tags) {
394-
foreach ($tags as $tag => $version) {
395-
if ($tagVersions[$tag] > $version) {
396-
$tagVersions[$tag] = $version;
397-
}
398-
}
399-
}
400-
}
401-
402398
$now = microtime(true);
403399
$tags = [];
404400
foreach ($tagVersions as $tag => $version) {
405401
$tags[$tag.static::TAGS_PREFIX] = $tag;
406-
if ($fetchTagVersions || !isset($this->knownTagVersions[$tag])) {
402+
if ($fetchTagVersions || ($this->knownTagVersions[$tag][1] ?? null) !== $version || $now - $this->knownTagVersions[$tag][0] >= $this->knownTagVersionsTtl) {
403+
// reuse previously fetched tag versions up to the ttl
407404
$fetchTagVersions = true;
408-
continue;
409-
}
410-
$version -= $this->knownTagVersions[$tag][1];
411-
if ((0 !== $version && 1 !== $version) || $now - $this->knownTagVersions[$tag][0] >= $this->knownTagVersionsTtl) {
412-
// reuse previously fetched tag versions up to the ttl, unless we are storing items or a potential miss arises
413-
$fetchTagVersions = true;
414-
} else {
415-
$this->knownTagVersions[$tag][1] += $version;
416405
}
417406
}
418407

419408
if (!$fetchTagVersions) {
420409
return $tagVersions;
421410
}
422411

412+
$newTags = [];
413+
$newVersion = null;
423414
foreach ($this->tags->getItems(array_keys($tags)) as $tag => $version) {
424-
$tagVersions[$tag = $tags[$tag]] = $version->get() ?: 0;
425-
if (isset($invalidatedTags[$tag])) {
426-
$invalidatedTags[$tag] = $version->set(++$tagVersions[$tag]);
415+
if (!$version->isHit()) {
416+
$newTags[$tag] = $version->set($newVersion ?? $newVersion = random_int(\PHP_INT_MIN, \PHP_INT_MAX));
427417
}
418+
$tagVersions[$tag = $tags[$tag]] = $version->get();
428419
$this->knownTagVersions[$tag] = [$now, $tagVersions[$tag]];
429420
}
430421

422+
if ($newTags) {
423+
(self::$saveTags)($this->tags, $newTags);
424+
}
425+
431426
return $tagVersions;
432427
}
433428
}

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

Lines changed: 1 addition & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -40,25 +40,6 @@ public static function tearDownAfterClass(): void
4040
(new Filesystem())->remove(sys_get_temp_dir().'/symfony-cache');
4141
}
4242

43-
/**
44-
* Test feature specific to TagAwareAdapter as it implicit needs to save deferred when also saving expiry info.
45-
*/
46-
public function testInvalidateCommitsSeperatePools()
47-
{
48-
$pool1 = $this->createCachePool();
49-
50-
$foo = $pool1->getItem('foo');
51-
$foo->tag('tag');
52-
53-
$pool1->saveDeferred($foo->set('foo'));
54-
$pool1->invalidateTags(['tag']);
55-
56-
$pool2 = $this->createCachePool();
57-
$foo = $pool2->getItem('foo');
58-
59-
$this->assertTrue($foo->isHit());
60-
}
61-
6243
public function testPrune()
6344
{
6445
$cache = new TagAwareAdapter($this->getPruneableMock());
@@ -84,6 +65,7 @@ public function testKnownTagVersionsTtl()
8465

8566
$tag = $this->createMock(CacheItemInterface::class);
8667
$tag->expects(self::exactly(2))->method('get')->willReturn(10);
68+
$tag->expects(self::exactly(2))->method('set')->willReturn($tag);
8769

8870
$tagsPool->expects(self::exactly(2))->method('getItems')->willReturn([
8971
'baz'.TagAwareAdapter::TAGS_PREFIX => $tag,

0 commit comments

Comments
 (0)