diff --git a/pom.xml b/pom.xml
index 95fb2956ff..fb65810359 100644
--- a/pom.xml
+++ b/pom.xml
@@ -5,7 +5,7 @@
org.springframework.data
spring-data-redis
- 4.1.0-SNAPSHOT
+ 4.1.x-GH-3290-SNAPSHOT
Spring Data Redis
Spring Data module for Redis
diff --git a/src/main/java/org/springframework/data/redis/cache/CacheWriterOperation.java b/src/main/java/org/springframework/data/redis/cache/CacheWriterOperation.java
new file mode 100644
index 0000000000..d568a48df3
--- /dev/null
+++ b/src/main/java/org/springframework/data/redis/cache/CacheWriterOperation.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2026-present the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.springframework.data.redis.cache;
+
+import org.jspecify.annotations.Nullable;
+
+/**
+ * @author Christoph Strobl
+ */
+interface CacheWriterOperation {
+
+ @Nullable
+ T doWithCacheWriter(RedisCacheWriter cacheWriter);
+}
diff --git a/src/main/java/org/springframework/data/redis/cache/DefaultRedisCacheWriter.java b/src/main/java/org/springframework/data/redis/cache/DefaultRedisCacheWriter.java
index a8b70dbcb5..e02443fc97 100644
--- a/src/main/java/org/springframework/data/redis/cache/DefaultRedisCacheWriter.java
+++ b/src/main/java/org/springframework/data/redis/cache/DefaultRedisCacheWriter.java
@@ -15,6 +15,7 @@
*/
package org.springframework.data.redis.cache;
+import org.springframework.util.StringUtils;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
@@ -24,6 +25,7 @@
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;
+import java.util.function.BiFunction;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier;
@@ -535,10 +537,17 @@ Long doUnlock(String name, RedisConnection connection) {
return connection.keyCommands().del(createCacheLockKey(name));
}
- private T execute(String name, Function callback) {
+ @Override
+ public T execute(Function callback) {
+ return execute(null, callback);
+ }
+
+ private T execute(@Nullable String name, Function callback) {
try (RedisConnection connection = this.connectionFactory.getConnection()) {
- checkAndPotentiallyWaitUntilUnlocked(name, connection);
+ if(StringUtils.hasText(name)) {
+ checkAndPotentiallyWaitUntilUnlocked(name, connection);
+ }
return callback.apply(connection);
}
}
diff --git a/src/main/java/org/springframework/data/redis/cache/RedisCacheManager.java b/src/main/java/org/springframework/data/redis/cache/RedisCacheManager.java
index fb25d8ecef..81dc0f0794 100644
--- a/src/main/java/org/springframework/data/redis/cache/RedisCacheManager.java
+++ b/src/main/java/org/springframework/data/redis/cache/RedisCacheManager.java
@@ -27,6 +27,8 @@
import org.springframework.cache.Cache;
import org.springframework.cache.CacheManager;
import org.springframework.cache.transaction.AbstractTransactionSupportingCacheManager;
+import org.springframework.data.redis.cache.ResetCachesStrategies.DefaultResetStrategy;
+import org.springframework.data.redis.cache.ResetCachesStrategies.ResetCachesStrategy;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.util.Assert;
@@ -64,6 +66,8 @@ public class RedisCacheManager extends AbstractTransactionSupportingCacheManager
private final Map initialCacheConfiguration;
+ private final ResetCachesStrategy resetCachesStrategy;
+
/**
* Creates a new {@link RedisCacheManager} initialized with the given {@link RedisCacheWriter} and default
* {@link RedisCacheConfiguration}.
@@ -109,6 +113,19 @@ private RedisCacheManager(RedisCacheWriter cacheWriter, RedisCacheConfiguration
this.cacheWriter = cacheWriter;
this.initialCacheConfiguration = new LinkedHashMap<>();
this.allowRuntimeCacheCreation = allowRuntimeCacheCreation;
+ this.resetCachesStrategy = DefaultResetStrategy.INSTANCE;
+ }
+
+ private RedisCacheManager(RedisCacheWriter cacheWriter, RedisCacheConfiguration redisCacheConfiguration, boolean allowRuntimeCacheCreation, Map initialCaches, ResetCachesStrategy resetCachesStrategy) {
+
+ Assert.notNull(redisCacheConfiguration, "DefaultCacheConfiguration must not be null");
+ Assert.notNull(cacheWriter, "CacheWriter must not be null");
+
+ this.defaultCacheConfiguration = redisCacheConfiguration;
+ this.cacheWriter = cacheWriter;
+ this.initialCacheConfiguration = new LinkedHashMap<>(initialCaches);
+ this.allowRuntimeCacheCreation = allowRuntimeCacheCreation;
+ this.resetCachesStrategy = resetCachesStrategy;
}
/**
@@ -225,6 +242,8 @@ public RedisCacheManager(RedisCacheWriter cacheWriter, RedisCacheConfiguration d
this.initialCacheConfiguration.putAll(initialCacheConfigurations);
}
+
+
/**
* Factory method returning a {@literal Builder} used to construct and configure a {@link RedisCacheManager}.
*
@@ -312,6 +331,16 @@ public boolean isAllowRuntimeCacheCreation() {
return this.allowRuntimeCacheCreation;
}
+ @Override
+ public void resetCaches() {
+
+ if(resetCachesStrategy instanceof CacheWriterOperation> operation) {
+ operation.doWithCacheWriter(cacheWriter);
+ return;
+ }
+ super.resetCaches();
+ }
+
/**
* Return an {@link Collections#unmodifiableMap(Map) unmodifiable Map} containing {@link String caches name} mapped to
* the {@link RedisCache} {@link RedisCacheConfiguration configuration}.
@@ -404,6 +433,7 @@ public static class RedisCacheManagerBuilder {
private boolean allowRuntimeCacheCreation = true;
private boolean enableTransactions;
+ private ResetCachesStrategy resetCachesStrategy = ResetCachesStrategies.oneByOne();
private CacheStatisticsCollector statisticsCollector = CacheStatisticsCollector.none();
@@ -610,6 +640,11 @@ public RedisCacheManagerBuilder withInitialCacheConfigurations(
return this;
}
+ public RedisCacheManagerBuilder withResetCachesStrategy(ResetCachesStrategy resetCachesStrategy) {
+ this.resetCachesStrategy = resetCachesStrategy;
+ return this;
+ }
+
/**
* Get the {@link RedisCacheConfiguration} for a given cache by its name.
*
@@ -654,7 +689,9 @@ public RedisCacheManager build() {
}
private RedisCacheManager newRedisCacheManager(RedisCacheWriter cacheWriter) {
- return new RedisCacheManager(cacheWriter, cacheDefaults(), this.allowRuntimeCacheCreation, this.initialCaches);
+ return new RedisCacheManager(cacheWriter, cacheDefaults(), this.allowRuntimeCacheCreation, this.initialCaches, this.resetCachesStrategy);
}
}
+
+
}
diff --git a/src/main/java/org/springframework/data/redis/cache/RedisCacheWriter.java b/src/main/java/org/springframework/data/redis/cache/RedisCacheWriter.java
index 338b053e05..dae75f3aff 100644
--- a/src/main/java/org/springframework/data/redis/cache/RedisCacheWriter.java
+++ b/src/main/java/org/springframework/data/redis/cache/RedisCacheWriter.java
@@ -18,10 +18,13 @@
import java.time.Duration;
import java.util.concurrent.CompletableFuture;
import java.util.function.Consumer;
+import java.util.function.Function;
import java.util.function.Supplier;
import org.jspecify.annotations.Nullable;
+import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.connection.RedisConnectionFactory;
+import org.springframework.data.redis.core.RedisCallback;
import org.springframework.util.Assert;
/**
@@ -342,6 +345,10 @@ default boolean invalidate(String name, byte[] pattern) {
*/
void clearStatistics(String name);
+ default T execute(Function callback) {
+ throw new UnsupportedOperationException("execute(...) is not supported by this RedisCacheWriter");
+ }
+
/**
* Obtain a {@link RedisCacheWriter} using the given {@link CacheStatisticsCollector} to collect metrics.
*
diff --git a/src/main/java/org/springframework/data/redis/cache/ResetCachesStrategies.java b/src/main/java/org/springframework/data/redis/cache/ResetCachesStrategies.java
new file mode 100644
index 0000000000..af1ab8730b
--- /dev/null
+++ b/src/main/java/org/springframework/data/redis/cache/ResetCachesStrategies.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2026-present the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.springframework.data.redis.cache;
+
+import org.springframework.data.redis.connection.RedisServerCommands.FlushOption;
+
+/**
+ * @author Christoph Strobl
+ */
+public abstract class ResetCachesStrategies {
+
+ public interface ResetCachesStrategy {}
+
+ public static ResetCachesStrategy oneByOne() {
+ return DefaultResetStrategy.INSTANCE;
+ }
+
+ public static ResetCachesStrategy flushing() {
+ return FlushingResetStrategy.INSTANCE;
+ }
+
+ enum FlushingResetStrategy implements ResetCachesStrategy, CacheWriterOperation {
+
+ INSTANCE;
+
+ @Override
+ public String doWithCacheWriter(RedisCacheWriter cacheWriter) {
+ return cacheWriter.execute(connection -> {
+ connection.serverCommands().flushDb(FlushOption.ASYNC);
+ return "ok";
+ });
+ }
+ }
+
+ enum DefaultResetStrategy implements ResetCachesStrategy {
+ INSTANCE;
+ }
+}
diff --git a/src/test/java/org/springframework/data/redis/cache/RedisCacheManagerUnitTests.java b/src/test/java/org/springframework/data/redis/cache/RedisCacheManagerUnitTests.java
index 4d314b6540..f74c5ec7fb 100644
--- a/src/test/java/org/springframework/data/redis/cache/RedisCacheManagerUnitTests.java
+++ b/src/test/java/org/springframework/data/redis/cache/RedisCacheManagerUnitTests.java
@@ -20,15 +20,18 @@
import java.time.Duration;
import java.util.Collections;
+import java.util.function.Function;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
-
import org.springframework.cache.Cache;
import org.springframework.cache.transaction.TransactionAwareCacheDecorator;
import org.springframework.data.redis.cache.RedisCacheManager.RedisCacheManagerBuilder;
+import org.springframework.data.redis.connection.RedisConnection;
+import org.springframework.data.redis.connection.RedisServerCommands;
import org.springframework.test.util.ReflectionTestUtils;
/**
@@ -211,4 +214,21 @@ void customizeRedisCacheConfigurationBasedOnDefaultsIsImmutable() {
assertThat(defaultCacheConfiguration.usePrefix()).isTrue();
assertThat(defaultCacheConfiguration.getTtlFunction().getTimeToLive(null, null)).isEqualTo(Duration.ofMinutes(30));
}
+
+ @Test // GH-3290
+ void configurationAllowsToSetResetCachesConfiguration() {
+
+ RedisConnection connectionMock = mock(RedisConnection.class);
+ RedisServerCommands serverCommandsMock = mock(RedisServerCommands.class);
+ ArgumentCaptor> capture = ArgumentCaptor.captor();
+ when(cacheWriter.execute(capture.capture())).thenReturn("ok");
+ when(connectionMock.serverCommands()).thenReturn(serverCommandsMock);
+
+ RedisCacheManager cacheManager = RedisCacheManager.builder(cacheWriter)
+ .withResetCachesStrategy(ResetCachesStrategies.flushing()).build();
+ cacheManager.resetCaches();
+
+ capture.getValue().apply(connectionMock);
+ verify(serverCommandsMock).flushDb(any());
+ }
}