Skip to content

Commit f9b7832

Browse files
committed
Introduce configuration API for RedisCacheWriter.
Also, allow configuration of immediate writes to enforce stronger consistency and visibility of cache writes.
1 parent ac70130 commit f9b7832

File tree

5 files changed

+408
-167
lines changed

5 files changed

+408
-167
lines changed

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

Lines changed: 117 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,8 @@ class DefaultRedisCacheWriter implements RedisCacheWriter {
8484

8585
private final AsyncCacheWriter asyncCacheWriter;
8686

87+
private final boolean asynchronousWrites;
88+
8789
/**
8890
* @param connectionFactory must not be {@literal null}.
8991
* @param batchStrategy must not be {@literal null}.
@@ -99,19 +101,11 @@ class DefaultRedisCacheWriter implements RedisCacheWriter {
99101
* @param batchStrategy must not be {@literal null}.
100102
*/
101103
DefaultRedisCacheWriter(RedisConnectionFactory connectionFactory, Duration sleepTime, BatchStrategy batchStrategy) {
102-
this(connectionFactory, sleepTime, TtlFunction.persistent(), CacheStatisticsCollector.none(), batchStrategy);
104+
this(connectionFactory, sleepTime, TtlFunction.persistent(), CacheStatisticsCollector.none(), batchStrategy, true);
103105
}
104106

105-
/**
106-
* @param connectionFactory must not be {@literal null}.
107-
* @param sleepTime sleep time between lock request attempts. Must not be {@literal null}. Use {@link Duration#ZERO}
108-
* to disable locking.
109-
* @param lockTtl Lock TTL function must not be {@literal null}.
110-
* @param cacheStatisticsCollector must not be {@literal null}.
111-
* @param batchStrategy must not be {@literal null}.
112-
*/
113107
DefaultRedisCacheWriter(RedisConnectionFactory connectionFactory, Duration sleepTime, TtlFunction lockTtl,
114-
CacheStatisticsCollector cacheStatisticsCollector, BatchStrategy batchStrategy) {
108+
CacheStatisticsCollector cacheStatisticsCollector, BatchStrategy batchStrategy, boolean asynchronousWrites) {
115109

116110
Assert.notNull(connectionFactory, "ConnectionFactory must not be null");
117111
Assert.notNull(sleepTime, "SleepTime must not be null");
@@ -126,12 +120,116 @@ class DefaultRedisCacheWriter implements RedisCacheWriter {
126120
this.batchStrategy = batchStrategy;
127121

128122
if (REACTIVE_REDIS_CONNECTION_FACTORY_PRESENT && this.connectionFactory instanceof ReactiveRedisConnectionFactory) {
129-
asyncCacheWriter = new AsynchronousCacheWriterDelegate();
123+
this.asyncCacheWriter = new AsynchronousCacheWriterDelegate();
124+
this.asynchronousWrites = asynchronousWrites;
130125
} else {
131126
asyncCacheWriter = UnsupportedAsyncCacheWriter.INSTANCE;
127+
this.asynchronousWrites = false;
132128
}
133129
}
134130

131+
/**
132+
* Create a new {@code DefaultRedisCacheWriter} applying configuration through {@code configurerConsumer}.
133+
*
134+
* @param connectionFactory the connection factory to use.
135+
* @param configurerConsumer configuration consumer.
136+
* @return a new {@code DefaultRedisCacheWriter}.
137+
* @since 4.0
138+
*/
139+
public static DefaultRedisCacheWriter create(RedisConnectionFactory connectionFactory,
140+
Consumer<RedisCacheWriterConfigurer> configurerConsumer) {
141+
142+
Assert.notNull(connectionFactory, "RedisConnectionFactory must not be null");
143+
Assert.notNull(configurerConsumer, "RedisCacheWriterConfigurer function must not be null");
144+
145+
DefaultRedisCacheWriterConfigurer config = new DefaultRedisCacheWriterConfigurer();
146+
configurerConsumer.accept(config);
147+
148+
return new DefaultRedisCacheWriter(connectionFactory, config.lockSleepTime, config.lockTtlFunction,
149+
config.cacheStatisticsCollector, config.batchStrategy, !config.immediateWrites);
150+
}
151+
152+
static class DefaultRedisCacheWriterConfigurer
153+
implements RedisCacheWriterConfigurer, CacheLockingConfigurer, CacheLockingConfiguration {
154+
155+
CacheStatisticsCollector cacheStatisticsCollector = CacheStatisticsCollector.none();
156+
BatchStrategy batchStrategy = BatchStrategies.keys();
157+
Duration lockSleepTime = Duration.ZERO;
158+
TtlFunction lockTtlFunction = TtlFunction.persistent();
159+
boolean immediateWrites = false;
160+
161+
@Override
162+
public RedisCacheWriterConfigurer collectStatistics(CacheStatisticsCollector cacheStatisticsCollector) {
163+
164+
Assert.notNull(cacheStatisticsCollector, "CacheStatisticsCollector must not be null");
165+
this.cacheStatisticsCollector = cacheStatisticsCollector;
166+
167+
return this;
168+
}
169+
170+
@Override
171+
public RedisCacheWriterConfigurer batchStrategy(BatchStrategy batchStrategy) {
172+
173+
Assert.notNull(batchStrategy, "BatchStrategy must not be null");
174+
this.batchStrategy = batchStrategy;
175+
176+
return this;
177+
}
178+
179+
@Override
180+
public RedisCacheWriterConfigurer cacheLocking(Consumer<CacheLockingConfigurer> configurerConsumer) {
181+
182+
Assert.notNull(configurerConsumer, "CacheLockingConfigurer function must not be null");
183+
configurerConsumer.accept(this);
184+
185+
return this;
186+
}
187+
188+
@Override
189+
public RedisCacheWriterConfigurer immediateWrites(boolean enableImmediateWrites) {
190+
191+
this.immediateWrites = enableImmediateWrites;
192+
return this;
193+
}
194+
195+
@Override
196+
public void disable() {
197+
this.lockSleepTime = Duration.ZERO;
198+
}
199+
200+
@Override
201+
public void enable(Consumer<CacheLockingConfiguration> configurationConsumer) {
202+
203+
Assert.notNull(configurationConsumer, "CacheLockingConfigurer function must not be null");
204+
205+
if (this.lockSleepTime.isZero() || this.lockSleepTime.isNegative()) {
206+
this.lockSleepTime = Duration.ofMillis(50);
207+
}
208+
configurationConsumer.accept(this);
209+
}
210+
211+
@Override
212+
public CacheLockingConfiguration sleepTime(Duration sleepTime) {
213+
214+
Assert.notNull(sleepTime, "Lock sleep time must not be null");
215+
Assert.isTrue(!sleepTime.isZero() && !sleepTime.isNegative(),
216+
"Lock sleep time must not be null zero or negative");
217+
218+
this.lockSleepTime = sleepTime;
219+
return this;
220+
}
221+
222+
@Override
223+
public CacheLockingConfiguration lockTimeout(TtlFunction ttlFunction) {
224+
225+
Assert.notNull(ttlFunction, "TTL function must not be null");
226+
227+
this.lockTtlFunction = ttlFunction;
228+
return this;
229+
}
230+
231+
}
232+
135233
@Override
136234
public byte @Nullable [] get(String name, byte[] key) {
137235
return get(name, key, null);
@@ -210,6 +308,10 @@ public boolean supportsAsyncRetrieve() {
210308
return asyncCacheWriter.isSupported();
211309
}
212310

311+
private boolean writeAsynchronously() {
312+
return supportsAsyncRetrieve() && asynchronousWrites;
313+
}
314+
213315
@Override
214316
public CompletableFuture<byte[]> retrieve(String name, byte[] key, @Nullable Duration ttl) {
215317

@@ -238,7 +340,7 @@ public void put(String name, byte[] key, byte[] value, @Nullable Duration ttl) {
238340
Assert.notNull(key, "Key must not be null");
239341
Assert.notNull(value, "Value must not be null");
240342

241-
if (supportsAsyncRetrieve()) {
343+
if (writeAsynchronously()) {
242344
asyncCacheWriter.store(name, key, value, ttl).thenRun(() -> statistics.incPuts(name));
243345
} else {
244346
execute(name, connection -> {
@@ -318,7 +420,7 @@ public void remove(String name, byte[] key) {
318420
Assert.notNull(name, "Name must not be null");
319421
Assert.notNull(key, "Key must not be null");
320422

321-
if (supportsAsyncRetrieve()) {
423+
if (writeAsynchronously()) {
322424
asyncCacheWriter.remove(name, key).thenRun(() -> statistics.incDeletes(name));
323425
} else {
324426
removeIfPresent(name, key);
@@ -340,7 +442,7 @@ public void clean(String name, byte[] pattern) {
340442
Assert.notNull(name, "Name must not be null");
341443
Assert.notNull(pattern, "Pattern must not be null");
342444

343-
if (supportsAsyncRetrieve()) {
445+
if (writeAsynchronously()) {
344446
asyncCacheWriter.clean(name, pattern, batchStrategy)
345447
.thenAccept(deleteCount -> statistics.incDeletesBy(name, deleteCount.intValue()));
346448
return;
@@ -393,7 +495,7 @@ public void clearStatistics(String name) {
393495
@Override
394496
public RedisCacheWriter withStatisticsCollector(CacheStatisticsCollector cacheStatisticsCollector) {
395497
return new DefaultRedisCacheWriter(connectionFactory, sleepTime, lockTtl, cacheStatisticsCollector,
396-
this.batchStrategy);
498+
this.batchStrategy, this.asynchronousWrites);
397499
}
398500

399501
/**

0 commit comments

Comments
 (0)