From 79ee7fd07c97f7a5e91f05e97e2094673a7b029f Mon Sep 17 00:00:00 2001 From: annemayor Date: Sun, 26 May 2024 23:19:05 +0900 Subject: [PATCH] Add multiple reactive keys exist checker --- .../redis/connection/ReactiveKeyCommands.java | 8 ++++++ .../lettuce/LettuceReactiveKeyCommands.java | 8 ++++++ .../redis/core/ReactiveRedisOperations.java | 10 ++++++++ .../redis/core/ReactiveRedisTemplate.java | 20 +++++++++++++++ ...ceReactiveKeyCommandsIntegrationTests.java | 22 +++++++++++++--- ...ReactiveRedisTemplateIntegrationTests.java | 25 ++++++++++++++++--- 6 files changed, 86 insertions(+), 7 deletions(-) diff --git a/src/main/java/org/springframework/data/redis/connection/ReactiveKeyCommands.java b/src/main/java/org/springframework/data/redis/connection/ReactiveKeyCommands.java index 91b81640c1..a04dff6c4c 100644 --- a/src/main/java/org/springframework/data/redis/connection/ReactiveKeyCommands.java +++ b/src/main/java/org/springframework/data/redis/connection/ReactiveKeyCommands.java @@ -175,6 +175,14 @@ default Mono exists(ByteBuffer key) { return exists(Mono.just(new KeyCommand(key))).next().map(BooleanResponse::getOutput); } + /** + * Determine the number of given {@literal keys} that exist. + * + * @param keys must not be {@literal null} or {@literal empty}. + * @return {@link Mono} emitting {@literal the number of existing keys}. + * @see Redis Documentation: EXISTS + */ + Mono exists(List keys); /** * Determine if given {@literal key} exists. diff --git a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveKeyCommands.java b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveKeyCommands.java index a1371b7856..733f34d6ba 100644 --- a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveKeyCommands.java +++ b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveKeyCommands.java @@ -319,4 +319,12 @@ public Mono idletime(ByteBuffer key) { public Mono refcount(ByteBuffer key) { return connection.execute(cmd -> cmd.objectRefcount(key)).next(); } + + @Override + public Mono exists(List keys) { + Assert.notNull(keys, "Key list must not be null"); + Assert.notEmpty(keys, "Key list must not be empty"); + + return connection.execute(cmd -> cmd.exists(keys.toArray(ByteBuffer[]::new))).next(); + } } diff --git a/src/main/java/org/springframework/data/redis/core/ReactiveRedisOperations.java b/src/main/java/org/springframework/data/redis/core/ReactiveRedisOperations.java index f027c46366..a4ac62a900 100644 --- a/src/main/java/org/springframework/data/redis/core/ReactiveRedisOperations.java +++ b/src/main/java/org/springframework/data/redis/core/ReactiveRedisOperations.java @@ -22,6 +22,7 @@ import java.time.Duration; import java.time.Instant; import java.util.Arrays; +import java.util.Collection; import java.util.Collections; import java.util.List; @@ -402,6 +403,15 @@ default Flux scan() { */ Mono getExpire(K key); + /** + * Get the number of given {@code keys} that exists. + * + * @param keys must not be {@literal null} or {@literal empty}. + * @return the number of existing keys in redis. 0 if there are no existing keys. + * @see Redis Documentation: EXISTS + */ + Mono countExistingKeys(Collection keys); + // ------------------------------------------------------------------------- // Methods dealing with Redis Lua scripts // ------------------------------------------------------------------------- diff --git a/src/main/java/org/springframework/data/redis/core/ReactiveRedisTemplate.java b/src/main/java/org/springframework/data/redis/core/ReactiveRedisTemplate.java index 92792ed81b..b86cf80965 100644 --- a/src/main/java/org/springframework/data/redis/core/ReactiveRedisTemplate.java +++ b/src/main/java/org/springframework/data/redis/core/ReactiveRedisTemplate.java @@ -23,6 +23,7 @@ import java.time.Duration; import java.time.Instant; import java.util.Arrays; +import java.util.Collection; import java.util.List; import java.util.stream.Collectors; @@ -489,6 +490,14 @@ public Mono move(K key, int dbIndex) { return doCreateMono(connection -> connection.keyCommands().move(rawKey(key), dbIndex)); } + @Override + public Mono countExistingKeys(Collection keys) { + Assert.notNull(keys, "Keys must not be null"); + + ByteBuffer[] rawKeys = rawKeys(keys); + return doCreateMono(connection -> connection.keyCommands().exists(Arrays.asList(rawKeys))); + } + // ------------------------------------------------------------------------- // Methods dealing with Redis Lua scripts // ------------------------------------------------------------------------- @@ -672,6 +681,17 @@ private ByteBuffer rawKey(K key) { return getSerializationContext().getKeySerializationPair().getWriter().write(key); } + private ByteBuffer[] rawKeys(Collection keys) { + final ByteBuffer[] rawKeys = new ByteBuffer[keys.size()]; + + int i = 0; + for (K key : keys) { + rawKeys[i++] = rawKey(key); + } + + return rawKeys; + } + @Nullable private K readKey(ByteBuffer buffer) { return getSerializationContext().getKeySerializationPair().getReader().read(buffer); diff --git a/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveKeyCommandsIntegrationTests.java b/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveKeyCommandsIntegrationTests.java index 3376c2727f..c90fcee6ee 100644 --- a/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveKeyCommandsIntegrationTests.java +++ b/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveKeyCommandsIntegrationTests.java @@ -66,6 +66,22 @@ void existsShouldReturnFalseForNonExistingKeys() { connection.keyCommands().exists(KEY_1_BBUFFER).as(StepVerifier::create).expectNext(false).verifyComplete(); } + @ParameterizedRedisTest + void existsKeyReturnsKeyCount() { + nativeCommands.set(KEY_1, "1000"); + nativeCommands.set(KEY_2, "2000"); + nativeCommands.set(KEY_3, "3000"); + + connection.keyCommands().exists(List.of(KEY_1_BBUFFER, KEY_2_BBUFFER, KEY_3_BBUFFER)).as(StepVerifier::create) + .expectNext(3L).verifyComplete(); + } + + @ParameterizedRedisTest + void existsKeyReturnsZeroWhenKeyDoesNotExist() { + connection.keyCommands().exists(List.of(KEY_1_BBUFFER, KEY_2_BBUFFER, KEY_3_BBUFFER)).as(StepVerifier::create) + .expectNext(0L).verifyComplete(); + } + @ParameterizedRedisTest // DATAREDIS-525 void typeShouldReturnTypeCorrectly() { @@ -164,7 +180,7 @@ void renameShouldAlterKeyNameCorrectly() { connection.keyCommands().rename(KEY_1_BBUFFER, KEY_2_BBUFFER).as(StepVerifier::create).expectNext(true) .verifyComplete(); assertThat(nativeCommands.exists(KEY_2)).isEqualTo(1L); - assertThat(nativeCommands.exists(KEY_1)).isEqualTo(0L); + assertThat(nativeCommands.exists(KEY_1)).isZero(); } @ParameterizedRedisTest // DATAREDIS-525 @@ -183,7 +199,7 @@ void renameNXShouldAlterKeyNameCorrectly() { .verifyComplete(); assertThat(nativeCommands.exists(KEY_2)).isEqualTo(1L); - assertThat(nativeCommands.exists(KEY_1)).isEqualTo(0L); + assertThat(nativeCommands.exists(KEY_1)).isZero(); } @ParameterizedRedisTest // DATAREDIS-525 @@ -395,7 +411,7 @@ void shouldMoveToDatabase() { .expectNext(true) // .expectComplete() // .verify(); - assertThat(nativeCommands.exists(KEY_1)).isEqualTo(0L); + assertThat(nativeCommands.exists(KEY_1)).isZero(); } @ParameterizedRedisTest // DATAREDIS-694 diff --git a/src/test/java/org/springframework/data/redis/core/ReactiveRedisTemplateIntegrationTests.java b/src/test/java/org/springframework/data/redis/core/ReactiveRedisTemplateIntegrationTests.java index bd8eb0b141..7f221bc190 100644 --- a/src/test/java/org/springframework/data/redis/core/ReactiveRedisTemplateIntegrationTests.java +++ b/src/test/java/org/springframework/data/redis/core/ReactiveRedisTemplateIntegrationTests.java @@ -24,10 +24,7 @@ import java.time.Duration; import java.time.Instant; -import java.util.Collection; -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; +import java.util.*; import java.util.function.Function; import org.junit.jupiter.api.BeforeEach; @@ -544,4 +541,24 @@ void listenToPatternLaterShouldReceiveChannelMessagesCorrectly() { .thenCancel() // .verify(Duration.ofSeconds(3)); } + + @ParameterizedRedisTest + void countExistingKeysIfValidKeyExists() { + + K key = keyFactory.instance(); + K key2 = keyFactory.instance(); + K key3 = keyFactory.instance(); + + redisTemplate.opsForValue().set(key, valueFactory.instance()).as(StepVerifier::create).expectNext(true).verifyComplete(); + redisTemplate.opsForValue().set(key2, valueFactory.instance()).as(StepVerifier::create).expectNext(true).verifyComplete(); + redisTemplate.opsForValue().set(key3, valueFactory.instance()).as(StepVerifier::create).expectNext(true).verifyComplete(); + + redisTemplate.countExistingKeys(Arrays.asList(key, key2, key3)).as(StepVerifier::create).expectNext(3L).verifyComplete(); + } + + @ParameterizedRedisTest + void countExistingKeysIfNotValidKeyExists() { + K key = keyFactory.instance(); + redisTemplate.countExistingKeys(List.of(key)).as(StepVerifier::create).expectNext(0L).verifyComplete(); + } }