Skip to content

Commit 44a2ca9

Browse files
committed
Support KEEPTTL via ValueOperations.
Closes spring-projects#2084
1 parent 9eadebd commit 44a2ca9

File tree

6 files changed

+242
-0
lines changed

6 files changed

+242
-0
lines changed

Diff for: src/main/java/org/springframework/data/redis/core/DefaultReactiveValueOperations.java

+19
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,15 @@ public Mono<Boolean> set(K key, V value, Duration timeout) {
7777
stringCommands.set(rawKey(key), rawValue(value), Expiration.from(timeout), SetOption.UPSERT));
7878
}
7979

80+
@Override
81+
public Mono<Boolean> set(K key, V value, boolean keepTtl) {
82+
83+
Assert.notNull(key, "Key must not be null");
84+
85+
Expiration expiration = keepTtl ? Expiration.keepTtl() : Expiration.persistent();
86+
return createMono(stringCommands -> stringCommands.set(rawKey(key), rawValue(value), expiration, SetOption.UPSERT));
87+
}
88+
8089
@Override
8190
public Mono<Boolean> setIfAbsent(K key, V value) {
8291

@@ -115,6 +124,16 @@ public Mono<Boolean> setIfPresent(K key, V value, Duration timeout) {
115124
stringCommands.set(rawKey(key), rawValue(value), Expiration.from(timeout), SetOption.SET_IF_PRESENT));
116125
}
117126

127+
@Override
128+
public Mono<Boolean> setIfPresent(K key, V value, boolean keepTtl) {
129+
130+
Assert.notNull(key, "Key must not be null");
131+
132+
Expiration expiration = keepTtl ? Expiration.keepTtl() : Expiration.persistent();
133+
return createMono(
134+
stringCommands -> stringCommands.set(rawKey(key), rawValue(value), expiration, SetOption.SET_IF_PRESENT));
135+
}
136+
118137
@Override
119138
public Mono<Boolean> multiSet(Map<? extends K, ? extends V> map) {
120139

Diff for: src/main/java/org/springframework/data/redis/core/DefaultValueOperations.java

+20
Original file line numberDiff line numberDiff line change
@@ -281,6 +281,16 @@ private boolean failsafeInvokePsetEx(RedisConnection connection) {
281281
});
282282
}
283283

