How to set ttl based on response object in spring Cacheable - spring

I have below use case for Cache. I am using #Cacheable annotation and it looks something like this:
#Cacheable(cacheNames = "sampleCache", key = "#customerId", cacheResolver = "sampleCacheResolver")
public #ResponseBody SampleResponse getSampleMethod(String customerId) {
}
TTL is set in sampleCacheResolver. However I have a requirement where I have to change the TTL based on the response object. For example, in the response object if say SampleResponse.month is current month, I want to set the ttl to 1 min, else I want to leave the default value of 3 mins.
Since the sampleCacheResolver is getting called at the request level, I am not sure how I can update the ttl based on the response.

This is not possible with Spring's Cache Abstraction out of the box.
Spring is very clear that neither TTL (Time To Live) nor TTI (Idle Timeout) expiration policies (or even Eviction policies for that matter) are handled by the abstraction, and for good reason.
Expiration and Eviction implementations are caching provider specific and many have different possible actions when entries expire or are evicted.
It's rather peculiar to apply TTL to the response, particular given that TTL is a set timeframe.
At any rate, you have 2 options.
First, some caching providers and their supporting Spring bits offer capabilities close to what you are seeking.
For instance, I work on Spring Boot, Session and Data for Apache Geode, which can serve as a caching provider in Spring's Cache Abstraction.
1 of the features I support is entry level (that is, entity) expiration policies via an annotation-based configuration model. See here. Of course, this is made possible by Apache Geode itself given it's ability to express custom expiration policies. That is, the implementation of entry level TTL using annotations is based on Apache Geode's CustomExpiry interface.
Another way to handle entry/entity or response TTL is to override/extend Spring's Cache Abstraction. The 2 primary interfaces in the abstraction is the CacheManager and Cache interfaces.
In your case, you would implement the Cache interface to include the TTL logic you need. You Cache (TTL) implementation would wrap the actual caching provider implementation (e.g. EhCache Cache). Then you must implement the CacheManager interface to wrap the caching provider CacheManager implementation (again, the EhCache CacheManager). Your CacheManager implementation exists soley to create instances of your TTL enabled Cache implementation wrapping the provider's Cache implementation, when called upon by the framework. Essentially, your TTL enabled Cache implementation is enhancing, or decorating the caching provider's Cache implementation with the TTL logic you require.
Yet another possible solution would be to use Spring AOP. Indeed, many of Spring's features like Caching or Transaction Management (demarcation) is handled by Spring AOP. A carefully crafted AOP Aspect could wrap the Caching Aspect and contain the TTL logic you require. You must be sure that the TTL AOP Aspect comes after Spring's Caching Aspect, which can be handled by bean ordering.
I did not provide an example for your exact use case this time. However, I have answered questions that required a "custom" implementation of Spring's Cache and CacheManager interfaces before. For your reference, you can refer to my example to see how you might implement your own Cache/CacheManager combination.
Before going down the paths I presented, I encourage you to explore your caching provider to see if it affords you the TTL functionality you are after. You should choose an appropriate caching provider based on your needs and requirements.
I know this is a lot of information, but I simply wanted you to have options and to think about the problem from multiple angles.

Related

Spring caching implementation

I am exploring spring caching facility. I have few queries regarding this.
First, should it be applied at service method level or DAO method level, where service method is calling DAO method.
Second, how should I avoid cache data getting stale?
IMO, The answer to both questions is "it depends".
Technically, Spring Wise, cache annotations applied on both Service and DAO will work, I don't think there is any difference, so it boils down to the concrete use case.
For example, if you "logically" plan to provide a cacheable abstraction of what should be calculated as a result of some computational process done on server, you better go with Caching at the service level.
If, on the other hand you have a DAO method that looks like Something getSomethingById(id) in the dao, and you would like to avoid relatively expensive calls to the underlying database, you can provide a cache at the level of the DAO. Having said that, it probably won't be useful to apply caching if you have methods like List<Something> fetchAll() or List<Something> fetchAllByFilter(). If you're working with JPA (implemented with Hibernate) they have their own abstraction of cache, but its kind of beyond the scope of the question, just something you should be aware of...
There are plenty of tutorials available on internet, some illustrate the service based approach, some go for DAO's methods annotations but again, these are only simple examples, in the real world you'll have to take a decision.
Now regarding the second question. In general caching makes sense if your data doesn't change much, so first off if it changes often, then probably caching is not appropriate/relevant for the use case.
Other than that, there are many techniques:
Cache Data Eviction (usually time based). See this tutorial
Some kind of messaging system that will send the message about the cache entry change. This one is especially useful if you have a distributed application and keep the cache in-memory only. When getting a message you might opt for "cache replication" or totally wiping out the cache, so that it will be "filled" with a new data eventually
Using the Distributed cache technologies like Hazelcast or Redis as opposed to the in-memory caching. So that technically the cached data consistency will be guaranteed by caching provider.
I would like also to recommend you This tutorial - the speaker talks about different aspects of caching implementation and I think its really relevant to your question.

