aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--lib/private/Memcache/CADTrait.php17
-rw-r--r--lib/private/Memcache/LoggerWrapperCache.php11
-rw-r--r--lib/private/Memcache/NullCache.php5
-rw-r--r--lib/private/Memcache/ProfilerWrapperCache.php12
-rw-r--r--lib/private/Memcache/Redis.php10
-rw-r--r--lib/public/IMemcache.php20
-rw-r--r--tests/lib/Memcache/Cache.php23
7 files changed, 96 insertions, 2 deletions
diff --git a/lib/private/Memcache/CADTrait.php b/lib/private/Memcache/CADTrait.php
index bb010e238dc..3bf94246338 100644
--- a/lib/private/Memcache/CADTrait.php
+++ b/lib/private/Memcache/CADTrait.php
@@ -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;
+ }
+ }
}
diff --git a/lib/private/Memcache/LoggerWrapperCache.php b/lib/private/Memcache/LoggerWrapperCache.php
index 11497e2a5d8..c2a06731910 100644
--- a/lib/private/Memcache/LoggerWrapperCache.php
+++ b/lib/private/Memcache/LoggerWrapperCache.php
@@ -150,6 +150,17 @@ class LoggerWrapperCache extends Cache implements IMemcacheTTL {
}
/** @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);
}
diff --git a/lib/private/Memcache/NullCache.php b/lib/private/Memcache/NullCache.php
index ab5c491913a..b667869bf0d 100644
--- a/lib/private/Memcache/NullCache.php
+++ b/lib/private/Memcache/NullCache.php
@@ -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;
}
diff --git a/lib/private/Memcache/ProfilerWrapperCache.php b/lib/private/Memcache/ProfilerWrapperCache.php
index 84e3d880a0c..a13d647c6a4 100644
--- a/lib/private/Memcache/ProfilerWrapperCache.php
+++ b/lib/private/Memcache/ProfilerWrapperCache.php
@@ -167,6 +167,18 @@ class ProfilerWrapperCache extends AbstractDataCollector implements IMemcacheTTL
}
/** @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);
}
diff --git a/lib/private/Memcache/Redis.php b/lib/private/Memcache/Redis.php
index 87dc86ab10d..714759e8fbf 100644
--- a/lib/private/Memcache/Redis.php
+++ b/lib/private/Memcache/Redis.php
@@ -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
diff --git a/lib/public/IMemcache.php b/lib/public/IMemcache.php
index 6e63884ff45..991af1a8d4f 100644
--- a/lib/public/IMemcache.php
+++ b/lib/public/IMemcache.php
@@ -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;
}
diff --git a/tests/lib/Memcache/Cache.php b/tests/lib/Memcache/Cache.php
index efdaffc94eb..4749e223fd5 100644
--- a/tests/lib/Memcache/Cache.php
+++ b/tests/lib/Memcache/Cache.php
@@ -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) {