284+
@Override
285+
public void set(K key, V value, boolean keepTtl) {
286+
287+
byte[] rawKey = rawKey(key);
288+
byte[] rawValue = rawValue(value);
289+
290+
Expiration expiration = keepTtl ? Expiration.keepTtl() : Expiration.persistent();
291+
execute(connection -> connection.set(rawKey, rawValue, expiration, SetOption.upsert()));
292+
}
293+
284294
@Override
285295
public Boolean setIfAbsent(K key, V value) {
286296

@@ -320,6 +330,16 @@ public Boolean setIfPresent(K key, V value, long timeout, TimeUnit unit) {
320330
return execute(connection -> connection.set(rawKey, rawValue, expiration, SetOption.ifPresent()));
321331
}
322332

333+
@Override
334+
public Boolean setIfPresent(K key, V value, boolean keepTtl) {
335+
336+
byte[] rawKey = rawKey(key);
337+
byte[] rawValue = rawValue(value);
338+
339+
Expiration expiration = keepTtl ? Expiration.keepTtl() : Expiration.persistent();
340+
return execute(connection -> connection.set(rawKey, rawValue, expiration, SetOption.ifPresent()));
341+
}
342+
323343
@Override
324344
public void set(K key, V value, long offset) {
325345

Diff for: src/main/java/org/springframework/data/redis/core/ReactiveValueOperations.java

+20
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,16 @@ public interface ReactiveValueOperations<K, V> {
5858
*/
5959
Mono<Boolean> set(K key, V value, Duration timeout);
6060

61+
/**
62+
* Set {@code value} for {@code key}.
63+
*
64+
* @param key must not be {@literal null}.
65+
* @param value
66+
* @param keepTtl whether to retain TTL associated with the key.
67+
* @see <a href="https://redis.io/commands/set">Redis Documentation: SET</a>
68+
*/
69+
Mono<Boolean> set(K key, V value, boolean keepTtl);
70+
6171
/**
6272
* Set {@code key} to hold the string {@code value} if {@code key} is absent.
6373
*
@@ -98,6 +108,16 @@ public interface ReactiveValueOperations<K, V> {
98108
*/
99109
Mono<Boolean> setIfPresent(K key, V value, Duration timeout);
100110

111+
/**
112+
* Set {@code key} to hold the string {@code value} if {@code key} is present.
113+
*
114+
* @param key must not be {@literal null}.
115+
* @param value
116+
* @param keepTtl whether to retain TTL associated with the key.
117+
* @see <a href="https://redis.io/commands/set">Redis Documentation: SET</a>
118+
*/
119+
Mono<Boolean> setIfPresent(K key, V value, boolean keepTtl);
120+
101121
/**
102122
* Set multiple keys to multiple values using key-value pairs provided in {@code tuple}.
103123
*

Diff for: src/main/java/org/springframework/data/redis/core/ValueOperations.java

+23
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,16 @@ default void set(K key, V value, Duration timeout) {
7676
}
7777
}
7878

79+
/**
80+
* Set {@code value} for {@code key}.
81+
*
82+
* @param key must not be {@literal null}.
83+
* @param value must not be {@literal null}.
84+
* @param keepTtl whether to retain TTL associated with the key.
85+
* @see <a href="https://redis.io/commands/set">Redis Documentation: SET</a>
86+
*/
87+
void set(K key, V value, boolean keepTtl);
88+
7989
/**
8090
* Set {@code key} to hold the string {@code value} if {@code key} is absent.
8191
*
@@ -175,6 +185,19 @@ default Boolean setIfPresent(K key, V value, Duration timeout) {
175185
return setIfPresent(key, value, timeout.getSeconds(), TimeUnit.SECONDS);
176186
}
177187

188+
/**
189+
* Set {@code key} to hold the string {@code value} if {@code key} is present.
190+
*
191+
* @param key must not be {@literal null}.
192+
* @param value must not be {@literal null}.
193+
* @param keepTtl whether to retain TTL associated with the key.
194+
* @return command result indicating if the key has been set.
195+
* @throws IllegalArgumentException if either {@code key} or {@code value} is not present.
196+
* @see <a href="https://redis.io/commands/set">Redis Documentation: SET</a>
197+
*/
198+
@Nullable
199+
Boolean setIfPresent(K key, V value, boolean keepTtl);
200+
178201
/**
179202
* Set multiple keys to multiple values using key-value pairs provided in {@code tuple}.
180203
*

Diff for: src/test/java/org/springframework/data/redis/core/DefaultReactiveValueOperationsIntegrationTests.java

+96
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,50 @@ void set() {
9898
valueOperations.get(key).as(StepVerifier::create).expectNext(value).verifyComplete();
9999
}
100100

101+
@ParameterizedRedisTest // GH-2084
102+
void setWithKeepTtl() {
103+
104+
K key = keyFactory.instance();
105+
V value1 = valueFactory.instance();
106+
V value2 = valueFactory.instance();
107+
108+
valueOperations.set(key, value1, Duration.ofMillis(5500)).as(StepVerifier::create) //
109+
.expectNext(true) //
110+
.verifyComplete();
111+
valueOperations.set(key, value2, true).as(StepVerifier::create) //
112+
.expectNext(true) //
113+
.verifyComplete();
114+
115+
valueOperations.get(key).as(StepVerifier::create) //
116+
.expectNext(value2) //
117+
.verifyComplete();
118+
redisTemplate.getExpire(key).as(StepVerifier::create) //
119+
.assertNext(actual -> assertThat(actual).isBetween(Duration.ofMillis(1), Duration.ofSeconds(6))) //
120+
.verifyComplete();
121+
}
122+
123+
@ParameterizedRedisTest // GH-2084
124+
void setWithoutKeepTtl() {
125+
126+
K key = keyFactory.instance();
127+
V value1 = valueFactory.instance();
128+
V value2 = valueFactory.instance();
129+
130+
valueOperations.set(key, value1, Duration.ofMillis(5500)).as(StepVerifier::create) //
131+
.expectNext(true) //
132+
.verifyComplete();
133+
valueOperations.set(key, value2, false).as(StepVerifier::create) //
134+
.expectNext(true) //
135+
.verifyComplete();
136+
137+
valueOperations.get(key).as(StepVerifier::create) //
138+
.expectNext(value2) //
139+
.verifyComplete();
140+
redisTemplate.getExpire(key).as(StepVerifier::create) //
141+
.assertNext(actual -> assertThat(actual).isZero()) //
142+
.verifyComplete();
143+
}
144+
101145
@ParameterizedRedisTest // DATAREDIS-602
102146
void setWithExpiry() {
103147

@@ -186,6 +230,58 @@ void setIfPresentWithExpiry() {
186230
}).verifyComplete();
187231
}
188232

233+
@ParameterizedRedisTest // GH-2084
234+
void setIfPresentWithKeepTtl() {
235+
236+
K key = keyFactory.instance();
237+
V value1 = valueFactory.instance();
238+
V value2 = valueFactory.instance();
239+
240+
valueOperations.setIfPresent(key, value1, true).as(StepVerifier::create) //
241+
.expectNext(false) //
242+
.verifyComplete();
243+
valueOperations.set(key, value1, Duration.ofMillis(5500)).as(StepVerifier::create) //
244+
.expectNext(true) //
245+
.verifyComplete();
246+
247+
valueOperations.setIfPresent(key, value2, true).as(StepVerifier::create) //
248+
.expectNext(true) //
249+
.verifyComplete();
250+
251+
valueOperations.get(key).as(StepVerifier::create) //
252+
.expectNext(value2) //
253+
.verifyComplete();
254+
redisTemplate.getExpire(key).as(StepVerifier::create) //
255+
.assertNext(actual -> assertThat(actual).isBetween(Duration.ofMillis(1), Duration.ofSeconds(6))) //
256+
.verifyComplete();
257+
}
258+
259+
@ParameterizedRedisTest // GH-2084
260+
void setIfPresentWithoutKeepTtl() {
261+
262+
K key = keyFactory.instance();
263+
V value1 = valueFactory.instance();
264+
V value2 = valueFactory.instance();
265+
266+
valueOperations.setIfPresent(key, value1, false).as(StepVerifier::create) //
267+
.expectNext(false) //
268+
.verifyComplete();
269+
valueOperations.set(key, value1, Duration.ofMillis(5500)).as(StepVerifier::create) //
270+
.expectNext(true) //
271+
.verifyComplete();
272+
273+
valueOperations.setIfPresent(key, value2, false).as(StepVerifier::create) //
274+
.expectNext(true) //
275+
.verifyComplete();
276+
277+
valueOperations.get(key).as(StepVerifier::create) //
278+
.expectNext(value2) //
279+
.verifyComplete();
280+
redisTemplate.getExpire(key).as(StepVerifier::create) //
281+
.assertNext(actual -> assertThat(actual).isZero()) //
282+
.verifyComplete();
283+
}
284+
189285
@ParameterizedRedisTest // DATAREDIS-602
190286
void multiSet() {
191287

Diff for: src/test/java/org/springframework/data/redis/core/DefaultValueOperationsIntegrationTests.java

+64
Original file line numberDiff line numberDiff line change
@@ -316,6 +316,37 @@ void testSetWithExpirationWithTimeUnitMilliseconds() {
316316
await().atMost(Duration.ofMillis(500L)).until(() -> !redisTemplate.hasKey(key));
317317
}
318318

319+
@ParameterizedRedisTest // GH-2084
320+
void testSetWithKeepTtl() {
321+
322+
K key = keyFactory.instance();
323+
V value1 = valueFactory.instance();
324+
V value2 = valueFactory.instance();
325+
326+
valueOps.set(key, value1, Duration.ofMillis(5500));
327+
valueOps.set(key, value2, true);
328+
329+
Long expire = redisTemplate.getExpire(key, TimeUnit.MILLISECONDS);
330+
331+
assertThat(valueOps.get(key)).isEqualTo(value2);
332+
assertThat(expire).isLessThan(TimeUnit.SECONDS.toMillis(6));
333+
assertThat(expire).isGreaterThan(TimeUnit.MILLISECONDS.toMillis(1));
334+
}
335+
336+
@ParameterizedRedisTest // GH-2084
337+
void testSetWithoutKeepTtl() {
338+
339+
K key = keyFactory.instance();
340+
V value1 = valueFactory.instance();
341+
V value2 = valueFactory.instance();
342+
343+
valueOps.set(key, value1, Duration.ofMillis(5500));
344+
valueOps.set(key, value2, false);
345+
346+
assertThat(valueOps.get(key)).isEqualTo(value2);
347+
assertThat(redisTemplate.getExpire(key)).isEqualTo(-1);
348+
}
349+
319350
@ParameterizedRedisTest
320351
void testAppend() {
321352

@@ -483,6 +514,39 @@ void testSetIfPresentWithExpirationPX() {
483514
assertThat(valueOps.get(key)).isEqualTo(value2);
484515
}
485516

517+
@ParameterizedRedisTest // GH-2084
518+
void testSetIfPresentWithKeepTtl() {
519+
520+
K key = keyFactory.instance();
521+
V value1 = valueFactory.instance();
522+
V value2 = valueFactory.instance();
523+
524+
assertThat(valueOps.setIfPresent(key, value1, true)).isFalse();
525+
valueOps.set(key, value1, Duration.ofMillis(5500));
526+
527+
Long expire = redisTemplate.getExpire(key, TimeUnit.MILLISECONDS);
528+
529+
assertThat(valueOps.setIfPresent(key, value2, true)).isTrue();
530+
assertThat(valueOps.get(key)).isEqualTo(value2);
531+
assertThat(expire).isLessThan(TimeUnit.SECONDS.toMillis(6));
532+
assertThat(expire).isGreaterThan(TimeUnit.MILLISECONDS.toMillis(1));
533+
}
534+
535+
@ParameterizedRedisTest // GH-2084
536+
void testSetIfPresentWithoutKeepTtl() {
537+
538+
K key = keyFactory.instance();
539+
V value1 = valueFactory.instance();
540+
V value2 = valueFactory.instance();
541+
542+
assertThat(valueOps.setIfPresent(key, value1, false)).isFalse();
543+
valueOps.set(key, value1, Duration.ofMillis(5500));
544+
545+
assertThat(valueOps.setIfPresent(key, value2, false)).isTrue();
546+
assertThat(valueOps.get(key)).isEqualTo(value2);
547+
assertThat(redisTemplate.getExpire(key)).isEqualTo(-1);
548+
}
549+
486550
@ParameterizedRedisTest
487551
void testSize() {
488552

0 commit comments

Comments
 (0)