I am working in a Kotlin and Spring Boot project and I am trying to use Caffeine for caching. I have a service with a suspending function that makes an http call. Here is my config:
@Bean
open fun caffeineConfig(): @NonNull Caffeine<Any, Any> {
return Caffeine.newBuilder().expireAfterWrite(60, TimeUnit.SECONDS)
}
@Bean
open fun cacheManager(caffeine: Caffeine<Any, Any>): CacheManager {
val caffeineCacheManager = CaffeineCacheManager()
caffeineCacheManager.getCache("test")
caffeineCacheManager.setCaffeine(caffeine)
return caffeineCacheManager
}
And here is the function that I want to cache:
@Cacheable(value = ["test"])
open suspend fun getString(id: String): String {
return client.getString(id)
}
But it seems that the caching is not working since I can see from logs that the client gets called every time the service-function gets called. Does @Cacheable
not work for suspending functions? Or am I missing something else?
The documentation of @Cacheable
says:
Each time an advised method is invoked, caching behavior will be applied, checking whether the method has been already invoked for the given arguments. A sensible default simply uses the method parameters to compute the key, but a SpEL expression can be provided via the key() attribute, or a custom KeyGenerator implementation can replace the default one (see keyGenerator()).
The suspend
modifier inserts an Continuation<String>
parameter in the generated code which accepts input from the caller. This presumably means each invocation gets its own continuation and the cache detects this as a unique call.
However since the return value also gets changed depending on the continuation you cannot have the cache ignore the continuation parameter. A better approach is to not use suspend
functions and instead returning a Deferred
which consumers can share:
@Cacheable(value = ["test"])
open fun getString(id: String): Deferred<String> {
return someScope.async {
client.getString(id)
}
}
// Consumer side
getString(id).await()
This should work with the standard caching mechanism since Deferred
is a normal object and no special parameters are required.