Skip to content

Commit 5568ffd

Browse files
Polishing
Order imports to match code style. Use one line per sentence in documentation. Original pull request: #2643
1 parent dddf353 commit 5568ffd

File tree

3 files changed

+116
-67
lines changed

3 files changed

+116
-67
lines changed

src/main/asciidoc/reference/redis-cache.adoc

+91-43
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,14 @@
33

44
NOTE: Changed in 2.0
55

6-
Spring Data Redis provides an implementation of Spring Framework's {spring-framework-reference}/integration.html#cache[Cache Abstraction] in the `org.springframework.data.redis.cache` package. To use Redis as a backing implementation, add `RedisCacheManager` to your configuration, as follows:
6+
Spring Data Redis provides an implementation of Spring Framework's {spring-framework-reference}/integration.html#cache[Cache Abstraction] in the `org.springframework.data.redis.cache` package.
7+
To use Redis as a backing implementation, add `RedisCacheManager` to your configuration, as follows:
78

89
[source,java]
910
----
1011
@Bean
1112
public RedisCacheManager cacheManager(RedisConnectionFactory connectionFactory) {
12-
return RedisCacheManager.create(connectionFactory);
13+
return RedisCacheManager.create(connectionFactory);
1314
}
1415
----
1516

@@ -18,27 +19,29 @@ public RedisCacheManager cacheManager(RedisConnectionFactory connectionFactory)
1819
[source,java]
1920
----
2021
RedisCacheManager cacheManager = RedisCacheManager.builder(connectionFactory)
21-
.cacheDefaults(RedisCacheConfiguration.defaultCacheConfig())
22-
.transactionAware()
23-
.withInitialCacheConfigurations(Collections.singletonMap("predefined",
22+
.cacheDefaults(RedisCacheConfiguration.defaultCacheConfig())
23+
.transactionAware()
24+
.withInitialCacheConfigurations(Collections.singletonMap("predefined",
2425
RedisCacheConfiguration.defaultCacheConfig().disableCachingNullValues()))
25-
.build();
26+
.build();
2627
----
2728

2829
As shown in the preceding example, `RedisCacheManager` allows custom configuration on a per-cache basis.
2930

30-
The behavior of `RedisCache` created by `RedisCacheManager` is defined with `RedisCacheConfiguration`. The configuration lets you set key expiration times, prefixes, and `RedisSerializer` implementations for converting to and from the binary storage format, as shown in the following example:
31+
The behavior of `RedisCache` created by `RedisCacheManager` is defined with `RedisCacheConfiguration`.
32+
The configuration lets you set key expiration times, prefixes, and `RedisSerializer` implementations for converting to and from the binary storage format, as shown in the following example:
3133

3234
[source,java]
3335
----
3436
RedisCacheConfiguration cacheConfiguration = RedisCacheConfiguration.defaultCacheConfig()
3537
.entryTtl(Duration.ofSeconds(1))
36-
.disableCachingNullValues();
38+
.disableCachingNullValues();
3739
----
3840

3941
`RedisCacheManager` defaults to a lock-free `RedisCacheWriter` for reading and writing binary values.
4042
Lock-free caching improves throughput.
41-
The lack of entry locking can lead to overlapping, non-atomic commands for the `Cache` `putIfAbsent` and `clean` operations, as those require multiple commands to be sent to Redis. The locking counterpart prevents command overlap by setting an explicit lock key and checking against presence of this key, which leads to additional requests and potential command wait times.
43+
The lack of entry locking can lead to overlapping, non-atomic commands for the `Cache` `putIfAbsent` and `clean` operations, as those require multiple commands to be sent to Redis.
44+
The locking counterpart prevents command overlap by setting an explicit lock key and checking against presence of this key, which leads to additional requests and potential command wait times.
4245

4346
Locking applies on the *cache level*, not per *cache entry*.
4447

@@ -48,8 +51,8 @@ It is possible to opt in to the locking behavior as follows:
4851
----
4952
RedisCacheManager cacheMangager = RedisCacheManager
5053
.build(RedisCacheWriter.lockingRedisCacheWriter(connectionFactory))
51-
.cacheDefaults(RedisCacheConfiguration.defaultCacheConfig())
52-
...
54+
.cacheDefaults(RedisCacheConfiguration.defaultCacheConfig())
55+
...
5356
----
5457

5558
By default, any `key` for a cache entry gets prefixed with the actual cache name followed by two colons (`::`).
@@ -69,17 +72,23 @@ RedisCacheConfiguration.defaultCacheConfig()
6972
.computePrefixWith(cacheName -> "¯\_(ツ)_/¯" + cacheName);
7073
----
7174

72-
The cache implementation defaults to use `KEYS` and `DEL` to clear the cache. `KEYS` can cause performance issues with large keyspaces. Therefore, the default `RedisCacheWriter` can be created with a `BatchStrategy` to switch to a `SCAN`-based batch strategy. The `SCAN` strategy requires a batch size to avoid excessive Redis command round trips:
75+
The cache implementation defaults to use `KEYS` and `DEL` to clear the cache. `KEYS` can cause performance issues with large keyspaces.
76+
Therefore, the default `RedisCacheWriter` can be created with a `BatchStrategy` to switch to a `SCAN`-based batch strategy.
77+
The `SCAN` strategy requires a batch size to avoid excessive Redis command round trips:
7378

7479
[source,java]
7580
----
7681
RedisCacheManager cacheManager = RedisCacheManager
7782
.build(RedisCacheWriter.nonLockingRedisCacheWriter(connectionFactory, BatchStrategies.scan(1000)))
7883
.cacheDefaults(RedisCacheConfiguration.defaultCacheConfig())
79-
...
84+
...
8085
----
8186

82-
NOTE: The `KEYS` batch strategy is fully supported using any driver and Redis operation mode (Standalone, Clustered). `SCAN` is fully supported when using the Lettuce driver. Jedis supports `SCAN` only in non-clustered modes.
87+
[NOTE]
88+
====
89+
The `KEYS` batch strategy is fully supported using any driver and Redis operation mode (Standalone, Clustered).
90+
`SCAN` is fully supported when using the Lettuce driver. Jedis supports `SCAN` only in non-clustered modes.
91+
====
8392

8493
The following table lists the default settings for `RedisCacheManager`:
8594

@@ -138,9 +147,34 @@ Use `RedisCacheManagerBuilder.enableStatistics()` to collect local _hits_ and _m
138147
[[redis:support:cache-abstraction:expiration]]
139148
== Redis Cache Expiration
140149

141-
Spring Data Redis's `Cache` implementation supports _time-to-live_ (TTL) expiration on cache entries. Users can either configure the TTL expiration timeout with a fixed `Duration` or a dynamically computed `Duration` per cache entry by supplying an implementation of the new `RedisCacheWriter.TtlFunction` interface.
150+
The implementation of time-to-idle (TTI) as well as time-to-live (TTL) varies in definition and behavior even across different data stores.
151+
152+
In general:
142153

143-
> TIP: The `RedisCacheWriter.TtlFunction` interface was introduced in Spring Data Redis `3.2.0`.
154+
* _time-to-live_ (TTL) _expiration_ - TTL is only set and reset by a create or update data access operation.
155+
As long as the entry is written before the TTL expiration timeout, including on creation, an entry's timeout will reset to the configured duration of the TTL expiration timeout.
156+
For example, if the TTL expiration timeout is set to 5 minutes, then the timeout will be set to 5 minutes on entry creation and reset to 5 minutes anytime the entry is updated thereafter and before the 5-minute interval expires.
157+
If no update occurs within 5 minutes, even if the entry was read several times, or even just read once during the 5-minute interval, the entry will still expire.
158+
The entry must be written to prevent the entry from expiring when declaring a TTL expiration policy.
159+
160+
* _time-to-idle_ (TTI) _expiration_ - TTI is reset anytime the entry is also read as well as for entry updates, and is effectively and extension to the TTL expiration policy.
161+
162+
[NOTE]
163+
====
164+
Some data stores expire an entry when TTL is configured no matter what type of data access operation occurs on the entry (reads, writes, or otherwise).
165+
After the set, configured TTL expiration timeout, the entry is evicted from the data store regardless. Eviction actions (for example: destroy, invalidate, overflow-to-disk (for persistent stores), etc.) are data store specific.
166+
====
167+
168+
[[redis:support:cache-abstraction:expiration:tti]]
169+
=== Time-To-Live (TTL) Expiration
170+
171+
Spring Data Redis's `Cache` implementation supports _time-to-live_ (TTL) expiration on cache entries.
172+
Users can either configure the TTL expiration timeout with a fixed `Duration` or a dynamically computed `Duration` per cache entry by supplying an implementation of the new `RedisCacheWriter.TtlFunction` interface.
173+
174+
[TIP]
175+
====
176+
The `RedisCacheWriter.TtlFunction` interface was introduced in Spring Data Redis `3.2.0`.
177+
====
144178

145179
If all cache entries should expire after a set duration of time, then simply configure a TTL expiration timeout with a fixed `Duration`, as follows:
146180

@@ -156,15 +190,18 @@ However, if the TTL expiration timeout should vary by cache entry, then you must
156190
----
157191
class MyCustomTtlFunction implements TtlFunction {
158192
159-
static final MyCustomTtlFunction INSTANCE = new MyCustomTtlFunction();
193+
static final MyCustomTtlFunction INSTANCE = new MyCustomTtlFunction();
160194
161-
public Duration getTimeToLive(Object key, @Nullable Object value) {
162-
// compute a TTL expiration timeout (Duration) based on the cache entry key and/or value
163-
}
195+
public Duration getTimeToLive(Object key, @Nullable Object value) {
196+
// compute a TTL expiration timeout (Duration) based on the cache entry key and/or value
197+
}
164198
}
165199
----
166200

167-
> NOTE: Under-the-hood, a fixed `Duration` TTL expiration is wrapped in a `TtlFunction` implementation returning the provided `Duration`.
201+
[NOTE]
202+
====
203+
Under-the-hood, a fixed `Duration` TTL expiration is wrapped in a `TtlFunction` implementation returning the provided `Duration`.
204+
====
168205

169206
Then, you can either configure the fixed `Duration` or the dynamic, per-cache entry `Duration` TTL expiration on a global basis using:
170207

@@ -187,7 +224,10 @@ RedisCacheManager cacheManager = RedisCacheManager.builder(redisConnectionFactor
187224
.build();
188225
----
189226

190-
> WARNING: If you try to set both a fixed `Duration` and dynamic, per-cache entry `Duration` TTL expiration using a custom `TtlFunction`, then last one wins!
227+
[WARNING]
228+
====
229+
If you try to set both a fixed `Duration` and dynamic, per-cache entry `Duration` TTL expiration using a custom `TtlFunction`, then last one wins!
230+
====
191231

192232
Of course, you can combine both global and per-cache configuration using:
193233

@@ -204,19 +244,11 @@ RedisCacheManager cacheManager = RedisCacheManager.builder(redisConnectionFactor
204244
[[redis:support:cache-abstraction:expiration:tti]]
205245
=== Time-To-Idle (TTI) Expiration
206246

207-
Redis itself does not support the concept of true, time-to-idle (TTI) expiration. Even across different data stores, the implementation of time-to-idle (TTI) as well as time-to-live (TTL) varies in definition and behavior.
208-
209-
In general:
210-
211-
* _time-to-live_ (TTL) _expiration_ - TTL is only set and reset by a create or update data access operation. As long as the entry is written before the TTL expiration timeout, including on creation, an entry's timeout will reset to the configured duration of the TTL expiration timeout. For example, if the TTL expiration timeout is set to 5 minutes, then the timeout will be set to 5 minutes on entry creation and reset to 5 minutes anytime the entry is updated thereafter and before the 5-minute interval expires. If no update occurs within 5 minutes, even if the entry was read several times, or even just read once during the 5-minute interval, the entry will still expire. The entry must be written to prevent the entry from expiring when declaring a TTL expiration policy.
212-
213-
* _time-to-idle_ (TTI) _expiration_ - TTI is reset anytime the entry is also read as well as for entry updates, and is effectively and extension to the TTL expiration policy.
214-
215-
> NOTE: Some data stores expire an entry when TTL is configured no matter what type of data access operation occurs on the entry (reads, writes, or otherwise). After the set, configured TTL expiration timeout, the entry is evicted from the data store regardless. Eviction actions (for example: destroy, invalidate, overflow-to-disk (for persistent stores), etc.) are data store specific.
216-
217-
Using Spring Data Redis's Cache implementation, it is possible to achieve time-to-idle (TTI) expiration-like behavior.
247+
Redis itself does not support the concept of true, time-to-idle (TTI) expiration.
248+
Still, using Spring Data Redis's Cache implementation, it is possible to achieve time-to-idle (TTI) expiration-like behavior.
218249

219-
The configuration of TTI in Spring Data Redis's Cache implementation must be explicitly enabled, that is, is opt-in. Additionally, you must also provide TTL configuration using either a fixed `Duration` or a custom implementation of the `TtlFunction` interface as described above in <<redis:support:cache-abstraction:expiration>>.
250+
The configuration of TTI in Spring Data Redis's Cache implementation must be explicitly enabled, that is, is opt-in.
251+
Additionally, you must also provide TTL configuration using either a fixed `Duration` or a custom implementation of the `TtlFunction` interface as described above in <<redis:support:cache-abstraction:expiration>>.
220252

221253
For example:
222254

@@ -226,34 +258,50 @@ For example:
226258
@EnableCaching
227259
class RedisConfiguration {
228260
229-
@Bean
261+
@Bean
230262
RedisConnectionFactory redisConnectionFactory() {
231-
// ...
263+
// ...
232264
}
233265
234-
@Bean
266+
@Bean
235267
RedisCacheConfiguration redisCacheConfiguration() {
236268
237-
return RedisCacheConfiguration.defaultCacheConfig()
269+
return RedisCacheConfiguration.defaultCacheConfig()
238270
.entryTtl(Duration.ofMinutes(5))
239271
.enableTimeToIdle();
240272
}
241273
242-
@Bean
274+
@Bean
243275
RedisCacheManager cacheManager(RedisConnectionFactory connectionFactory,
244276
RedisCacheConfiguraton cacheConfiguraton) {
245277
246-
return RedisCacheManager.builder(connectionFactory)
278+
return RedisCacheManager.builder(connectionFactory)
247279
.cacheDefaults(cacheConfiguration)
248280
.build();
249281
}
250282
}
251283
----
252284

253-
Because Redis servers do not implement a proper notion of TTI, then TTI can only be achieved with Redis commands accepting expiration options. In Redis, the "expiration" is technically a time-to-live (TTL) policy. However, TTL expiration can be passed when reading the value of a key thereby effectively resetting the TTL expiration timeout, as is now the case in Spring Data Redis's `Cache.get(key)` operation.
285+
Because Redis servers do not implement a proper notion of TTI, then TTI can only be achieved with Redis commands accepting expiration options.
286+
In Redis, the "expiration" is technically a time-to-live (TTL) policy.
287+
However, TTL expiration can be passed when reading the value of a key thereby effectively resetting the TTL expiration timeout, as is now the case in Spring Data Redis's `Cache.get(key)` operation.
254288

255289
`RedisCache.get(key)` is implemented by calling the Redis `GETEX` command.
256290

257-
> WARNING: The Redis https://redis.io/commands/getex[`GETEX`] command is only available in Redis version `6.2.0` and later. Therefore, if you are not using Redis `6.2.0` or later, then it is not possible to use Spring Data Redis's TTI expiration. A command execution exception will be thrown if you enable TTI against an incompatible Redis (server) version. No attempt is made to determine if the Redis server version is correct and supports the `GETEX` command.
291+
[WARNING]
292+
====
293+
The Redis https://redis.io/commands/getex[`GETEX`] command is only available in Redis version `6.2.0` and later.
294+
Therefore, if you are not using Redis `6.2.0` or later, then it is not possible to use Spring Data Redis's TTI expiration.
295+
A command execution exception will be thrown if you enable TTI against an incompatible Redis (server) version.
296+
No attempt is made to determine if the Redis server version is correct and supports the `GETEX` command.
297+
====
258298

259-
> WARNING: In order to achieve true time-to-idle (TTI) expiration-like behavior in your Spring Data Redis application, then an entry must be consistently accessed with (TTL) expiration on every read or write operation. There are no exceptions to this rule. If you are mixing and matching different data access patterns across your Spring Data Redis application (for example: caching, invoking operations using `RedisTemplate` and possibly, or especially when using Spring Data Repository CRUD operations), then accessing an entry may not necessarily prevent the entry from expiring if TTL expiration was set. For example, an entry maybe "put" in (written to) the cache during a `@Cacheable` service method invocation with a TTL expiration (i.e. `SET <expiration options>`) and later read using a Spring Data Redis Repository before the expiration timeout (using `GET` without expiration options). A simple `GET` without specifying expiration options will not reset the TTL expiration timeout on an entry. Therefore, the entry may expire before the next data access operation, even though it was just read. Since this cannot be enforced in the Redis server, then it is the responsibility of your application to consistently access an entry when time-to-idle expiration is configured, in and outside of caching, where appropriate.
299+
[WARNING]
300+
====
301+
In order to achieve true time-to-idle (TTI) expiration-like behavior in your Spring Data Redis application, then an entry must be consistently accessed with (TTL) expiration on every read or write operation.
302+
There are no exceptions to this rule.
303+
If you are mixing and matching different data access patterns across your Spring Data Redis application (for example: caching, invoking operations using `RedisTemplate` and possibly, or especially when using Spring Data Repository CRUD operations), then accessing an entry may not necessarily prevent the entry from expiring if TTL expiration was set.
304+
For example, an entry maybe "put" in (written to) the cache during a `@Cacheable` service method invocation with a TTL expiration (i.e. `SET <expiration options>`) and later read using a Spring Data Redis Repository before the expiration timeout (using `GET` without expiration options).
305+
A simple `GET` without specifying expiration options will not reset the TTL expiration timeout on an entry.
306+
Therefore, the entry may expire before the next data access operation, even though it was just read. Since this cannot be enforced in the Redis server, then it is the responsibility of your application to consistently access an entry when time-to-idle expiration is configured, in and outside of caching, where appropriate.
307+
====

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

+2
Original file line numberDiff line numberDiff line change
@@ -218,6 +218,8 @@ public RedisCacheConfiguration disableKeyPrefix() {
218218
* is applied to all {@link Cache} operations, both read and write alike, and {@link Cache} operations passed with
219219
* expiration are used consistently across the application, then in effect, an application can achieve
220220
* {@literal TTI} expiration-like behavior.
221+
* <p>
222+
* Requires Redis 6.2.0 or newer.
221223
*
222224
* @return this {@link RedisCacheConfiguration}.
223225
* @see <a href="https://redis.io/commands/getex/">GETEX</a>

0 commit comments

Comments
 (0)