-
Notifications
You must be signed in to change notification settings - Fork 38.2k
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
Improve Configuration Experience for Multiple CacheManagers #33282
Comments
It's a common topic in Spring Boot where users enjoy the automated behavior for configuring the infrastructure but it doesn't expand to multiple instances of the same type, see spring-projects/spring-boot#15732.
There's a mix of things here. First of all, you don't have to override The problem with that stuff is that the infrastructure has to configure early, to be able to wrap things correctly. In your example above, the two cache managers don't need to be technically bean (albeit declaring them as such makes sure that callbacks are honored, typically on shutdown). Having any infrastructure declaring such types is a call for early initialization I am afraid. I can see how the contract of the contributor makes it harder than it should. I wonder if the following would work: @Configuration
class CacheManagersConfig {
@Bean
public CacheManager redisCacheManager(RedisConnectionFactory factory, CacheProperties props) {
// Implementing Redis Cache
}
@Bean
public CacheManager caffeineCache(CacheProperties cacheProperties) {
// Implementing Caffeine Cache
}
} @Configuration
@Import(CacheManagersConfig.class)
@EnableCaching
class CachingConfig implements CachingConfigurer {
private final ObjectProvider<CacheManager> cacheManagers;
CachingConfig(ObjectProvider<CacheManager> cacheManagers) { ... }
@Override
@Bean
public CacheResolver cacheResolver() {
// Implement Resolver
}
} (note I've used |
The
Threw me off, even though the next line does state
Returning public interface CachingConfigurer {
@Nullable
default KeyGenerator keyGenerator() { return new SimpleKeyGenerator(); }
} Although now that conflicts with the Going back to the
This is reminding me a bit of the following Spring Security Issue. Although a That approach makes sense with this section's note in Spring Boot Docs,
Maybe this adjust should be made instead? public interface CacheManager {
Collection<? extends Cache> resolveCaches(CacheOperationInvovationContext<?> context);
Collection<String> getCacheNames();
} Then using the public class ConcurrentMapCacheManager implements CacheManager, BeanClassLoaderAware {
@Override
public Collection<? extends Cache> resolveCaches(CacheOperationInvovationContext<?> context) {
Collection<String> cacheNames = context.getOperation().getCacheNames();
// Unsure if the empty collection check should be in here or the caller
return cacheNames.stream().map(this::getCache).filter(Objects::nonNull).toList();
}
@Nullable
private Cache getCache(String name) {
Cache cache = this.cacheMap.get(name);
if (cache == null && this.dynamic) {
cache = this.cacheMap.computeIfAbsent(name, this::createConcurrentMapCache);
}
return cache;
}
} This feels easier to work with, since there is a single contract to work with. Power users could still used named |
Can we focus on the issue as reported please? I'll have a look to the Javadoc and see what needs to be updated but I think I've provided a fairly good route for what you claim is a problem. Please give that a try and report if that works for you. |
Sorry about that. To make things worse I also realized while writing the reply this was Spring Framework's Repository, not Spring Boot's 🤦🏻♂️. Since the behavior falls into the Cache Auto Configuration, not the Cache Configuration, I'm going to review the Your solution does work, thank you. |
Javadoc has been polished in #33288. The requirement mismatch for |
The automatic configuration of a cache is amazing for the simple cases, however quickly becomes weird if you decide to use multiple
CacheManager
instances.For example you might want to have a Distributed Cache using Redis, and a Local Caffeine or ConcurrentMap Cache for small items, you'd need the following configuration.
From a user perspective there are some parts that are a little verbose compared to other configurations. Highlights would be bringing in the
ApplicationContext
, but the class really doesn't care for it, implementing theKeyGenerator
using the same default Spring does (per CachingConfigurer Docs), and then not overridingCachingConfigurer::cacheManager()
for a mixture of reasons.I personally I think Caching could use a more "Default unless Declared" approach, where we could reduce this down to the following.
This removes the
KeyGenerator
, swapsApplicationContext::getBean
with Parameter Injection, and removes theCachingConfigurer
in favor of global bean detection. What other adjustments would the Spring Boot Team be open to?The text was updated successfully, but these errors were encountered: