From 971b177d27ec43fca1ed6c027971190508bc5063 Mon Sep 17 00:00:00 2001 From: Trevor North Date: Thu, 20 Feb 2020 16:09:42 +0000 Subject: [PATCH] Fix versioned namespace clears 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 concurreny 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. --- src/Symfony/Component/Cache/Traits/AbstractTrait.php | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/Symfony/Component/Cache/Traits/AbstractTrait.php b/src/Symfony/Component/Cache/Traits/AbstractTrait.php index f9a1d8fdafa2..7639424ea78c 100644 --- a/src/Symfony/Component/Cache/Traits/AbstractTrait.php +++ b/src/Symfony/Component/Cache/Traits/AbstractTrait.php @@ -111,9 +111,14 @@ public function hasItem($key) */ 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 @@ public function clear(/*string $prefix = ''*/) $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]);