Skip to content

Commit aa8c5a5

Browse files
Koooooo-7mp911de
authored andcommitted
Add TtlFunction to RedisCacheConfiguration for dynamic Time to Live.
We now support a TtlFunction to compute the time to live for cache entries. Closes spring-projects#1433 Original pull request: spring-projects#2587
1 parent 050beeb commit aa8c5a5

File tree

3 files changed

+62
-15
lines changed

3 files changed

+62
-15
lines changed

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -185,7 +185,7 @@ public void put(Object key, @Nullable Object value) {
185185
}
186186

187187
getCacheWriter().put(getName(), createAndConvertCacheKey(key), serializeCacheValue(cacheValue),
188-
getCacheConfiguration().getTtl());
188+
getCacheConfiguration().getTtl(key, value));
189189
}
190190

191191
@Override
@@ -198,7 +198,7 @@ public ValueWrapper putIfAbsent(Object key, @Nullable Object value) {
198198
}
199199

200200
byte[] result = getCacheWriter().putIfAbsent(getName(), createAndConvertCacheKey(key),
201-
serializeCacheValue(cacheValue), getCacheConfiguration().getTtl());
201+
serializeCacheValue(cacheValue), getCacheConfiguration().getTtl(key, value));
202202

203203
return result != null ? new SimpleValueWrapper(fromStoreValue(deserializeCacheValue(result))) : null;
204204
}

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

Lines changed: 42 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717

1818
import java.nio.charset.StandardCharsets;
1919
import java.time.Duration;
20+
import java.util.function.BiFunction;
2021
import java.util.function.Consumer;
2122

2223
import org.springframework.cache.Cache;
@@ -40,6 +41,7 @@
4041
* @author Christoph Strobl
4142
* @author Mark Paluch
4243
* @author John Blum
44+
* @author Koy Zhuang
4345
* @since 2.0
4446
*/
4547
public class RedisCacheConfiguration {
@@ -106,7 +108,7 @@ public static RedisCacheConfiguration defaultCacheConfig(@Nullable ClassLoader c
106108

107109
registerDefaultConverters(conversionService);
108110

109-
return new RedisCacheConfiguration(Duration.ZERO, DEFAULT_CACHE_NULL_VALUES, DEFAULT_USE_PREFIX,
111+
return new RedisCacheConfiguration((k, v) -> Duration.ZERO, DEFAULT_CACHE_NULL_VALUES, DEFAULT_USE_PREFIX,
110112
CacheKeyPrefix.simple(),
111113
SerializationPair.fromSerializer(RedisSerializer.string()),
112114
SerializationPair.fromSerializer(RedisSerializer.java(classLoader)), conversionService);
@@ -119,17 +121,17 @@ public static RedisCacheConfiguration defaultCacheConfig(@Nullable ClassLoader c
119121

120122
private final ConversionService conversionService;
121123

122-
private final Duration ttl;
124+
private final BiFunction<Object, Object, Duration> ttlProvider;
123125

124126
private final SerializationPair<String> keySerializationPair;
125127
private final SerializationPair<Object> valueSerializationPair;
126128

127129
@SuppressWarnings("unchecked")
128-
private RedisCacheConfiguration(Duration ttl, Boolean cacheNullValues, Boolean usePrefix, CacheKeyPrefix keyPrefix,
130+
private RedisCacheConfiguration(BiFunction<Object, Object, Duration> ttlProvider, Boolean cacheNullValues, Boolean usePrefix, CacheKeyPrefix keyPrefix,
129131
SerializationPair<String> keySerializationPair, SerializationPair<?> valueSerializationPair,
130132
ConversionService conversionService) {
131133

132-
this.ttl = ttl;
134+
this.ttlProvider = ttlProvider;
133135
this.cacheNullValues = cacheNullValues;
134136
this.usePrefix = usePrefix;
135137
this.keyPrefix = keyPrefix;
@@ -165,7 +167,7 @@ public RedisCacheConfiguration computePrefixWith(CacheKeyPrefix cacheKeyPrefix)
165167

166168
Assert.notNull(cacheKeyPrefix, "Function for computing prefix must not be null");
167169

168-
return new RedisCacheConfiguration(ttl, cacheNullValues, DEFAULT_USE_PREFIX, cacheKeyPrefix,
170+
return new RedisCacheConfiguration(ttlProvider, cacheNullValues, DEFAULT_USE_PREFIX, cacheKeyPrefix,
169171
keySerializationPair, valueSerializationPair, conversionService);
170172
}
171173

@@ -178,7 +180,7 @@ public RedisCacheConfiguration computePrefixWith(CacheKeyPrefix cacheKeyPrefix)
178180
* @return new {@link RedisCacheConfiguration}.
179181
*/
180182
public RedisCacheConfiguration disableCachingNullValues() {
181-
return new RedisCacheConfiguration(ttl, DO_NOT_CACHE_NULL_VALUES, usePrefix, keyPrefix, keySerializationPair,
183+
return new RedisCacheConfiguration(ttlProvider, DO_NOT_CACHE_NULL_VALUES, usePrefix, keyPrefix, keySerializationPair,
182184
valueSerializationPair, conversionService);
183185
}
184186

@@ -191,12 +193,12 @@ public RedisCacheConfiguration disableCachingNullValues() {
191193
*/
192194
public RedisCacheConfiguration disableKeyPrefix() {
193195

194-
return new RedisCacheConfiguration(ttl, cacheNullValues, DO_NOT_USE_PREFIX, keyPrefix, keySerializationPair,
196+
return new RedisCacheConfiguration(ttlProvider, cacheNullValues, DO_NOT_USE_PREFIX, keyPrefix, keySerializationPair,
195197
valueSerializationPair, conversionService);
196198
}
197199

198200
/**
199-
* Set the ttl to apply for cache entries. Use {@link Duration#ZERO} to declare an eternal cache.
201+
* Set the constant ttl to apply for cache entries. Use {@link Duration#ZERO} to declare an eternal cache.
200202
*
201203
* @param ttl must not be {@literal null}.
202204
* @return new {@link RedisCacheConfiguration}.
@@ -205,10 +207,25 @@ public RedisCacheConfiguration entryTtl(Duration ttl) {
205207

206208
Assert.notNull(ttl, "TTL duration must not be null");
207209

208-
return new RedisCacheConfiguration(ttl, cacheNullValues, usePrefix, keyPrefix, keySerializationPair,
210+
return new RedisCacheConfiguration((k, v) -> ttl, cacheNullValues, usePrefix, keyPrefix, keySerializationPair,
209211
valueSerializationPair, conversionService);
210212
}
211213

214+
/**
215+
* Set the ttl Provider, which can dynamic provide ttl to apply for cache entries.
216+
* @param ttlProvider {@link BiFunction} calculate ttl with the actual original cache key and value,
217+
* which must not be {@literal null}, and the ttl must not be {@literal null} either.
218+
*
219+
* @return new {@link RedisCacheConfiguration}.
220+
*/
221+
public RedisCacheConfiguration entryTtlProvider(BiFunction<Object, Object, Duration> ttlProvider) {
222+
223+
Assert.notNull(ttlProvider, "ttlProvider must not be null");
224+
225+
return new RedisCacheConfiguration(ttlProvider, cacheNullValues, usePrefix, keyPrefix, keySerializationPair,
226+
valueSerializationPair, conversionService);
227+
}
228+
212229
/**
213230
* Define the {@link SerializationPair} used for de-/serializing cache keys.
214231
*
@@ -219,7 +236,7 @@ public RedisCacheConfiguration serializeKeysWith(SerializationPair<String> keySe
219236

220237
Assert.notNull(keySerializationPair, "KeySerializationPair must not be null");
221238

222-
return new RedisCacheConfiguration(ttl, cacheNullValues, usePrefix, keyPrefix, keySerializationPair,
239+
return new RedisCacheConfiguration(ttlProvider, cacheNullValues, usePrefix, keyPrefix, keySerializationPair,
223240
valueSerializationPair, conversionService);
224241
}
225242

@@ -233,7 +250,7 @@ public RedisCacheConfiguration serializeValuesWith(SerializationPair<?> valueSer
233250

234251
Assert.notNull(valueSerializationPair, "ValueSerializationPair must not be null");
235252

236-
return new RedisCacheConfiguration(ttl, cacheNullValues, usePrefix, keyPrefix, keySerializationPair,
253+
return new RedisCacheConfiguration(ttlProvider, cacheNullValues, usePrefix, keyPrefix, keySerializationPair,
237254
valueSerializationPair, conversionService);
238255
}
239256

@@ -247,7 +264,7 @@ public RedisCacheConfiguration withConversionService(ConversionService conversio
247264

248265
Assert.notNull(conversionService, "ConversionService must not be null");
249266

250-
return new RedisCacheConfiguration(ttl, cacheNullValues, usePrefix, keyPrefix, keySerializationPair,
267+
return new RedisCacheConfiguration(ttlProvider, cacheNullValues, usePrefix, keyPrefix, keySerializationPair,
251268
valueSerializationPair, conversionService);
252269
}
253270

@@ -301,9 +318,21 @@ public SerializationPair<Object> getValueSerializationPair() {
301318
}
302319

303320
/**
304-
* @return The expiration time (ttl) for cache entries. Never {@literal null}.
321+
* @return The constant expiration time (ttl) for cache entries. Never {@literal null}.
305322
*/
306323
public Duration getTtl() {
324+
Duration ttl = ttlProvider.apply(Object.class, Object.class);
325+
Assert.notNull(ttl, "TTL duration must not be null");
326+
return ttl;
327+
}
328+
329+
/**
330+
* @return The expiration time (ttl) for cache entries with original key and value. Never {@literal null}.
331+
*/
332+
public Duration getTtl(Object key, Object val) {
333+
Duration ttl = ttlProvider.apply(key, val);
334+
Assert.notNull(ttl, "TTL duration must not be null");
335+
307336
return ttl;
308337
}
309338

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

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,9 @@
2424
import org.springframework.instrument.classloading.ShadowingClassLoader;
2525
import org.springframework.lang.Nullable;
2626

27+
import java.time.Duration;
28+
import java.util.function.BiFunction;
29+
2730
/**
2831
* Unit tests for {@link RedisCacheConfiguration}.
2932
*
@@ -56,6 +59,21 @@ void shouldAllowConverterRegistration() {
5659
assertThat(config.getConversionService().canConvert(DomainType.class, String.class)).isTrue();
5760
}
5861

62+
63+
@Test
64+
void shouldGetDynamicTtlGivenTtlProvider() {
65+
66+
BiFunction<Object, Object, Duration> ttlProvider = (key, val) ->
67+
Duration.ofSeconds(Integer.parseInt(key + String.valueOf(val)));
68+
69+
RedisCacheConfiguration defaultCacheConfiguration = RedisCacheConfiguration.defaultCacheConfig()
70+
.entryTtlProvider(ttlProvider);
71+
72+
assertThat(defaultCacheConfiguration.getTtl(1, 12)).isEqualTo(Duration.ofSeconds(112));
73+
assertThat(defaultCacheConfiguration.getTtl(15, 22)).isEqualTo(Duration.ofSeconds(1522));
74+
assertThat(defaultCacheConfiguration.getTtl(77, 0)).isEqualTo(Duration.ofSeconds(770));
75+
}
76+
5977
private static class DomainType {
6078

6179
}

0 commit comments

Comments
 (0)