Spring's Caching Configuration through properties is incredibly useful. For my in particular I enjoy the ability to set it to none in my tests and have it just disable all caching. However the moment you want two CacheManagers you lose out on this ability.
I'll use an example situation where the Developer wants to have two caches. A local Caffeine Cache and a Distributed Redis Cache. Since the declaration of any Cache Manager disables auto configuration, a simple setup requires the following.
@Bean
@Primary
public CacheManager compositeCacheManager(List<CacheManager> cacheManagers) { ... } // Manually configure CompositeCacheManager
@Bean
@ConditionalOnBean(RedisConnectionFactory.class)
public CacheManager redisCache(RedisConnectionFactory redisConnectionFactory, CacheProperties cacheProperties) { ... } // Configure Redis Cache with Factory
@Bean
public CacheManager caffeineCache(CacheProperties cacheProps) { ... } // Configure Caffeine Cache
You could still leverage the CacheProperties for configuring everything but the Cache Names. However this approach isn't recommended (Properties Classes can change), and also removes a lot of quality of life changes from AutoConfiguration (Namely being able to disable it within Tests).
As most Developers will be using Unique Cache Names between their Caches, the following change seems reasonable to me.
spring:
cache:
type: composite # Added to the Type Enum.
cache-names: #Can be kept, however isn't used for type==Composite
redis:
cache-names: my-redis-cache # Used when type==Composite, unsure how it would be used otherwise
# Other configuration
caffeine:
cache-names: my-caffeine-cache # See notes on Redis Cache Configuration
# Other Configuration
composite:
using: # Controls the other Types configured when type==composite
- redis
- caffeine
noop-fallback: false # This is the only property in `CompositeCacheManager`, so we allow it to be configured
```
When `spring.cache.type=composite`, the following are enforced to ensure deterministic caching behavior.
All `CacheManager` instances being used are automatically configured to disable Lazy Cache Creation. This is so that only the `CacheManager` with the Cache Name provided is used.
Cache Names across all configured `CacheManager`s in `spring.cache.composite.using` are unique to the` CacheManager`*, so the following is invalid.
spring.cache.redis.cache-names=my-cache spring.cache.caffeine.cache-names=my-cache
Since the above cannot resolve to a single `CacheManager`, the following annotation wouldn't behave deterministically.
```java
@Cacheable("my-cache")
*This behavior could potentially be tied to if a CacheResolver bean is provided.
As you can select the CacheManager instance directly in @Cacheable, the qualifier String would be {cacheType}CacheManager, for example redisCacheManager or caffeineCacheManager. With the CompositeCacheManager using the traditional cacheManager
Current alternatives,
You can declare two CacheManagers beans with a Primary one, then utilize @Cacheable(cacheManager="my-manager"), instead of the CompositeCacheManager. However changing the location of a single Cache to a different Manager will have cascading code changes.
You can manually build the CompositeCacheManager, however you'll lose out on Auto Configuration.
Comment From: snicoll
Thanks for the suggestion but I've already shared our position in the previous issue you've raised on this topic. Spring Boot auto-configuration will not auto-configure multiple beans of the same type until we review https://github.com/spring-projects/spring-boot/issues/15732.
Irrespective of this, your proposal sounds like programming by properties, which we usually don't recommend. Working on the above would make the auto-configuration much more complex and it already is at the moment.
Comment From: Crain-32
I misunderstood #15732 a bit then. I read it as specifically covering specifically DataSource beans, and how it can be hard to coordinate which one to use.
I do think there is value in discussing the CompositeCacheManager some more though. It is a useful implementation that currently has no visibility to the average Spring Developer.
Comment From: snicoll
I misunderstood https://github.com/spring-projects/spring-boot/issues/15732 a bit then. I read it as specifically covering specifically DataSource beans, and how it can be hard to coordinate which one to use.
Did you read the complete history? Specifically this comment.
I do think there is value in discussing the CompositeCacheManager some more though. It is a useful implementation that currently has no visibility to the average Spring Developer.
I'd argue the average developer wouldn't use such a thing. Any implementation that we put out there has to be reasonable in terms of complexity. Your idea of using qualifier Strings is absolutely reasonable for a private implementation because you chose and live with your conventions, but not here.