Is there any way in spring-data-gemfire to set the TTL for every entry when I put an object(entry) in cache?

I want to set TTL for every object in GemFire cache whenever I am putting that object into the cache. The TTL for an entry(object) in the cache can be different from another. Is it possible to do using Spring Data for Pivotal GemFire?
This stackoverflow post has some examples of how to do this -
Spring Data GemFire: CustomExpiry Examples
Basically you can configure your own CustomExpiry for the region than can return a different ttl for each entry.
It is a bit unclear what you are asking.
As Dan mentioned in his answer to this post, you can set TTL for the entire Region, which will apply to all entries.
Alternatively, if you have really specific needs, you can set TTL per class type using the SDG Expiration annotation config as documented here:
https://docs.spring.io/spring-data/gemfire/docs/current/reference/html/#bootstrap:region:expiration:annotation
However, if you Region only stores a single type of object (e.g. Customer.class), then it is advisable to set TTL on the Region.
Although, if you have some complex object hierarchy, then the Expiration annotation support might be of value to your UC.
NOTE: The Expiration annotation support in SDG is actually implemented using a CustomExpiry implementation provided by SDG that introspect's the object's class type to determine the expiration policy (wether TTL or TTI) for that object based on the annotations.

Spring JCache logging cache hits

I have a method on which I added a cache by adding the #CacheResult annotation (I actual created a proxy because I can't change the original implementation of SomethingService):
#Service
public class SomethingServiceProxyImpl implements SomethingService {
#Autowired
#Qualifier("somethingService")
SomethingService somethingService;
#Override
#CacheResult(cacheName = "somethingCache", exceptionCacheName = "somethingExceptionCache", cachedExceptions = { SomeException.class })
public SomePojo someMethod(String someArg) {
return somethingService.someMethod(someArg);
}
}
What I need now, is to be able to log cache hits, meaning cases where the result returned was the one from the cache. I've looked at Spring Cache, at JCache and EHCache (the implementation I use) and I've only found way to listen (with listeners) to the following events: CREATED, UPDATED, REMOVED, EVICTED, EXPIRED but none of them have an event for when the cache returned a result (not null).
I don't really want to have to change the implementation to use the cache programatically instead of using the annotations (I actually have a lot of services to change, not just the one), is there a good way to log those events anyway?
Thoughts about that topic. Probably, the first two are the most relevant:
Don't: The code that gets executed in Spring and the respective cache on a cache hit, is the most performance critical one. That's why it is not so clever to let call additional code in that case, or even have an option for that. Wiring in a log will impact your performance massively. Usually there is already logging in an application for everything that leads to a cache request (e.g. incoming web requests). To get an idea whether the cache is working correctly, a counter of the hits is enough. That is available via the JCache JMX Statistics.
Logging adapter: Using Spring, you can write a Cache adapter which does the logging as you need it and wire it in via configuration. Rough idea: Look at the CacheManager and Cache interfaces. Wrap the CacheManager create cache method and return a wrapped cache with logging.
Hack via ExpiryPolicy: When a custom ExpiryPolicy is specified a JCache implementation calls the method getExpiryForAccess on every cache access. However, you don't get any information on the actual key being requested. I also recommend staying away from own ExpiryPolicy implementations, because of performance reasons. So this is just for completeness.
Logging cache / log every access: In case you specify multiple caches, Spring calls them one after another. You could wire in a dummy cache as first cache, which just logs the access.

Method caching with Spring boot and Hazelcast.How and where do I specify my refresh/reload intervals?

I realise #Cacheable annotation helps me with caching the result of a particular method call and subsequent calls are returned from the cache if there are no changes to arguments etc.
I have a requirement where I'm trying to minimise the number of calls to a db and hence loading the entire table. However,I would like to reload this data say every day just to ensure that my cache is not out of sync with the underlying data on the database.
How can I specify such reload/refresh intervals.
I'm trying to use Spring boot and hazelcast.All the examples I have seen talk about specifying LRU LFU etc policies on the config file for maps etc but nothing at a method level.
I can't go with the LRU/LFU etc eviction policies as I intend to reload the entire table data every x hrs or x days.
Kindly help or point me to any such implementation or docs etc.
Spring #Cacheable doesn't support this kind of policies at method level. See for example the code for CacheableOperation.
If you are using hazelcast as your cache provider for spring, you can explicitly evict elements or load datas by using the corresponding IMap from your HazelcastInstance.

Is there a feature in Java Spring Cache to recalculate value assync if ttl expired?

Is there a way to configure the cache behavior like async recalculation cache if TTL?
As a query might be too heavy - I would prefer to return a value from cache, but I also want to trigger recalculation, until the value has been recalculated - return it from cache.
Spring provides an abstraction over existing caching libraries like ehcache. So you should have a look at your caching provider. For example in EhCache you can start with UpdatingSelfPopulatingCache - but it is blocking.

Resources