refactor: tidy up non-native CAD/NCAD implementations

Signed-off-by: Josh <josh.t.richards@gmail.com>
This commit is contained in:
Josh 2025-09-12 23:26:53 -04:00 committed by GitHub
parent 8fc8c6cef6
commit 0a05cc26e5
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -7,6 +7,9 @@
*/
namespace OC\Memcache;
/**
* CAD/NCAD implementations for cache backends that lack their own.
*/
trait CADTrait {
abstract public function get($key);
@ -15,42 +18,77 @@ trait CADTrait {
abstract public function add($key, $value, $ttl = 0);
/**
* Compare and delete
* Compare-and-delete.
*
* If $key's current value is equal to $expectedValue, delete $key.
* Note may return false for reasons other than not meeting condition.
*
* Implements CAD using simple locking (only requiring backend add/remove support).
*
* @param string $key
* @param mixed $old
* @return bool
* @param mixed $expectedValue
* @return bool True if key was deleted; False if no deletion occurred
*/
public function cad($key, $old) {
//no native cas, emulate with locking
if ($this->add($key . '_lock', true)) {
if ($this->get($key) === $old) {
$this->remove($key);
$this->remove($key . '_lock');
return true;
} else {
$this->remove($key . '_lock');
return false;
}
public function cad($key, $expectedValue) {
if (!$this->acquireLock($key)) {
return false;
}
$currentValue = $this->get($key);
// Check condition
if ($currentValue === $expectedValue) {
// Matches condition
$this->remove($key);
$this->releaseLock($key);
return true;
} else {
// Fails condition
$this->releaseLock($key);
// TODO: consider throwing if release fails
return false;
}
}
public function ncad(string $key, mixed $old): bool {
//no native cad, emulate with locking
if ($this->add($key . '_lock', true)) {
$value = $this->get($key);
if ($value !== null && $value !== $old) {
$this->remove($key);
$this->remove($key . '_lock');
return true;
} else {
$this->remove($key . '_lock');
return false;
}
/**
* Nonequal-compare-and-delete.
*
* If $key's current value is not equal to $expectedValue, delete $key.
* Note may return false for reasons other than not meeting condition.
*
* Implements NCAD using simple locking (only requiring backend add/remove support).
*
* @param string $key
* @param mixed $expectedValue
* @return bool True if key was deleted; False if no deletion occurred
*/
public function ncad(string $key, mixed $expectedValue): bool {
if (!$this->acquireLock($key)) {
return false;
}
$currentValue = $this->get($key);
// Check condition
if ($currentValue !== null && $currentValue !== $expectedValue) {
// Matches condition
$this->remove($key);
$this->releaseLock($key);
return true;
} else {
// Fails condition
$this->releaseLock($key);
// TODO: consider throwing if release fails
return false;
}
}
//
// Utilities
//
private function acquireLock(string $key): bool {
// TODO: an actual TTL would be clearer
return $this->add($key . '_lock', true);
}
private function releaseLock(string $key): bool {
return $this->remove($key . '_lock');
}
}