Skip to content

Commit 20d8ea6

Browse files
committed
Polish new TTI expiration functionality from review feedback.
Closes spring-projects#2351
1 parent 74ff067 commit 20d8ea6

File tree

10 files changed

+62
-45
lines changed

10 files changed

+62
-45
lines changed

src/main/java/org/springframework/data/redis/cache/DefaultRedisCacheWriter.java

+5
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,11 @@ public void put(String name, byte[] key, byte[] value, @Nullable Duration ttl) {
123123
statistics.incPuts(name);
124124
}
125125

126+
@Override
127+
public byte[] get(String name, byte[] key) {
128+
return get(name, key, null);
129+
}
130+
126131
@Override
127132
public byte[] get(String name, byte[] key, @Nullable Duration ttl) {
128133

src/main/java/org/springframework/data/redis/cache/RedisCache.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -189,7 +189,7 @@ protected <T> T loadCacheValue(Object key, Callable<T> valueLoader) {
189189
@Override
190190
protected Object lookup(Object key) {
191191

192-
byte[] value = getCacheConfiguration().isTtiExpirationEnabled()
192+
byte[] value = getCacheConfiguration().isTimeToIdleEnabled()
193193
? getCacheWriter().get(getName(), createAndConvertCacheKey(key), getTimeToLive(key))
194194
: getCacheWriter().get(getName(), createAndConvertCacheKey(key));
195195

src/main/java/org/springframework/data/redis/cache/RedisCacheConfiguration.java

+20-18
Original file line numberDiff line numberDiff line change
@@ -47,11 +47,11 @@
4747
public class RedisCacheConfiguration {
4848

4949
protected static final boolean DEFAULT_CACHE_NULL_VALUES = true;
50-
protected static final boolean DEFAULT_ENABLE_TTI_EXPIRATION = false;
50+
protected static final boolean DEFAULT_ENABLE_TIME_TO_IDLE_EXPIRATION = false;
5151
protected static final boolean DEFAULT_USE_PREFIX = true;
5252
protected static final boolean DO_NOT_CACHE_NULL_VALUES = false;
5353
protected static final boolean DO_NOT_USE_PREFIX = false;
54-
protected static final boolean ENABLE_IDLE_TIME_EXPIRATION = true;
54+
protected static final boolean USE_TIME_TO_IDLE_EXPIRATION = true;
5555

5656
/**
5757
* Default {@link RedisCacheConfiguration} using the following:
@@ -112,7 +112,7 @@ public static RedisCacheConfiguration defaultCacheConfig(@Nullable ClassLoader c
112112

113113
return new RedisCacheConfiguration(TtlFunction.persistent(),
114114
DEFAULT_CACHE_NULL_VALUES,
115-
DEFAULT_ENABLE_TTI_EXPIRATION,
115+
DEFAULT_ENABLE_TIME_TO_IDLE_EXPIRATION,
116116
DEFAULT_USE_PREFIX,
117117
CacheKeyPrefix.simple(),
118118
SerializationPair.fromSerializer(RedisSerializer.string()),
@@ -121,7 +121,7 @@ public static RedisCacheConfiguration defaultCacheConfig(@Nullable ClassLoader c
121121
}
122122

123123
private final boolean cacheNullValues;
124-
private final boolean enableTtiExpiration;
124+
private final boolean enableTimeToIdle;
125125
private final boolean usePrefix;
126126

127127
private final CacheKeyPrefix keyPrefix;
@@ -134,13 +134,13 @@ public static RedisCacheConfiguration defaultCacheConfig(@Nullable ClassLoader c
134134
private final TtlFunction ttlFunction;
135135

136136
@SuppressWarnings("unchecked")
137-
private RedisCacheConfiguration(TtlFunction ttlFunction, Boolean cacheNullValues, Boolean enableTtiExpiration,
137+
private RedisCacheConfiguration(TtlFunction ttlFunction, Boolean cacheNullValues, Boolean enableTimeToIdle,
138138
Boolean usePrefix, CacheKeyPrefix keyPrefix, SerializationPair<String> keySerializationPair,
139139
SerializationPair<?> valueSerializationPair, ConversionService conversionService) {
140140

141141
this.ttlFunction = ttlFunction;
142142
this.cacheNullValues = cacheNullValues;
143-
this.enableTtiExpiration = enableTtiExpiration;
143+
this.enableTimeToIdle = enableTimeToIdle;
144144
this.usePrefix = usePrefix;
145145
this.keyPrefix = keyPrefix;
146146
this.keySerializationPair = keySerializationPair;
@@ -175,7 +175,7 @@ public RedisCacheConfiguration computePrefixWith(CacheKeyPrefix cacheKeyPrefix)
175175

176176
Assert.notNull(cacheKeyPrefix, "Function used to compute prefix must not be null");
177177

178-
return new RedisCacheConfiguration(getTtlFunction(), getAllowCacheNullValues(), isTtiExpirationEnabled(),
178+
return new RedisCacheConfiguration(getTtlFunction(), getAllowCacheNullValues(), isTimeToIdleEnabled(),
179179
DEFAULT_USE_PREFIX, cacheKeyPrefix, getKeySerializationPair(), getValueSerializationPair(),
180180
getConversionService());
181181
}
@@ -189,7 +189,7 @@ DEFAULT_USE_PREFIX, cacheKeyPrefix, getKeySerializationPair(), getValueSerializa
189189
* @return new {@link RedisCacheConfiguration}.
190190
*/
191191
public RedisCacheConfiguration disableCachingNullValues() {
192-
return new RedisCacheConfiguration(getTtlFunction(), DO_NOT_CACHE_NULL_VALUES, isTtiExpirationEnabled(),
192+
return new RedisCacheConfiguration(getTtlFunction(), DO_NOT_CACHE_NULL_VALUES, isTimeToIdleEnabled(),
193193
usePrefix(), getKeyPrefix(), getKeySerializationPair(), getValueSerializationPair(),
194194
getConversionService());
195195
}
@@ -202,7 +202,7 @@ public RedisCacheConfiguration disableCachingNullValues() {
202202
* @return new {@link RedisCacheConfiguration}.
203203
*/
204204
public RedisCacheConfiguration disableKeyPrefix() {
205-
return new RedisCacheConfiguration(getTtlFunction(), getAllowCacheNullValues(), isTtiExpirationEnabled(),
205+
return new RedisCacheConfiguration(getTtlFunction(), getAllowCacheNullValues(), isTimeToIdleEnabled(),
206206
DO_NOT_USE_PREFIX, getKeyPrefix(), getKeySerializationPair(), getValueSerializationPair(), getConversionService());
207207
}
208208

@@ -221,9 +221,10 @@ public RedisCacheConfiguration disableKeyPrefix() {
221221
*
222222
* @return this {@link RedisCacheConfiguration}.
223223
* @see <a href="https://redis.io/commands/getex/">GETEX</a>
224+
* @since 3.2.0
224225
*/
225-
public RedisCacheConfiguration enableTtiExpiration() {
226-
return new RedisCacheConfiguration(getTtlFunction(), getAllowCacheNullValues(), ENABLE_IDLE_TIME_EXPIRATION,
226+
public RedisCacheConfiguration enableTimeToIdle() {
227+
return new RedisCacheConfiguration(getTtlFunction(), getAllowCacheNullValues(), USE_TIME_TO_IDLE_EXPIRATION,
227228
usePrefix(), getKeyPrefix(), getKeySerializationPair(), getValueSerializationPair(),
228229
getConversionService());
229230
}
@@ -253,7 +254,7 @@ public RedisCacheConfiguration entryTtl(TtlFunction ttlFunction) {
253254

254255
Assert.notNull(ttlFunction, "TtlFunction must not be null");
255256

256-
return new RedisCacheConfiguration(ttlFunction, getAllowCacheNullValues(), isTtiExpirationEnabled(),
257+
return new RedisCacheConfiguration(ttlFunction, getAllowCacheNullValues(), isTimeToIdleEnabled(),
257258
usePrefix(), getKeyPrefix(), getKeySerializationPair(), getValueSerializationPair(),
258259
getConversionService());
259260
}
@@ -268,7 +269,7 @@ public RedisCacheConfiguration serializeKeysWith(SerializationPair<String> keySe
268269

269270
Assert.notNull(keySerializationPair, "KeySerializationPair must not be null");
270271

271-
return new RedisCacheConfiguration(getTtlFunction(), getAllowCacheNullValues(), isTtiExpirationEnabled(),
272+
return new RedisCacheConfiguration(getTtlFunction(), getAllowCacheNullValues(), isTimeToIdleEnabled(),
272273
usePrefix(), getKeyPrefix(), keySerializationPair, getValueSerializationPair(), getConversionService());
273274
}
274275

@@ -282,7 +283,7 @@ public RedisCacheConfiguration serializeValuesWith(SerializationPair<?> valueSer
282283

283284
Assert.notNull(valueSerializationPair, "ValueSerializationPair must not be null");
284285

285-
return new RedisCacheConfiguration(getTtlFunction(), getAllowCacheNullValues(), isTtiExpirationEnabled(),
286+
return new RedisCacheConfiguration(getTtlFunction(), getAllowCacheNullValues(), isTimeToIdleEnabled(),
286287
usePrefix(), getKeyPrefix(), getKeySerializationPair(), valueSerializationPair, getConversionService());
287288
}
288289

@@ -296,7 +297,7 @@ public RedisCacheConfiguration withConversionService(ConversionService conversio
296297

297298
Assert.notNull(conversionService, "ConversionService must not be null");
298299

299-
return new RedisCacheConfiguration(getTtlFunction(), getAllowCacheNullValues(), isTtiExpirationEnabled(),
300+
return new RedisCacheConfiguration(getTtlFunction(), getAllowCacheNullValues(), isTimeToIdleEnabled(),
300301
usePrefix(), getKeyPrefix(), getKeySerializationPair(), getValueSerializationPair(), conversionService);
301302
}
302303

@@ -310,14 +311,15 @@ public boolean getAllowCacheNullValues() {
310311
/**
311312
* Determines whether {@literal time-to-idle (TTI) expiration} has been enabled for caching.
312313
* <p>
313-
* Use {@link #enableTtiExpiration()} to opt-in and enable {@literal time-to-idle (TTI) expiration} for caching.
314+
* Use {@link #enableTimeToIdle()} to opt-in and enable {@literal time-to-idle (TTI) expiration} for caching.
314315
*
315316
* @return {@literal true} if {@literal time-to-idle (TTI) expiration} was configured and enabled for caching.
316317
* Defaults to {@literal false}.
317318
* @see <a href="https://redis.io/commands/getex/">GETEX</a>
319+
* @since 3.2.0
318320
*/
319-
public boolean isTtiExpirationEnabled() {
320-
return this.enableTtiExpiration;
321+
public boolean isTimeToIdleEnabled() {
322+
return this.enableTimeToIdle;
321323
}
322324

323325
/**

src/main/java/org/springframework/data/redis/cache/RedisCacheWriter.java

+5-4
Original file line numberDiff line numberDiff line change
@@ -119,9 +119,7 @@ static RedisCacheWriter lockingRedisCacheWriter(RedisConnectionFactory connectio
119119
* @see #get(String, byte[], Duration)
120120
*/
121121
@Nullable
122-
default byte[] get(String name, byte[] key) {
123-
return get(name, key, null);
124-
}
122+
byte[] get(String name, byte[] key);
125123

126124
/**
127125
* Get the binary value representation from Redis stored for the given key and set the given
@@ -132,7 +130,10 @@ default byte[] get(String name, byte[] key) {
132130
* @param ttl {@link Duration} specifying the {@literal expiration timeout} for the cache entry.
133131
* @return {@literal null} if key does not exist or has {@literal expired}.
134132
*/
135-
byte[] get(String name, byte[] key, @Nullable Duration ttl);
133+
@Nullable
134+
default byte[] get(String name, byte[] key, @Nullable Duration ttl) {
135+
return get(name, key);
136+
}
136137

137138
/**
138139
* Write the given key/value pair to Redis and set the expiration time if defined.

src/main/java/org/springframework/data/redis/core/types/Expiration.java

+2-1
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121

2222
import org.springframework.lang.Nullable;
2323
import org.springframework.util.Assert;
24+
import org.springframework.util.ObjectUtils;
2425

2526
/**
2627
* {@link Expiration} holds a {@link Long numeric value} with an associated {@link TimeUnit}.
@@ -236,7 +237,7 @@ public boolean equals(Object obj) {
236237

237238
@Override
238239
public int hashCode() {
239-
return Objects.hash(getExpirationTime(), getTimeUnit());
240+
return ObjectUtils.nullSafeHashCode(new Object[] { getExpirationTime(), getTimeUnit() });
240241
}
241242

242243
/**

src/test/java/org/springframework/data/redis/cache/DefaultRedisCacheWriterUnitTests.java

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2017-2023 the original author or authors.
2+
* Copyright 2023 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -42,7 +42,7 @@
4242
* @author John Blum
4343
*/
4444
@ExtendWith(MockitoExtension.class)
45-
public class DefaultRedisCacheWriterUnitTests {
45+
class DefaultRedisCacheWriterUnitTests {
4646

4747
@Mock
4848
private RedisConnection mockConnection;

src/test/java/org/springframework/data/redis/cache/RedisCacheConfigurationUnitTests.java

+3-3
Original file line numberDiff line numberDiff line change
@@ -107,13 +107,13 @@ public void enableTtiExpirationShouldConfigureTti() {
107107
RedisCacheConfiguration cacheConfiguration = RedisCacheConfiguration.defaultCacheConfig();
108108

109109
assertThat(cacheConfiguration).isNotNull();
110-
assertThat(cacheConfiguration.isTtiExpirationEnabled()).isFalse();
110+
assertThat(cacheConfiguration.isTimeToIdleEnabled()).isFalse();
111111

112-
RedisCacheConfiguration ttiEnabledCacheConfiguration = cacheConfiguration.enableTtiExpiration();
112+
RedisCacheConfiguration ttiEnabledCacheConfiguration = cacheConfiguration.enableTimeToIdle();
113113

114114
assertThat(ttiEnabledCacheConfiguration).isNotNull();
115115
assertThat(ttiEnabledCacheConfiguration).isNotSameAs(cacheConfiguration);
116-
assertThat(ttiEnabledCacheConfiguration.isTtiExpirationEnabled()).isTrue();
116+
assertThat(ttiEnabledCacheConfiguration.isTimeToIdleEnabled()).isTrue();
117117
}
118118

119119
private static class DomainType {

src/test/java/org/springframework/data/redis/cache/RedisCacheTests.java

+8-3
Original file line numberDiff line numberDiff line change
@@ -479,6 +479,11 @@ public void put(String name, byte[] key, byte[] value, @Nullable Duration ttl) {
479479
storage.set(value);
480480
}
481481

482+
@Override
483+
public byte[] get(String name, byte[] key) {
484+
return get(name, key, null);
485+
}
486+
482487
@Override
483488
public byte[] get(String name, byte[] key, @Nullable Duration ttl) {
484489

@@ -545,7 +550,7 @@ public CacheStatistics getCacheStatistics(String cacheName) {
545550
}
546551

547552
@ParameterizedRedisTest // GH-2351
548-
void cacheGetWithTtiExpirationWhenEntryNotExpiredShouldReturnValue() {
553+
void cacheGetWithTimeToIdleExpirationWhenEntryNotExpiredShouldReturnValue() {
549554

550555
doWithConnection(connection -> connection.set(this.binaryCacheKey, this.binarySample));
551556

@@ -561,7 +566,7 @@ void cacheGetWithTtiExpirationWhenEntryNotExpiredShouldReturnValue() {
561566
}
562567

563568
@ParameterizedRedisTest // GH-2351
564-
void cacheGetWithTtiExpirationAfterEntryExpiresShouldReturnNull() {
569+
void cacheGetWithTimeToIdleExpirationAfterEntryExpiresShouldReturnNull() {
565570

566571
doWithConnection(connection -> connection.set(this.binaryCacheKey, this.binarySample));
567572

@@ -585,7 +590,7 @@ private Function<RedisCacheConfiguration, RedisCacheConfiguration> withTtiExpira
585590
Function<RedisCacheConfiguration, RedisCacheConfiguration> entryTtlFunction =
586591
cacheConfiguration -> cacheConfiguration.entryTtl(Duration.ofMillis(100));
587592

588-
return entryTtlFunction.andThen(RedisCacheConfiguration::enableTtiExpiration);
593+
return entryTtlFunction.andThen(RedisCacheConfiguration::enableTimeToIdle);
589594
}
590595

591596
void doWithConnection(Consumer<RedisConnection> callback) {

src/test/java/org/springframework/data/redis/cache/RedisCacheWriterUnitTests.java

+11-8
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2017-2023 the original author or authors.
2+
* Copyright 2023 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -19,38 +19,41 @@
1919
import static org.mockito.ArgumentMatchers.any;
2020
import static org.mockito.ArgumentMatchers.anyString;
2121
import static org.mockito.ArgumentMatchers.eq;
22-
import static org.mockito.ArgumentMatchers.isNull;
2322
import static org.mockito.Mockito.doCallRealMethod;
2423
import static org.mockito.Mockito.doReturn;
2524
import static org.mockito.Mockito.mock;
2625
import static org.mockito.Mockito.times;
2726
import static org.mockito.Mockito.verify;
2827
import static org.mockito.Mockito.verifyNoMoreInteractions;
2928

29+
import java.time.Duration;
30+
3031
import org.junit.jupiter.api.Test;
3132

3233
/**
3334
* Unit tests for {@link RedisCacheWriter}.
3435
*
3536
* @author John Blum
3637
*/
37-
public class RedisCacheWriterUnitTests {
38+
class RedisCacheWriterUnitTests {
3839

3940
@Test // GH-2351
40-
void defaultGetCallsGetWithNullTtlExpiration() {
41+
void defaultGetWithNameKeyAndTtlCallsGetWithNameAndKeyDiscardingTtl() {
4142

4243
byte[] key = "TestKey".getBytes();
4344
byte[] value = "TestValue".getBytes();
4445

46+
Duration thirtyMinutes = Duration.ofMinutes(30);
47+
4548
RedisCacheWriter cacheWriter = mock(RedisCacheWriter.class);
4649

47-
doCallRealMethod().when(cacheWriter).get(anyString(), any());
48-
doReturn(value).when(cacheWriter).get(anyString(), any(), any());
50+
doCallRealMethod().when(cacheWriter).get(anyString(), any(), any());
51+
doReturn(value).when(cacheWriter).get(anyString(), any());
4952

50-
assertThat(cacheWriter.get("TestCacheName", key)).isEqualTo(value);
53+
assertThat(cacheWriter.get("TestCacheName", key, thirtyMinutes)).isEqualTo(value);
5154

55+
verify(cacheWriter, times(1)).get(eq("TestCacheName"), eq(key), eq(thirtyMinutes));
5256
verify(cacheWriter, times(1)).get(eq("TestCacheName"), eq(key));
53-
verify(cacheWriter, times(1)).get(eq("TestCacheName"), eq(key), isNull());
5457
verifyNoMoreInteractions(cacheWriter);
5558
}
5659
}

src/test/java/org/springframework/data/redis/core/types/ExpirationUnitTests.java

+5-5
Original file line numberDiff line numberDiff line change
@@ -57,8 +57,8 @@ void fromMinutes() {
5757
assertThat(expiration.getTimeUnit()).isEqualTo(TimeUnit.SECONDS);
5858
}
5959

60-
@Test
61-
public void equalValuedExpirationsAreEqual() {
60+
@Test // GH-2351
61+
void equalValuedExpirationsAreEqual() {
6262

6363
Expiration sixtyThousandMilliseconds = Expiration.milliseconds(60_000L);
6464
Expiration sixtySeconds = Expiration.seconds(60L);
@@ -69,8 +69,8 @@ public void equalValuedExpirationsAreEqual() {
6969
assertThat(oneMinute).isEqualTo(sixtyThousandMilliseconds);
7070
}
7171

72-
@Test
73-
public void unequalValuedExpirationsAreNotEqual() {
72+
@Test // GH-2351
73+
void unequalValuedExpirationsAreNotEqual() {
7474

7575
Expiration sixtySeconds = Expiration.seconds(60L);
7676
Expiration sixtyMilliseconds = Expiration.milliseconds(60L);
@@ -79,7 +79,7 @@ public void unequalValuedExpirationsAreNotEqual() {
7979
}
8080

8181
@Test // GH-2351
82-
public void hashCodeIsCorrect() {
82+
void hashCodeIsCorrect() {
8383

8484
Expiration expiration = Expiration.seconds(60);
8585

0 commit comments

Comments
 (0)