Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit88258e4

Browse files
committed
feat:#47835 added redis garbage collector
1 parent877a8e8 commit88258e4

File tree

2 files changed

+192
-2
lines changed

2 files changed

+192
-2
lines changed

‎src/Symfony/Component/Cache/Adapter/AbstractTagAwareAdapter.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ abstract class AbstractTagAwareAdapter implements TagAwareAdapterInterface, TagA
3535
use AbstractAdapterTrait;
3636
use ContractsTrait;
3737

38-
privateconstTAGS_PREFIX ="\1tags\1";
38+
protectedconstTAGS_PREFIX ="\1tags\1";
3939

4040
protectedfunction__construct(string$namespace ='',int$defaultLifetime =0)
4141
{

‎src/Symfony/Component/Cache/Adapter/RedisTagAwareAdapter.php

Lines changed: 191 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
useSymfony\Component\Cache\Marshaller\DeflateMarshaller;
2424
useSymfony\Component\Cache\Marshaller\MarshallerInterface;
2525
useSymfony\Component\Cache\Marshaller\TagAwareMarshaller;
26+
useSymfony\Component\Cache\PruneableInterface;
2627
useSymfony\Component\Cache\Traits\RedisTrait;
2728

2829
/**
@@ -44,7 +45,7 @@
4445
* @author Nicolas Grekas <p@tchwork.com>
4546
* @author André Rømcke <andre.romcke+symfony@gmail.com>
4647
*/
47-
class RedisTagAwareAdapterextends AbstractTagAwareAdapter
48+
class RedisTagAwareAdapterextends AbstractTagAwareAdapterimplements PruneableInterface
4849
{
4950
use RedisTrait;
5051

@@ -305,4 +306,193 @@ private function getRedisEvictionPolicy(): string
305306

306307
return$this->redisEvictionPolicy ='';
307308
}
309+
310+
311+
privatefunctiongetPrefix():string
312+
{
313+
if ($this->redisinstanceof \Predis\ClientInterface) {
314+
$prefix =$this->redis->getOptions()->prefix ?$this->redis->getOptions()->prefix->getPrefix() :'';
315+
}elseif (\is_array($prefix =$this->redis->getOption(\Redis::OPT_PREFIX) ??'')) {
316+
$prefix =current($prefix);
317+
}
318+
return$prefix;
319+
}
320+
321+
/**
322+
* Returns all existing tag keys from the cache.
323+
*
324+
* @TODO Verify the LUA scripts are redis-cluster safe.
325+
*
326+
* @return array
327+
*/
328+
protectedfunctiongetAllTagKeys():array
329+
{
330+
$tagKeys = [];
331+
$prefix =$this->getPrefix();
332+
// need to trim the \0 for lua script
333+
$tagsPrefix =trim(self::TAGS_PREFIX);
334+
335+
// get all SET entries which are tagged
336+
$getTagsLua = <<<'EOLUA'
337+
redis.replicate_commands()
338+
local cursor = ARGV[1]
339+
local prefix = ARGV[2]
340+
local tagPrefix = string.gsub(KEYS[1], prefix, "")
341+
return redis.call('SCAN', cursor, 'COUNT', 5000, 'MATCH', '*' .. tagPrefix .. '*', 'TYPE', 'set')
342+
EOLUA;
343+
$cursor =null;
344+
do {
345+
$results =$this->pipeline(function ()use ($getTagsLua,$cursor,$prefix,$tagsPrefix) {
346+
yield'eval' => [$getTagsLua, [$tagsPrefix,$cursor,$prefix],1];
347+
});
348+
349+
$setKeys =$results->valid() ?iterator_to_array($results) : [];
350+
[$cursor,$ids] =$setKeys[$tagsPrefix] ?? [null,null];
351+
// merge the fetched ids together
352+
$tagKeys =array_merge($tagKeys,$ids);
353+
}while ($cursor = (int)$cursor);
354+
355+
return$tagKeys;
356+
}
357+
358+
359+
/**
360+
* Checks all tags in the cache for orphaned items and creates a "report" array.
361+
*
362+
* By default, only completely orphaned tag keys are reported. If
363+
* compressMode is enabled the report will include all tag keys
364+
* that have any orphaned references to cache items
365+
*
366+
* @TODO Verify the LUA scripts are redis-cluster safe.
367+
* @TODO Is there anything that can be done to reduce memory footprint?
368+
*
369+
* @param bool $compressMode
370+
* @return array{tagKeys: string[], orphanedTagKeys: string[], orphanedTagReferenceKeys?: array<string, string[]>}
371+
* tagKeys: List of all tags in the cache.
372+
* orphanedTagKeys: List of tags that only reference orphaned cache items.
373+
* orphanedTagReferenceKeys: List of all orphaned cache item references per tag.
374+
* Keyed by tag, value is the list of orphaned cache item keys.
375+
*/
376+
privatefunctiongetOrphanedTagsStats(bool$compressMode =false):array
377+
{
378+
$prefix =$this->getPrefix();
379+
$tagKeys =$this->getAllTagKeys();
380+
381+
// lua for fetching all entries/content from a SET
382+
$getSetContentLua = <<<'EOLUA'
383+
redis.replicate_commands()
384+
local cursor = ARGV[1]
385+
return redis.call('SSCAN', KEYS[1], cursor, 'COUNT', 5000)
386+
EOLUA;
387+
388+
$orphanedTagReferenceKeys = [];
389+
$orphanedTagKeys = [];
390+
// Iterate over each tag and check if its entries reference orphaned
391+
// cache items.
392+
foreach ($tagKeysas$tagKey) {
393+
$tagKey =substr($tagKey,strlen($prefix));
394+
$cursor =null;
395+
$hasExistingKeys =false;
396+
do {
397+
// Fetch all referenced cache keys from the tag entry.
398+
$results =$this->pipeline(function ()use ($getSetContentLua,$tagKey,$cursor) {
399+
yield'eval' => [$getSetContentLua, [$tagKey,$cursor],1];
400+
});
401+
[$cursor,$referencedCacheKeys] =$results->valid() ?$results->current() : [null,null];
402+
403+
if (!empty($referencedCacheKeys)) {
404+
// Counts how many of the referenced cache items exist.
405+
$existingCacheKeysResult =$this->pipeline(function ()use ($referencedCacheKeys) {
406+
yield'exists' =>$referencedCacheKeys;
407+
});
408+
$existingCacheKeysCount =$existingCacheKeysResult->valid() ?$existingCacheKeysResult->current() :0;
409+
$hasExistingKeys =$hasExistingKeys || ($existingCacheKeysCount >0 ??false);
410+
411+
// If compression mode is enabled and the count between
412+
// referenced and existing cache keys differs collect the
413+
// missing references.
414+
if ($compressMode &&count($referencedCacheKeys) >$existingCacheKeysCount) {
415+
// In order to create the delta each single reference
416+
// has to be checked.
417+
foreach ($referencedCacheKeysas$cacheKey) {
418+
$existingCacheKeyResult =$this->pipeline(function ()use ($cacheKey) {
419+
yield'exists' => [$cacheKey];
420+
});
421+
if ($existingCacheKeyResult->valid() && !$existingCacheKeyResult->current()) {
422+
$orphanedTagReferenceKeys[$tagKey][] =$cacheKey;
423+
}
424+
}
425+
}
426+
// Stop processing cursors in case compression mode is
427+
// disabled and the tag references existing keys.
428+
if (!$compressMode &&$hasExistingKeys) {
429+
break;
430+
}
431+
}
432+
}while ($cursor = (int)$cursor);
433+
if (!$hasExistingKeys) {
434+
$orphanedTagKeys[] =$tagKey;
435+
}
436+
}
437+
438+
$stats = ['orphanedTagKeys' =>$orphanedTagKeys,'tagKeys' =>$tagKeys];
439+
if ($compressMode) {
440+
$stats['orphanedTagReferenceKeys'] =$orphanedTagReferenceKeys;
441+
}
442+
return$stats;
443+
}
444+
445+
/**
446+
*
447+
* @TODO Verify the LUA scripts are redis-cluster safe.
448+
*
449+
* @param bool $compressMode
450+
* @return bool
451+
*/
452+
privatefunctionpruneOrphanedTags(bool$compressMode =false):bool
453+
{
454+
$success =true;
455+
$orphanedTagsStats =$this->getOrphanedTagsStats($compressMode);
456+
457+
// Delete all tags that don't reference any existing cache item.
458+
foreach ($orphanedTagsStats['orphanedTagKeys']as$orphanedTagKey) {
459+
$result =$this->pipeline(function ()use ($orphanedTagKey) {
460+
yield'del' => [$orphanedTagKey];
461+
});
462+
if (!$result->valid() ||$result->current() !==1) {
463+
$success =false;
464+
}
465+
}
466+
// If orphaned cache key references are provided prune them too.
467+
if (!empty($orphanedTagsStats['orphanedTagReferenceKeys'])) {
468+
// lua for deleting member from a SET
469+
$removeSetMemberLua = <<<'EOLUA'
470+
redis.replicate_commands()
471+
return redis.call('SREM', KEYS[1], KEYS[2])
472+
EOLUA;
473+
// Loop through all tags with orphaned cache item references.
474+
foreach ($orphanedTagsStats['orphanedTagReferenceKeys']as$tagKey =>$orphanedCacheKeys) {
475+
// Remove each cache item reference from the tag set.
476+
foreach ($orphanedCacheKeysas$orphanedCacheKey) {
477+
$result =$this->pipeline(function ()use ($removeSetMemberLua,$tagKey,$orphanedCacheKey) {
478+
yield'srem' => [$tagKey,$orphanedCacheKey];
479+
});
480+
if (!$result->valid() ||$result->current() !==1) {
481+
$success =false;
482+
}
483+
}
484+
}
485+
}
486+
return$success;
487+
}
488+
489+
/**
490+
* @TODO Make compression mode flag configurable.
491+
*
492+
* @return bool
493+
*/
494+
publicfunctionprune():bool
495+
{
496+
return$this->pruneOrphanedTags(true);
497+
}
308498
}

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp