Skip to content

Allow to dynamically provide TTL configuration #2587

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 5 commits into from
Closed
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import java.nio.charset.StandardCharsets;
import java.time.Duration;
import java.util.function.Consumer;
import java.util.function.Supplier;

import org.springframework.cache.Cache;
import org.springframework.cache.interceptor.SimpleKey;
Expand All @@ -40,6 +41,7 @@
* @author Christoph Strobl
* @author Mark Paluch
* @author John Blum
* @author Koy Zhuang
* @since 2.0
*/
public class RedisCacheConfiguration {
Expand Down Expand Up @@ -106,7 +108,7 @@ public static RedisCacheConfiguration defaultCacheConfig(@Nullable ClassLoader c

registerDefaultConverters(conversionService);

return new RedisCacheConfiguration(Duration.ZERO, DEFAULT_CACHE_NULL_VALUES, DEFAULT_USE_PREFIX,
return new RedisCacheConfiguration(() -> Duration.ZERO, DEFAULT_CACHE_NULL_VALUES, DEFAULT_USE_PREFIX,
CacheKeyPrefix.simple(),
SerializationPair.fromSerializer(RedisSerializer.string()),
SerializationPair.fromSerializer(RedisSerializer.java(classLoader)), conversionService);
Expand All @@ -119,17 +121,17 @@ public static RedisCacheConfiguration defaultCacheConfig(@Nullable ClassLoader c

private final ConversionService conversionService;

private final Duration ttl;
private final Supplier<Duration> ttlProvider;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A Supplier doesn't make much sense as it just defers the decision on TTL without adding much contextual value.


private final SerializationPair<String> keySerializationPair;
private final SerializationPair<Object> valueSerializationPair;

@SuppressWarnings("unchecked")
private RedisCacheConfiguration(Duration ttl, Boolean cacheNullValues, Boolean usePrefix, CacheKeyPrefix keyPrefix,
private RedisCacheConfiguration(Supplier<Duration> ttl, Boolean cacheNullValues, Boolean usePrefix, CacheKeyPrefix keyPrefix,
SerializationPair<String> keySerializationPair, SerializationPair<?> valueSerializationPair,
ConversionService conversionService) {

this.ttl = ttl;
this.ttlProvider = ttl;
this.cacheNullValues = cacheNullValues;
this.usePrefix = usePrefix;
this.keyPrefix = keyPrefix;
Expand Down Expand Up @@ -165,7 +167,7 @@ public RedisCacheConfiguration computePrefixWith(CacheKeyPrefix cacheKeyPrefix)

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

return new RedisCacheConfiguration(ttl, cacheNullValues, DEFAULT_USE_PREFIX, cacheKeyPrefix,
return new RedisCacheConfiguration(ttlProvider, cacheNullValues, DEFAULT_USE_PREFIX, cacheKeyPrefix,
keySerializationPair, valueSerializationPair, conversionService);
}

Expand All @@ -178,7 +180,7 @@ public RedisCacheConfiguration computePrefixWith(CacheKeyPrefix cacheKeyPrefix)
* @return new {@link RedisCacheConfiguration}.
*/
public RedisCacheConfiguration disableCachingNullValues() {
return new RedisCacheConfiguration(ttl, DO_NOT_CACHE_NULL_VALUES, usePrefix, keyPrefix, keySerializationPair,
return new RedisCacheConfiguration(ttlProvider, DO_NOT_CACHE_NULL_VALUES, usePrefix, keyPrefix, keySerializationPair,
valueSerializationPair, conversionService);
}

Expand All @@ -191,12 +193,12 @@ public RedisCacheConfiguration disableCachingNullValues() {
*/
public RedisCacheConfiguration disableKeyPrefix() {

return new RedisCacheConfiguration(ttl, cacheNullValues, DO_NOT_USE_PREFIX, keyPrefix, keySerializationPair,
return new RedisCacheConfiguration(ttlProvider, cacheNullValues, DO_NOT_USE_PREFIX, keyPrefix, keySerializationPair,
valueSerializationPair, conversionService);
}

/**
* Set the ttl to apply for cache entries. Use {@link Duration#ZERO} to declare an eternal cache.
* Set the constant ttl to apply for cache entries. Use {@link Duration#ZERO} to declare an eternal cache.
*
* @param ttl must not be {@literal null}.
* @return new {@link RedisCacheConfiguration}.
Expand All @@ -205,10 +207,24 @@ public RedisCacheConfiguration entryTtl(Duration ttl) {

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

return new RedisCacheConfiguration(ttl, cacheNullValues, usePrefix, keyPrefix, keySerializationPair,
return new RedisCacheConfiguration(() -> ttl, cacheNullValues, usePrefix, keyPrefix, keySerializationPair,
valueSerializationPair, conversionService);
}

/**
* Set the ttl Provider, which can dynamic provide ttl to apply for cache entries.
*
* @param ttlProvider must not be {@literal null}, and the ttl must not be {@literal null} either.
* @return new {@link RedisCacheConfiguration}.
*/
public RedisCacheConfiguration entryTtlProvider(Supplier<Duration> ttlProvider) {

Assert.notNull(ttlProvider, "ttlProvider must not be null");

return new RedisCacheConfiguration(ttlProvider, cacheNullValues, usePrefix, keyPrefix, keySerializationPair,
valueSerializationPair, conversionService);
}

/**
* Define the {@link SerializationPair} used for de-/serializing cache keys.
*
Expand All @@ -219,7 +235,7 @@ public RedisCacheConfiguration serializeKeysWith(SerializationPair<String> keySe

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

return new RedisCacheConfiguration(ttl, cacheNullValues, usePrefix, keyPrefix, keySerializationPair,
return new RedisCacheConfiguration(ttlProvider, cacheNullValues, usePrefix, keyPrefix, keySerializationPair,
valueSerializationPair, conversionService);
}

Expand All @@ -233,7 +249,7 @@ public RedisCacheConfiguration serializeValuesWith(SerializationPair<?> valueSer

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

return new RedisCacheConfiguration(ttl, cacheNullValues, usePrefix, keyPrefix, keySerializationPair,
return new RedisCacheConfiguration(ttlProvider, cacheNullValues, usePrefix, keyPrefix, keySerializationPair,
valueSerializationPair, conversionService);
}

Expand All @@ -247,7 +263,7 @@ public RedisCacheConfiguration withConversionService(ConversionService conversio

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

return new RedisCacheConfiguration(ttl, cacheNullValues, usePrefix, keyPrefix, keySerializationPair,
return new RedisCacheConfiguration(ttlProvider, cacheNullValues, usePrefix, keyPrefix, keySerializationPair,
valueSerializationPair, conversionService);
}

Expand Down Expand Up @@ -304,6 +320,10 @@ public SerializationPair<Object> getValueSerializationPair() {
* @return The expiration time (ttl) for cache entries. Never {@literal null}.
*/
public Duration getTtl() {
Duration ttl = ttlProvider.get();

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

return ttl;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@
import org.springframework.instrument.classloading.ShadowingClassLoader;
import org.springframework.lang.Nullable;

import java.time.Duration;
import java.util.function.Supplier;

/**
* Unit tests for {@link RedisCacheConfiguration}.
*
Expand Down Expand Up @@ -56,6 +59,25 @@ void shouldAllowConverterRegistration() {
assertThat(config.getConversionService().canConvert(DomainType.class, String.class)).isTrue();
}


@Test
void shouldGetDynamicTtlGivenTtlProvider() {

final int[] base = {1};

Supplier<Duration> ttlProvider = () -> {
base[0] = base[0] * 10;
return Duration.ofSeconds(base[0]);
};

RedisCacheConfiguration defaultCacheConfiguration = RedisCacheConfiguration.defaultCacheConfig()
.entryTtlProvider(ttlProvider);

assertThat(defaultCacheConfiguration.getTtl()).isEqualTo(Duration.ofSeconds(10));
assertThat(defaultCacheConfiguration.getTtl()).isEqualTo(Duration.ofSeconds(100));
assertThat(defaultCacheConfiguration.getTtl()).isEqualTo(Duration.ofSeconds(1000));
}

private static class DomainType {

}
Expand Down