Skip to content

@Cacheable does not respect cache hit when empty Mono/Flux response is returned #31868

@fabioacsilva

Description

@fabioacsilva

Hello,

Upon upgrading to spring-boot 3.2.0 and Java 21 our team noticed that our methods annotated with @Cacheable do not cache properly when the return is an empty Flux.

Sample source code provided:
cacheable.zip

Using the past example if we hit the endpoint with anything other than "broken" we get the following log:

2023-12-19T13:41:26.592Z  INFO 50082 --- [           main] c.e.cacheable.CacheableApplicationKt     : Started CacheableApplicationKt in 0.809 seconds (process running for 1.124)
2023-12-19T13:41:30.216Z  INFO 50082 --- [ctor-http-nio-2] com.example.cacheable.Controller         : Controller - id:hello
2023-12-19T13:41:30.217Z TRACE 50082 --- [ctor-http-nio-2] o.s.cache.interceptor.CacheInterceptor   : Computed cache key 'hello' for operation Builder[public reactor.core.publisher.Flux com.example.cacheable.Service.get(java.lang.String)] caches=[myCache] | key='' | keyGenerator='' | cacheManager='' | cacheResolver='' | condition='' | unless='' | sync='false'
2023-12-19T13:41:30.217Z TRACE 50082 --- [ctor-http-nio-2] o.s.cache.interceptor.CacheInterceptor   : No cache entry for key 'hello' in cache(s) [myCache]
2023-12-19T13:41:30.218Z  INFO 50082 --- [ctor-http-nio-2] org.springframework.stereotype.Service   : Service - id:hello
2023-12-19T13:41:30.219Z TRACE 50082 --- [ctor-http-nio-2] o.s.cache.interceptor.CacheInterceptor   : Computed cache key 'hello' for operation Builder[public reactor.core.publisher.Flux com.example.cacheable.Service.get(java.lang.String)] caches=[myCache] | key='' | keyGenerator='' | cacheManager='' | cacheResolver='' | condition='' | unless='' | sync='false'
2023-12-19T13:41:30.219Z TRACE 50082 --- [ctor-http-nio-2] o.s.cache.interceptor.CacheInterceptor   : Creating cache entry for key 'hello' in cache(s) [myCache]
2023-12-19T13:41:32.744Z  INFO 50082 --- [ctor-http-nio-2] com.example.cacheable.Controller         : Controller - id:hello
2023-12-19T13:41:32.745Z TRACE 50082 --- [ctor-http-nio-2] o.s.cache.interceptor.CacheInterceptor   : Computed cache key 'hello' for operation Builder[public reactor.core.publisher.Flux com.example.cacheable.Service.get(java.lang.String)] caches=[myCache] | key='' | keyGenerator='' | cacheManager='' | cacheResolver='' | condition='' | unless='' | sync='false'
2023-12-19T13:41:32.757Z TRACE 50082 --- [ctor-http-nio-2] o.s.cache.interceptor.CacheInterceptor   : Cache entry for key 'hello' found in cache(s) 

As we can see the service log is only called once due to the cache.

If we call it with "broken" we get the following log:

2023-12-19T13:41:48.729Z  INFO 50082 --- [ctor-http-nio-2] com.example.cacheable.Controller         : Controller - id:broken
2023-12-19T13:41:48.730Z TRACE 50082 --- [ctor-http-nio-2] o.s.cache.interceptor.CacheInterceptor   : Computed cache key 'broken' for operation Builder[public reactor.core.publisher.Flux com.example.cacheable.Service.get(java.lang.String)] caches=[myCache] | key='' | keyGenerator='' | cacheManager='' | cacheResolver='' | condition='' | unless='' | sync='false'
2023-12-19T13:41:48.730Z TRACE 50082 --- [ctor-http-nio-2] o.s.cache.interceptor.CacheInterceptor   : No cache entry for key 'broken' in cache(s) [myCache]
2023-12-19T13:41:48.730Z  INFO 50082 --- [ctor-http-nio-2] org.springframework.stereotype.Service   : Service - id:broken
2023-12-19T13:41:48.731Z TRACE 50082 --- [ctor-http-nio-2] o.s.cache.interceptor.CacheInterceptor   : Computed cache key 'broken' for operation Builder[public reactor.core.publisher.Flux com.example.cacheable.Service.get(java.lang.String)] caches=[myCache] | key='' | keyGenerator='' | cacheManager='' | cacheResolver='' | condition='' | unless='' | sync='false'
2023-12-19T13:41:48.731Z TRACE 50082 --- [ctor-http-nio-2] o.s.cache.interceptor.CacheInterceptor   : Creating cache entry for key 'broken' in cache(s) [myCache]
2023-12-19T13:41:51.560Z  INFO 50082 --- [ctor-http-nio-2] com.example.cacheable.Controller         : Controller - id:broken
2023-12-19T13:41:51.561Z TRACE 50082 --- [ctor-http-nio-2] o.s.cache.interceptor.CacheInterceptor   : Computed cache key 'broken' for operation Builder[public reactor.core.publisher.Flux com.example.cacheable.Service.get(java.lang.String)] caches=[myCache] | key='' | keyGenerator='' | cacheManager='' | cacheResolver='' | condition='' | unless='' | sync='false'
2023-12-19T13:41:51.561Z TRACE 50082 --- [ctor-http-nio-2] o.s.cache.interceptor.CacheInterceptor   : Cache entry for key 'broken' found in cache(s) [myCache]
2023-12-19T13:41:51.564Z  INFO 50082 --- [ctor-http-nio-2] org.springframework.stereotype.Service   : Service - id:broken
2023-12-19T13:41:51.565Z TRACE 50082 --- [ctor-http-nio-2] o.s.cache.interceptor.CacheInterceptor   : Computed cache key 'broken' for operation Builder[public reactor.core.publisher.Flux com.example.cacheable.Service.get(java.lang.String)] caches=[myCache] | key='' | keyGenerator='' | cacheManager='' | cacheResolver='' | condition='' | unless='' | sync='false'
2023-12-19T13:41:51.565Z TRACE 50082 --- [ctor-http-nio-2] o.s.cache.interceptor.CacheInterceptor   : Creating cache entry for key 'broken' in cache(s) [myCache]

The service keeps getting called meaning the cache is not working properly.

If we revert to spring-boot 3.1.6 and Java 17 the behaviour is the one expected and the empty flux gets cached

Metadata

Metadata

Assignees

Labels

in: coreIssues in core modules (aop, beans, core, context, expression)type: regressionA bug that is also a regression

Type

No type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions