Spring Boot #Cachable - how to find out expire datetime at runtime? - spring

When using #Cachable in Spring, is there any way to find out at runtime if the next method call would be a cache hit or cache miss? Or, at what datetime the cache expires?
The background is, it would be nice to have a scheduled job which refreshes caches just before they expire. Also, this could allow us to find out if caches are expired and show the user a message that the system is now refreshing the caches. "please hold on, we are refreshing your caches for XY" to let the user know what's going on. - Please note we use the Cacheable feature to cache method calls which collect data from multiple calls to a 3rd party system which take up to several minutes.

There is no such thing in the Spring Cache abstraction.
As the data collection takes minutes, and the users might need that last value while data is being collected, you can't wait for the cache eviction to collect new data.
One solution would be to add an #Scheduled task that populates the cache calling a method with the #CachePut.
CachedService.java
#CachePut(cacheNames="book", key="#isbn")
public Book updateBook(int id, Book book) {
// Left blank
}
Config.java
#Scheduled(fixedRate = 60000)
public void reportCurrentTime() {
log.info("refreshing cache");
... long data collection ...
// Call method that has #CachePut
service.updateBook(bookId, book);
log.info("cache refreshed");
}
The example shows 60000ms (1 minute), it updates the cache every minute. You have to calculate it so it is less time than the Spring Cache expiration time. i.e. (expire time - request time)

Related

Spring Boot Caching auto refresh using #PostConstruct

