mirror of
https://github.com/nextcloud/server.git
synced 2026-02-18 18:28:50 -05:00
feat: add negative compare-and-delete to imemcache
Signed-off-by: Robin Appelman <robin@icewind.nl>
This commit is contained in:
parent
307608b26c
commit
20dbb6c7e8
7 changed files with 96 additions and 2 deletions
|
|
@ -35,4 +35,21 @@ trait CADTrait {
|
|||
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;
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -149,6 +149,17 @@ class LoggerWrapperCache extends Cache implements IMemcacheTTL {
|
|||
return $this->wrappedCache->cad($key, $old);
|
||||
}
|
||||
|
||||
/** @inheritDoc */
|
||||
public function ncad(string $key, mixed $old): bool {
|
||||
file_put_contents(
|
||||
$this->logFile,
|
||||
$this->getNameSpace() . '::ncad::' . $key . "\n",
|
||||
FILE_APPEND
|
||||
);
|
||||
|
||||
return $this->wrappedCache->cad($key, $old);
|
||||
}
|
||||
|
||||
/** @inheritDoc */
|
||||
public function setTTL(string $key, int $ttl) {
|
||||
$this->wrappedCache->setTTL($key, $ttl);
|
||||
|
|
|
|||
|
|
@ -43,6 +43,11 @@ class NullCache extends Cache implements \OCP\IMemcache {
|
|||
return true;
|
||||
}
|
||||
|
||||
public function ncad(string $key, mixed $old): bool {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
public function clear($prefix = '') {
|
||||
return true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -166,6 +166,18 @@ class ProfilerWrapperCache extends AbstractDataCollector implements IMemcacheTTL
|
|||
return $ret;
|
||||
}
|
||||
|
||||
/** @inheritDoc */
|
||||
public function ncad(string $key, mixed $old): bool {
|
||||
$start = microtime(true);
|
||||
$ret = $this->wrappedCache->ncad($key, $old);
|
||||
$this->data['queries'][] = [
|
||||
'start' => $start,
|
||||
'end' => microtime(true),
|
||||
'op' => $this->getPrefix() . '::ncad::' . $key,
|
||||
];
|
||||
return $ret;
|
||||
}
|
||||
|
||||
/** @inheritDoc */
|
||||
public function setTTL(string $key, int $ttl) {
|
||||
$this->wrappedCache->setTTL($key, $ttl);
|
||||
|
|
|
|||
|
|
@ -23,6 +23,10 @@ class Redis extends Cache implements IMemcacheTTL {
|
|||
'if redis.call("get", KEYS[1]) == ARGV[1] then return redis.call("del", KEYS[1]) else return 0 end',
|
||||
'cf0e94b2e9ffc7e04395cf88f7583fc309985910',
|
||||
],
|
||||
'ncad' => [
|
||||
'if redis.call("get", KEYS[1]) ~= ARGV[1] then return redis.call("del", KEYS[1]) else return 0 end',
|
||||
'75526f8048b13ce94a41b58eee59c664b4990ab2',
|
||||
],
|
||||
'caSetTtl' => [
|
||||
'if redis.call("get", KEYS[1]) == ARGV[1] then redis.call("expire", KEYS[1], ARGV[2]) return 1 else return 0 end',
|
||||
'fa4acbc946d23ef41d7d3910880b60e6e4972d72',
|
||||
|
|
@ -164,6 +168,12 @@ class Redis extends Cache implements IMemcacheTTL {
|
|||
return $this->evalLua('cad', [$key], [$old]) > 0;
|
||||
}
|
||||
|
||||
public function ncad(string $key, mixed $old): bool {
|
||||
$old = self::encodeValue($old);
|
||||
|
||||
return $this->evalLua('ncad', [$key], [$old]) > 0;
|
||||
}
|
||||
|
||||
public function setTTL($key, $ttl) {
|
||||
if ($ttl === 0) {
|
||||
// having infinite TTL can lead to leaked keys as the prefix changes with version upgrades
|
||||
|
|
|
|||
|
|
@ -56,10 +56,12 @@ interface IMemcache extends ICache {
|
|||
/**
|
||||
* Compare and set
|
||||
*
|
||||
* Set $key to $new only if it's current value is $new
|
||||
*
|
||||
* @param string $key
|
||||
* @param mixed $old
|
||||
* @param mixed $new
|
||||
* @return bool
|
||||
* @return bool true if the value was successfully set or false if $key wasn't set to $old
|
||||
* @since 8.1.0
|
||||
*/
|
||||
public function cas($key, $old, $new);
|
||||
|
|
@ -67,10 +69,24 @@ interface IMemcache extends ICache {
|
|||
/**
|
||||
* Compare and delete
|
||||
*
|
||||
* Delete $key if the stored value is equal to $old
|
||||
*
|
||||
* @param string $key
|
||||
* @param mixed $old
|
||||
* @return bool
|
||||
* @return bool true if the value was successfully deleted or false if $key wasn't set to $old
|
||||
* @since 8.1.0
|
||||
*/
|
||||
public function cad($key, $old);
|
||||
|
||||
/**
|
||||
* Negative compare and delete
|
||||
*
|
||||
* Delete $key if the stored value is not equal to $old
|
||||
*
|
||||
* @param string $key
|
||||
* @param mixed $old
|
||||
* @return bool true if the value was successfully deleted or false if $key was set to $old or is not set
|
||||
* @since 30.0.0
|
||||
*/
|
||||
public function ncad(string $key, mixed $old): bool;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -109,6 +109,10 @@ abstract class Cache extends \Test\Cache\TestCache {
|
|||
$this->assertEquals('bar1', $this->instance->get('foo'));
|
||||
}
|
||||
|
||||
public function testCasNotSet() {
|
||||
$this->assertFalse($this->instance->cas('foo', 'bar', 'asd'));
|
||||
}
|
||||
|
||||
public function testCadNotChanged() {
|
||||
$this->instance->set('foo', 'bar');
|
||||
$this->assertTrue($this->instance->cad('foo', 'bar'));
|
||||
|
|
@ -121,6 +125,25 @@ abstract class Cache extends \Test\Cache\TestCache {
|
|||
$this->assertTrue($this->instance->hasKey('foo'));
|
||||
}
|
||||
|
||||
public function testCadNotSet() {
|
||||
$this->assertFalse($this->instance->cad('foo', 'bar'));
|
||||
}
|
||||
|
||||
public function testNcadNotChanged() {
|
||||
$this->instance->set('foo', 'bar');
|
||||
$this->assertFalse($this->instance->ncad('foo', 'bar'));
|
||||
$this->assertTrue($this->instance->hasKey('foo'));
|
||||
}
|
||||
|
||||
public function testNcadChanged() {
|
||||
$this->instance->set('foo', 'bar1');
|
||||
$this->assertTrue($this->instance->ncad('foo', 'bar'));
|
||||
$this->assertFalse($this->instance->hasKey('foo'));
|
||||
}
|
||||
|
||||
public function testNcadNotSet() {
|
||||
$this->assertFalse($this->instance->ncad('foo', 'bar'));
|
||||
}
|
||||
|
||||
protected function tearDown(): void {
|
||||
if ($this->instance) {
|
||||
|
|
|
|||
Loading…
Reference in a new issue