Skip to content

Commit

Permalink
TASK: Improve flushByTags performance
Browse files Browse the repository at this point in the history
This backports the optimized code from the redis backend of the neos/cache package. (https://github.com/neos/flow-development-collection/blob/8.3/Neos.Cache/Classes/Backend/RedisBackend.php#L297)
  • Loading branch information
daniellienert committed Jul 18, 2023
1 parent 274b445 commit fcdc3d1
Showing 1 changed file with 45 additions and 4 deletions.
49 changes: 45 additions & 4 deletions Classes/RedisBackend.php
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,11 @@ class RedisBackend extends IndependentAbstractBackend implements TaggableBackend

protected static array $loggedErrors = [];

/**
* Redis allows a maximum of 1024 * 1024 parameters, but we use a lower limit to prevent long blocking calls.
*/
protected int $batchSize = 100000;

/**
* @param EnvironmentConfiguration $environmentConfiguration
* @param array $options Configuration options - depends on the actual backend
Expand Down Expand Up @@ -284,11 +289,47 @@ public function flushByTag(string $tag): int
*/
public function flushByTags(array $tags): int
{
$flushedTags = 0;
foreach ($tags as $tag) {
$flushedTags += $this->flushByTag($tag);
if ($this->isFrozen()) {
throw new \RuntimeException(sprintf('Cannot add or modify cache entry because the backend of cache "%s" is frozen.', $this->cacheIdentifier), 1647642328);
}
return $flushedTags;

// language=lua
$script = "
local total_entries = 0
local num_arg = #ARGV
for i = 1, num_arg do
local entries = redis.call('SMEMBERS', KEYS[i])
for k1,entryIdentifier in ipairs(entries) do
redis.call('DEL', ARGV[i]..'entry:'..entryIdentifier)
local tags = redis.call('SMEMBERS', ARGV[i]..'tags:'..entryIdentifier)
for k2,tagName in ipairs(tags) do
redis.call('SREM', ARGV[i]..'tag:'..tagName, entryIdentifier)
end
redis.call('DEL', ARGV[i]..'tags:'..entryIdentifier)
end
redis.call('DEL', KEYS[i])
total_entries = total_entries + #entries
end
return total_entries
";

$flushedEntriesTotal = 0;

// Flush tags in batches
for ($i = 0, $iMax = count($tags); $i < $iMax; $i += $this->batchSize) {
$tagList = array_slice($tags, $i, $this->batchSize);
$keys = array_map(function ($tag) {
return $this->getPrefixedIdentifier('tag:' . $tag);
}, $tagList);
$values = array_fill(0, count($keys), $this->getPrefixedIdentifier(''));

$flushedEntries = $this->client->eval($script, count($keys), ...$keys, ...$values);
$flushedEntriesTotal = is_int($flushedEntries) ? $flushedEntries : 0;
}

return $flushedEntriesTotal;
}

/**
Expand Down

0 comments on commit fcdc3d1

Please sign in to comment.