Skip to content

Commit 5315fa7

Browse files
committed
[OPTIONAL] Implement retrieve(key) and retrieve(key, Supplier<CompletableFuture<T>> Cache operations without Jedis support.
Closes #2650
1 parent a047d0c commit 5315fa7

File tree

4 files changed

+77
-4
lines changed

4 files changed

+77
-4
lines changed

Diff for: src/main/java/org/springframework/data/redis/cache/DefaultRedisCacheWriter.java

+7
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,11 @@ public byte[] get(String name, byte[] key, @Nullable Duration ttl) {
137137
return result;
138138
}
139139

140+
@Override
141+
public boolean isRetrieveSupported() {
142+
return isReactive();
143+
}
144+
140145
@Override
141146
public CompletableFuture<byte[]> retrieve(String name, byte[] key, @Nullable Duration ttl) {
142147

@@ -165,6 +170,8 @@ private BiFunction<byte[], Duration, CompletableFuture<byte[]>> nonBlockingRetri
165170
return isReactive() ? reactiveRetrieveFunction(cacheName) : asyncRetrieveFunction(cacheName);
166171
}
167172

173+
// TODO: Possibly remove if we rely on the default Cache.retrieve(..) behavior
174+
// after assessing RedisCacheWriter.isRetrieveSupported().
168175
// Function applied for Cache.retrieve(key) when a non-reactive Redis driver is used, such as Jedis.
169176
private BiFunction<byte[], Duration, CompletableFuture<byte[]>> asyncRetrieveFunction(String cacheName) {
170177

Diff for: src/main/java/org/springframework/data/redis/cache/RedisCache.java

+13-4
Original file line numberDiff line numberDiff line change
@@ -293,18 +293,27 @@ protected Object preProcessCacheValue(@Nullable Object value) {
293293

294294
@Override
295295
public CompletableFuture<?> retrieve(Object key) {
296-
return retrieveValue(key).thenApply(this::nullSafeDeserializedStoreValue);
296+
297+
if (getCacheWriter().isRetrieveSupported()) {
298+
return retrieveValue(key).thenApply(this::nullSafeDeserializedStoreValue);
299+
}
300+
301+
return super.retrieve(key);
297302
}
298303

299304
@Override
300305
@SuppressWarnings("unchecked")
301306
public <T> CompletableFuture<T> retrieve(Object key, Supplier<CompletableFuture<T>> valueLoader) {
302307

303-
return retrieveValue(key)
308+
if (getCacheWriter().isRetrieveSupported()) {
309+
return retrieveValue(key)
304310
.thenApply(this::nullSafeDeserializedStoreValue)
305311
.thenCompose(cachedValue -> cachedValue != null
306-
? CompletableFuture.completedFuture((T) cachedValue)
307-
: valueLoader.get());
312+
? CompletableFuture.completedFuture((T) cachedValue)
313+
: valueLoader.get());
314+
}
315+
316+
return super.retrieve(key, valueLoader);
308317
}
309318

310319
CompletableFuture<byte[]> retrieveValue(Object key) {

Diff for: src/main/java/org/springframework/data/redis/cache/RedisCacheWriter.java

+17
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717

1818
import java.time.Duration;
1919
import java.util.concurrent.CompletableFuture;
20+
import java.util.function.Supplier;
2021

2122
import org.springframework.data.redis.connection.RedisConnectionFactory;
2223
import org.springframework.lang.Nullable;
@@ -136,6 +137,22 @@ default byte[] get(String name, byte[] key, @Nullable Duration ttl) {
136137
return get(name, key);
137138
}
138139

140+
/**
141+
* Determines whether the asynchronous {@link #retrieve(String, byte[])}
142+
* and {@link #retrieve(String, byte[], Duration)} cache operations are supported by the implementation.
143+
* <p>
144+
* The main factor for whether the {@literal retrieve} operation can be supported will primarily be determined by
145+
* the Redis driver in use at runtime.
146+
* <p>
147+
* Returns {@literal false} by default. This will have an effect of {@link RedisCache#retrieve(Object)}
148+
* and {@link RedisCache#retrieve(Object, Supplier)} throwing an {@link UnsupportedOperationException}.
149+
*
150+
* @return {@literal true} if asynchronous {@literal retrieve} operations are supported by the implementation.
151+
*/
152+
default boolean isRetrieveSupported() {
153+
return false;
154+
}
155+
139156
/**
140157
* Returns the {@link CompletableFuture value} to which the {@link RedisCache} maps the given {@link byte[] key}.
141158
* <p>

Diff for: src/test/java/org/springframework/data/redis/cache/RedisCacheTests.java

+40
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
package org.springframework.data.redis.cache;
1717

1818
import static org.assertj.core.api.Assertions.assertThat;
19+
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
1920
import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
2021
import static org.assertj.core.api.Assumptions.assumeThat;
2122
import static org.awaitility.Awaitility.await;
@@ -52,9 +53,12 @@
5253
import org.springframework.data.redis.connection.RedisConnection;
5354
import org.springframework.data.redis.connection.RedisConnectionFactory;
5455
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
56+
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
5557
import org.springframework.data.redis.serializer.RedisSerializationContext.SerializationPair;
5658
import org.springframework.data.redis.serializer.RedisSerializer;
5759
import org.springframework.data.redis.test.condition.EnabledOnCommand;
60+
import org.springframework.data.redis.test.condition.EnabledOnRedisDriver;
61+
import org.springframework.data.redis.test.condition.RedisDriver;
5862
import org.springframework.data.redis.test.extension.parametrized.MethodSource;
5963
import org.springframework.data.redis.test.extension.parametrized.ParameterizedRedisTest;
6064
import org.springframework.lang.Nullable;
@@ -568,10 +572,37 @@ void cacheGetWithTimeToIdleExpirationAfterEntryExpiresShouldReturnNull() {
568572
assertThat(cache.get(this.cacheKey, Person.class)).isNull();
569573
}
570574

575+
@ParameterizedRedisTest
576+
void retrieveCacheValueUsingJedis() {
577+
578+
// TODO: Is there a better way to do this? @EnableOnRedisDriver(RedisDriver.JEDIS) does not work!
579+
assumeThat(this.connectionFactory instanceof JedisConnectionFactory).isTrue();
580+
581+
assertThatExceptionOfType(UnsupportedOperationException.class)
582+
.isThrownBy(() -> this.cache.retrieve(this.binaryCacheKey))
583+
.withMessageContaining(RedisCache.class.getName())
584+
.withNoCause();
585+
}
586+
587+
@ParameterizedRedisTest
588+
void retrieveCacheValueWithLoaderUsingJedis() {
589+
590+
// TODO: Is there a better way to do this? @EnableOnRedisDriver(RedisDriver.JEDIS) does not work!
591+
assumeThat(this.connectionFactory instanceof JedisConnectionFactory).isTrue();
592+
593+
assertThatExceptionOfType(UnsupportedOperationException.class)
594+
.isThrownBy(() -> this.cache.retrieve(this.binaryCacheKey, () -> CompletableFuture.completedFuture("TEST")))
595+
.withMessageContaining(RedisCache.class.getName())
596+
.withNoCause();
597+
}
598+
571599
@ParameterizedRedisTest // GH-2650
572600
@SuppressWarnings("unchecked")
573601
void retrieveReturnsCachedValue() throws Exception {
574602

603+
// TODO: Is there a better way to do this? @EnableOnRedisDriver(RedisDriver.LETTUCE) does not work!
604+
assumeThat(this.connectionFactory instanceof LettuceConnectionFactory).isTrue();
605+
575606
doWithConnection(connection -> connection.stringCommands().set(this.binaryCacheKey, this.binarySample));
576607

577608
RedisCache cache = new RedisCache("cache", usingLockingRedisCacheWriter(), usingRedisCacheConfiguration());
@@ -587,6 +618,9 @@ void retrieveReturnsCachedValue() throws Exception {
587618
@SuppressWarnings("unchecked")
588619
void retrieveReturnsCachedValueWhenLockIsReleased() throws Exception {
589620

621+
// TODO: Is there a better way to do this? @EnableOnRedisDriver(RedisDriver.LETTUCE) does not work!
622+
assumeThat(this.connectionFactory instanceof LettuceConnectionFactory).isTrue();
623+
590624
String mockValue = "MockValue";
591625
String testValue = "TestValue";
592626

@@ -618,6 +652,9 @@ void retrieveReturnsCachedValueWhenLockIsReleased() throws Exception {
618652
@ParameterizedRedisTest // GH-2650
619653
void retrieveReturnsLoadedValue() throws Exception {
620654

655+
// TODO: Is there a better way to do this? @EnableOnRedisDriver(RedisDriver.LETTUCE) does not work!
656+
assumeThat(this.connectionFactory instanceof LettuceConnectionFactory).isTrue();
657+
621658
RedisCache cache = new RedisCache("cache", usingLockingRedisCacheWriter(), usingRedisCacheConfiguration());
622659

623660
AtomicBoolean loaded = new AtomicBoolean(false);
@@ -646,6 +683,9 @@ void retrieveReturnsLoadedValue() throws Exception {
646683
@ParameterizedRedisTest // GH-2650
647684
void retrieveReturnsNull() throws Exception {
648685

686+
// TODO: Is there a better way to do this? @EnableOnRedisDriver(RedisDriver.LETTUCE) does not work!
687+
assumeThat(this.connectionFactory instanceof LettuceConnectionFactory).isTrue();
688+
649689
doWithConnection(connection -> connection.stringCommands().set(this.binaryCacheKey, this.binaryNullValue));
650690

651691
RedisCache cache = new RedisCache("cache", usingLockingRedisCacheWriter(), usingRedisCacheConfiguration());

0 commit comments

Comments
 (0)