bug #35803 [Cache] Fix versioned namespace atomic clears (trvrnrth)

This PR was merged into the 4.4 branch.

Discussion
----------

[Cache] Fix versioned namespace atomic clears

| Q             | A
| ------------- | ---
| Branch?       | 4.4
| Bug fix?      | yes
| New feature?  | no
| Deprecations? | no
| License       | MIT

When using namespace versioning to achieve atomic cache clears, only delete cache keys matching the old/current version.

This resolves tag inconsistency issues whereby the process running the clear would delete keys set against the new version by more recently spawned concurrent processes. Most seriously this could result in newly set data keys remaining, but with empty associated tag sets meaning the invalidation via tags was no longer possible.

Clearing specific prefixes is not supported when using versioned namespaces as it is desirable to clear all old keys as they will no longer be used and would otherwise eventually fill cache memory.

Commits
-------

971b177d27 Fix versioned namespace clears
This commit is contained in:
Nicolas Grekas 2020-02-24 15:49:35 +01:00
commit c0caef1708
1 changed files with 10 additions and 2 deletions

View File

@ -111,9 +111,14 @@ trait AbstractTrait
*/
public function clear(/*string $prefix = ''*/)
{
$prefix = 0 < \func_num_args() ? (string) func_get_arg(0) : '';
$this->deferred = [];
if ($cleared = $this->versioningIsEnabled) {
if ('' === $namespaceVersionToClear = $this->namespaceVersion) {
foreach ($this->doFetch([static::NS_SEPARATOR.$this->namespace]) as $v) {
$namespaceVersionToClear = $v;
}
}
$namespaceToClear = $this->namespace.$namespaceVersionToClear;
$namespaceVersion = substr_replace(base64_encode(pack('V', mt_rand())), static::NS_SEPARATOR, 5);
try {
$cleared = $this->doSave([static::NS_SEPARATOR.$this->namespace => $namespaceVersion], 0);
@ -124,10 +129,13 @@ trait AbstractTrait
$this->namespaceVersion = $namespaceVersion;
$this->ids = [];
}
} else {
$prefix = 0 < \func_num_args() ? (string) func_get_arg(0) : '';
$namespaceToClear = $this->namespace.$prefix;
}
try {
return $this->doClear($this->namespace.$prefix) || $cleared;
return $this->doClear($namespaceToClear) || $cleared;
} catch (\Exception $e) {
CacheItem::log($this->logger, 'Failed to clear the cache: '.$e->getMessage(), ['exception' => $e]);