Skip to content

Commit 7d7f429

Browse files
icewind1991backportbot[bot]
authored andcommitted
feat: add negative compare-and-delete to imemcache
Signed-off-by: Robin Appelman <robin@icewind.nl>
1 parent 28af501 commit 7d7f429

File tree

7 files changed

+96
-2
lines changed

7 files changed

+96
-2
lines changed

lib/private/Memcache/CADTrait.php

+17
Original file line numberDiff line numberDiff line change
@@ -35,4 +35,21 @@ public function cad($key, $old) {
3535
return false;
3636
}
3737
}
38+
39+
public function ncad(string $key, mixed $old): bool {
40+
//no native cad, emulate with locking
41+
if ($this->add($key . '_lock', true)) {
42+
$value = $this->get($key);
43+
if ($value !== null && $value !== $old) {
44+
$this->remove($key);
45+
$this->remove($key . '_lock');
46+
return true;
47+
} else {
48+
$this->remove($key . '_lock');
49+
return false;
50+
}
51+
} else {
52+
return false;
53+
}
54+
}
3855
}

lib/private/Memcache/LoggerWrapperCache.php

+11
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,17 @@ public function cad($key, $old) {
149149
return $this->wrappedCache->cad($key, $old);
150150
}
151151

152+
/** @inheritDoc */
153+
public function ncad(string $key, mixed $old): bool {
154+
file_put_contents(
155+
$this->logFile,
156+
$this->getNameSpace() . '::ncad::' . $key . "\n",
157+
FILE_APPEND
158+
);
159+
160+
return $this->wrappedCache->cad($key, $old);
161+
}
162+
152163
/** @inheritDoc */
153164
public function setTTL(string $key, int $ttl) {
154165
$this->wrappedCache->setTTL($key, $ttl);

lib/private/Memcache/NullCache.php

+5
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,11 @@ public function cad($key, $old) {
4343
return true;
4444
}
4545

46+
public function ncad(string $key, mixed $old): bool {
47+
return true;
48+
}
49+
50+
4651
public function clear($prefix = '') {
4752
return true;
4853
}

lib/private/Memcache/ProfilerWrapperCache.php

+12
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,18 @@ public function cad($key, $old) {
166166
return $ret;
167167
}
168168

169+
/** @inheritDoc */
170+
public function ncad(string $key, mixed $old): bool {
171+
$start = microtime(true);
172+
$ret = $this->wrappedCache->ncad($key, $old);
173+
$this->data['queries'][] = [
174+
'start' => $start,
175+
'end' => microtime(true),
176+
'op' => $this->getPrefix() . '::ncad::' . $key,
177+
];
178+
return $ret;
179+
}
180+
169181
/** @inheritDoc */
170182
public function setTTL(string $key, int $ttl) {
171183
$this->wrappedCache->setTTL($key, $ttl);

lib/private/Memcache/Redis.php

+10
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,10 @@ class Redis extends Cache implements IMemcacheTTL {
2323
'if redis.call("get", KEYS[1]) == ARGV[1] then return redis.call("del", KEYS[1]) else return 0 end',
2424
'cf0e94b2e9ffc7e04395cf88f7583fc309985910',
2525
],
26+
'ncad' => [
27+
'if redis.call("get", KEYS[1]) ~= ARGV[1] then return redis.call("del", KEYS[1]) else return 0 end',
28+
'75526f8048b13ce94a41b58eee59c664b4990ab2',
29+
],
2630
'caSetTtl' => [
2731
'if redis.call("get", KEYS[1]) == ARGV[1] then redis.call("expire", KEYS[1], ARGV[2]) return 1 else return 0 end',
2832
'fa4acbc946d23ef41d7d3910880b60e6e4972d72',
@@ -164,6 +168,12 @@ public function cad($key, $old) {
164168
return $this->evalLua('cad', [$key], [$old]) > 0;
165169
}
166170

171+
public function ncad(string $key, mixed $old): bool {
172+
$old = self::encodeValue($old);
173+
174+
return $this->evalLua('ncad', [$key], [$old]) > 0;
175+
}
176+
167177
public function setTTL($key, $ttl) {
168178
if ($ttl === 0) {
169179
// having infinite TTL can lead to leaked keys as the prefix changes with version upgrades

lib/public/IMemcache.php

+18-2
Original file line numberDiff line numberDiff line change
@@ -56,21 +56,37 @@ public function dec($key, $step = 1);
5656
/**
5757
* Compare and set
5858
*
59+
* Set $key to $new only if it's current value is $new
60+
*
5961
* @param string $key
6062
* @param mixed $old
6163
* @param mixed $new
62-
* @return bool
64+
* @return bool true if the value was successfully set or false if $key wasn't set to $old
6365
* @since 8.1.0
6466
*/
6567
public function cas($key, $old, $new);
6668

6769
/**
6870
* Compare and delete
6971
*
72+
* Delete $key if the stored value is equal to $old
73+
*
7074
* @param string $key
7175
* @param mixed $old
72-
* @return bool
76+
* @return bool true if the value was successfully deleted or false if $key wasn't set to $old
7377
* @since 8.1.0
7478
*/
7579
public function cad($key, $old);
80+
81+
/**
82+
* Negative compare and delete
83+
*
84+
* Delete $key if the stored value is not equal to $old
85+
*
86+
* @param string $key
87+
* @param mixed $old
88+
* @return bool true if the value was successfully deleted or false if $key was set to $old or is not set
89+
* @since 30.0.0
90+
*/
91+
public function ncad(string $key, mixed $old): bool;
7692
}

tests/lib/Memcache/Cache.php

+23
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,10 @@ public function testCasChanged() {
109109
$this->assertEquals('bar1', $this->instance->get('foo'));
110110
}
111111

112+
public function testCasNotSet() {
113+
$this->assertFalse($this->instance->cas('foo', 'bar', 'asd'));
114+
}
115+
112116
public function testCadNotChanged() {
113117
$this->instance->set('foo', 'bar');
114118
$this->assertTrue($this->instance->cad('foo', 'bar'));
@@ -121,6 +125,25 @@ public function testCadChanged() {
121125
$this->assertTrue($this->instance->hasKey('foo'));
122126
}
123127

128+
public function testCadNotSet() {
129+
$this->assertFalse($this->instance->cad('foo', 'bar'));
130+
}
131+
132+
public function testNcadNotChanged() {
133+
$this->instance->set('foo', 'bar');
134+
$this->assertFalse($this->instance->ncad('foo', 'bar'));
135+
$this->assertTrue($this->instance->hasKey('foo'));
136+
}
137+
138+
public function testNcadChanged() {
139+
$this->instance->set('foo', 'bar1');
140+
$this->assertTrue($this->instance->ncad('foo', 'bar'));
141+
$this->assertFalse($this->instance->hasKey('foo'));
142+
}
143+
144+
public function testNcadNotSet() {
145+
$this->assertFalse($this->instance->ncad('foo', 'bar'));
146+
}
124147

125148
protected function tearDown(): void {
126149
if ($this->instance) {

0 commit comments

Comments
 (0)