I currently have a Spring Boot based application where there is no active cache. Our application is heavily dependent on key-value configurations which we maintain in an Oracle DB. Currently, without cache, each time I want to get any value from that table, it is a database call. This is, expectedly causing a lot of overhead due to high number of transactions to the DB. Hence, the need for cache arrived.
On searching for caching solutions for SpringBoot, I mostly found links where we are caching object while any CRUD operation is performed via the application code itself, using annotations like #Cacheable, #CachePut, #CacheEvict, etc. but this is not applicable for me. I have a master data of key-value pairs in the DB, any change needs approvals and hence the access is not directly provided to the user, it is made once approved directly in the DB.
I want to have these said key-values to be loaded at startup time and kept in the memory, so I tried to implement the same using #PostConstruct and ConcurrentHashMap class, something like this:
public ConcurrentHashMap<String, String> cacheMap = new ConcurrentHashMap<>();
#PostConstruct
public void initialiseCacheMap() {
List<MyEntity> list = myRepository.findAll();
for(int i = 0; i < list.size(); i++) {
cacheMap.put(list.get(i).getKey(), list.get(i).getValue());
}
}
In my service class, whenever I want to get something, I am first checking if the data is available in the map, if not I am checking the DB.
My purpose is getting fulfilled and I am able to drastically improve the performance of the application. A certain set of transactions were earlier taking 6.28 seconds to complete, which are now completed in mere 562 milliseconds! however, there is just one problem which I am not able to figure out:
#PostConstruct is called once by Spring, on startup, post dependency injection. Which means, I have no means to re-trigger the cache build without restart or application downtime, this is not acceptable unfortunately. Further, as of now, I do not have the liberty to use any existing caching frameworks or libraries like ehcache or Redis.
How can I achieve periodic refreshing of this cache (let's say every 30 minutes?) with only plain old Java/Spring classes/libraries?
Thanks in advance for any ideas!
You can do this several ways, but how you can also achieve this is by doing something in the direction of:
private const val everyThrityMinute = "0 0/30 * * * ?"
#Component
class TheAmazingPreloader {
#Scheduled(cron = everyThrityMinute)
#EventListener(ApplicationReadyEvent::class)
fun refreshCachedEntries() {
// the preloading happens here
}
}
Then you have the preloading bits when the application has started, and also the refreshing mechanism in place that triggers, say, every 30 minutes.
You will require to add the annotation on some #Configuration-class or the #SpringBootApplication-class:
#EnableScheduling

Kotlin + Spring Boot - Add TTL to your map

So I have an empty map referenced like:
private var labelsForGroupId: Map<GroupId, Label> = emptyMap()
to lower the amount of calls through network api. After first call I cache the response to the map.
However, I would love to add TTL to that map, (for example, every hour it should be empty again). I am quite new to Kotlin, so wondering what would be the best approach here with some examples?
Instead of using a Map, you could use Guava Cache. It works like a Map (key-value) and have expiration policies.
Expiration by time example:
CacheBuilder.newBuilder()
.expireAfterAccess(200, TimeUnit.MILLISECONDS)
.build(loader);
If you are not interested in caches at all, then you could try to setup a Coroutine with a ScheduledExecutorService as Dispatcher. I never did this before but is a way out. Take a look at the Executors documentation - Coroutine context and dispatchers
If the given [ExecutorService] is an instance of
[ScheduledExecutorService], then all time-related * coroutine
operations such as [delay], [withTimeout] and time-based [Flow]
operators will be scheduled * on this executor using
[schedule][ScheduledExecutorService.schedule] method. If the
corresponding * coroutine is cancelled, [ScheduledFuture.cancel] will
be invoked on the corresponding future.

JPA Spring Refresh issue

I'm working with SpringBoot 2.3.5 with Hibernate and Hikari Pool.
I've an entity, lets call it A, this entity has an "execution status counter" long field, incremented by an Async method, at the end of its executions, so I can split the execution on multiple threads, having a progress counter.
The increment is performed like this:
#Transactional(propagation = Propagation.REQUIRES_NEW)
#Lock(LockModeType.PESSIMISTIC_WRITE)
default void incrementStatusCount(String lotto, Long progressivo) {
A a = findById(...).get();
a.setStatoLdSN(a.getStatoLdSN() != null ? a.getStatoLdSN()+1:1L);
saveAndFlush(a);
}
And it works fine, so externally, using a DB Tool, I can see the counter update.
Now, at the end of my whole execution, I change the string status of my whole execution, so I load the entity, but the entity is already in the cache, so I call a refresh on the entity manager, I can see the query performad and also the retrieved values from the Hibernate log....and my counter 0.
Now, Oracle default isolation leve is READ_COMMITTED (and I verified it on the connection), and my incremented value is committed because I saw it using the DB client, no?
So why JPA, not even calling refresh is loading the right value?

Spring Cache Hit Flag/Indicator

I am using Spring Cache to cache some objects through #Cacheable. However, 1 of the requirement requires me to be able to know if the returned object was from the Cache Hit or standard call. Is there any flag or indicator thats gets set i can use to check this ?
I have seen past questions regarding cache hits being logged whenever there are cache hits but that is not really useful for my situation. I am currently using Spring Cache with the Simple Provider and am open to using any external Cache Managers that is able to do this.
Yes, we can know whether it is a cache hit or a cache miss(a direct call to REST call or a database call) using a flag.
Using #Cacheable, it always first checks in the cache, before it executes the method, if found in cache, it will skip the method execution, where as #CachePut works slightly different, where it will executes the adivised method & updates the cache, so it will miss the cache always.
For example:
private volatile boolean cacheMiss = false;
public boolean isCacheMiss(){
boolean cacheMiss = this.cacheMiss;
this.cacheMiss = false; //resetting for next read
return cacheMiss;
}
protected void setCacheMiss(){
this.cacheMiss = true;
}
#Cacheable("Quotes")
public Quote requestQuote(Long id) {
setCacheMiss();
//REST CALL HERE
return requestQuote(ID_BASED_QUOTE_SERVICE_URL,
Collections.singletonMap("id", id));
}
cacheMiss variable gives the status, whether it is from cache or not.
Here is it discussed Spring Caching with GemFire, the underlying caching provider is Pivotal GemFire. You can use any such caching providers.

MVC3 - using AsyncController to prepopulate ObjectCache from database

I have a form that searches via AJAX against two different data sources. The data is relatively small but the speed at which it returns is slow.
I built a cache layer to store the full result after the first query... however, I would like to prime the cache with data before the user executes the search.
Should I be looking at an AsyncController to do this? Any recommendations?
My desired behavior is (updated):
User requests any ActionABC of some controller (not necessarily the search action)
Server-side, that action checks the cache and asynchronously requests data if empty
ActionABC returns requested view while cache continues to populate on server
If the user subsequently performs a search while cache being populated, their request waits until cache populate is complete otherwise cache data is immediately available
You would get a benefit from an async controller only if you could perform the 2 searches in parallel.
In this case your logic could be:
If the data is found in the cache return the result immediately.
If the data is not found in the cache launch 2 parallel async tasks to perform the search.
Synchronize those tasks so that once they both finish you populate the cache and return the final result.
Also if you are going the AsyncController route make sure you use async ADO.NET API to query your database (command.BeginExecuteResult/command.EndExecuteResult) so that you can take full advantage of I/O Completion ports and do not block worker threads during the execution of the expensive search operations.
I ended up not having to use AsyncControllers.
I used the Task Factory to "fire and forget" a call to load the data initially upon any call to the controller.
Task.Factory.StartNew(() => { var x = GetData(); });
Inside "GetData" call I used LOCK to force subsequent calls to wait until cache was populated (addresses #4)
private static object ThisLock = new object();
protected MyData GetData()
{
if(<MyData in cache>)
return MyData from cache;
lock(ThisLock)
{
// just entered lock, see if cache was set by previous blocking thread
if(MyData in cache>)
return data from cache;
... load MyData from database ...
... save MyData to cache ...
return MyData from cache;
}
}

Resources