I have a service which calls external system to retrieve some kind of objects by their external id as well as submit them back to update. Rather than retrieving objects one by one there is a more generic purpose methods:
public interface ExternalSystem {
List<ExternalDTO> getObjects(List<String> externalIds);
void updateObjects(List<ExternalDTO> updates);
}
I would like put a cache on top of the ExternalSystem calls because they are quite expensive.
In the implementation of the service I can simply put spring annotations:
@Cacheable("cache-external")
List<ExternalDTO> getObjects(List<String> externalIds) {}
@CacheEvict(cacheNames="cache-external", allEntries=true)
void updateObjects(List<ExternalDTO> updates);
However, such a cache will behave very badly in case I have a lot of intersection between externalIds, i.e.
- Call#1 getObjects([1,2,3,4]) -> cache put by [1,2,3,4] key
- Call#2 getObjects([1,2,3,4,5]) -> cache put by [1,2,3,4,5] key
- Call#3 getObjects([6,7,8,9]) -> cache put by [6,7,8,9] key
- Call#4 updateObjects(1) -> evict all the caches but the third cache doesn't contain 3
So, the question is how to implement the custom strategy (I assume it's not doable out-of the box) which will evict only those entries which really should be evicted and will make the keys such a way that intersecting objects are retrieved from the cache?
Upd. I've found two similar questions:
- spring-cache-abstraction-with-multi-value-queries
- using-spring-cache-on-methods-that-take-an-array-or-collection
- spring-cacheable-methods-with-lists
Upd2. Here is something similar to what I want except I will put into cache pairs of String and ExternalDTO for each item in collection. element-level-caching-of-list-to-list