-
Notifications
You must be signed in to change notification settings - Fork 1.2k
Draft: Reset Caches by flushing DB #3314
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
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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<T> { | ||
|
|
||
| @Nullable | ||
| T doWithCacheWriter(RedisCacheWriter cacheWriter); | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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> T execute(String name, Function<RedisConnection, T> callback) { | ||
| @Override | ||
| public <T> T execute(Function<RedisConnection, T> callback) { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I am sure this will clear up w/ javadocs once we get firm on the direction, but it is currently not clear that the newly added execute method does not do the potential wait for unlocked (i.e. |
||
| return execute(null, callback); | ||
| } | ||
|
|
||
| private <T> T execute(@Nullable String name, Function<RedisConnection, T> callback) { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We could just call |
||
|
|
||
| try (RedisConnection connection = this.connectionFactory.getConnection()) { | ||
| checkAndPotentiallyWaitUntilUnlocked(name, connection); | ||
| if(StringUtils.hasText(name)) { | ||
| checkAndPotentiallyWaitUntilUnlocked(name, connection); | ||
| } | ||
| return callback.apply(connection); | ||
| } | ||
| } | ||
|
|
||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I am sure I am missing something, but it seems that we are overlapping w/ current cache cleaning impl in BatchStrategy ( |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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<String, RedisCacheConfiguration> 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<String, RedisCacheConfiguration> 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) { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. My first thought was to abstract the impl down into the strategy via a Just putting my thoughts - I am not sure which direction to go but I do like the thought of the strategy handling the work. |
||
| 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); | ||
| } | ||
| } | ||
|
|
||
|
|
||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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 { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think this may read better as Uggh, naming... always hard.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. How about just Design-wise, it could be an interface with two static methods, |
||
|
|
||
| public interface ResetCachesStrategy {} | ||
|
|
||
| public static ResetCachesStrategy oneByOne() { | ||
| return DefaultResetStrategy.INSTANCE; | ||
| } | ||
|
|
||
| public static ResetCachesStrategy flushing() { | ||
| return FlushingResetStrategy.INSTANCE; | ||
| } | ||
|
|
||
| enum FlushingResetStrategy implements ResetCachesStrategy, CacheWriterOperation<String> { | ||
|
|
||
| INSTANCE; | ||
|
|
||
| @Override | ||
| public String doWithCacheWriter(RedisCacheWriter cacheWriter) { | ||
| return cacheWriter.execute(connection -> { | ||
| connection.serverCommands().flushDb(FlushOption.ASYNC); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is there any other cleanup / stats we are missing that the |
||
| return "ok"; | ||
| }); | ||
| } | ||
| } | ||
|
|
||
| enum DefaultResetStrategy implements ResetCachesStrategy { | ||
| INSTANCE; | ||
| } | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can be a
@FunctionalInterface.