Let's say I start with a single CacheManager like this:
private static CacheManager manager = CacheManager.create();
When I call getCache or addCache do I need to synchronize on the CacheManager first?
When I call put or get on the Cache, should I synchronize on the Cache first?
These docs imply that you have to synchronize on the CacheManager instance, as, for example, addCache throws an exception if the given cache "already exists" in the cache.
Related
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
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.
I've seen lots of examples of using things like Netflix's #HystrixCommand on service methods, but is it possible to enable a circuit breaker when using one or more CacheManager instances? In other words, bypass the Spring cache if the underlying service is unavailable or unstable over time.
Specifically, in our usage of Spring Cache, we simply have a method annotated with #Cacheable. Typically with a circuit breaker you specify a fallback, but our fallback is "Dont' use the cache, just execute our method normally". How would we implement that with annotations? Do we need to refactor our code to separate the CacheManager calls?
I think you should write a custom cache manager and write the circuit breaker logic there.
#Component
public CacheManager CustomCacheManager implements CacheManager {
#Override
public Cache getCache(String s) {
return null;
}
#Override
public Collection<String> getCacheNames() {
return null;
}
}
Is it possible to set up Ehcache in such a way that cache will return "expired" data if the underlying CacheLoaderWriter fails (throws an Exception)?
Accordingly to my tests, if a given cache entry is expired, and CacheLoaderWriter fails to load data, cache returns null.
However, on my case, it would be better to return the expired cached data than nothing. What I am doing as a work around is to store the latest successfully loaded data in the CacheLoaderWriter, so if a new execution of the load fails, I return this copy. Anyway, think that would be more "memory efficient" to, somehow, return the data that it's already in the cache, instead of keeping another copy in the CacheLoaderWriter.
I am using ehcache 3.4.0
CacheManager cacheManager = CacheManagerBuilder.newCacheManagerBuilder()
.withCache("myCacheName",
CacheConfigurationBuilder.newCacheConfigurationBuilder(Integer.class, String.class, ResourcePoolsBuilder.heap(100))
.withExpiry(Expirations.timeToLiveExpiration(Duration.of(4, TimeUnit.HOURS)))
.withLoaderWriter(new TestCacheLoaderWriter()))
.build();
It is not possible. However, unless you are using a copier, it isn't a copy you are keeping in your loader-writer. It's only another reference to the object in cache.
Your request could be filed as an enhancement in the Ehcache issue tracker.
The alternative would be to never expire the data but replacing it from outside when needed.
I am using Ehcache in Hibernate.
How can I notify the cache that the database has changed? How can I invalidate the cached data? How can I programmatically achieve this?
What do exactly mean? There are 2 ways to make a change in the database: inside the app & outside the app
Inside the app you can trigger easily an invalidation. The second one is a harder to solve. Cache elements are stored with a key and based on that key you can delete it.
CacheManager manager = CacheManager.getInstance();
cache = manager.getCache(cacheName);
cache.remove(key);
or
cache.removeAll();
Depending how you configured ehcache of course. But you need to know the name of the cache that holds the object.
This is how you can invalidate all EhCache items without bothering what the individual cache names are:
CacheManager manager = CacheManager.getInstance();
String[] names = manager.getCacheNames();
for (String name : names)
{
Cache cache = manager.getCache(name);
cache.removeAll();
}
Try:
CacheManager.create().clearAll()
It will clear all caches in CacheManager.
To clear only one particular cache, get it from cache manager and remove all elements:
CacheManager cacheManager = CacheManager.create();
Ehcache cache = cacheManager.getEhcache(cacheConfiguration.getName());
cache.removeAll();
Set cache expiration so data is refreshed as or more often than the database changes .
Also you can update in cache only elements that change in the database .
In case if you want to remove a specific cache (eg:employeeCache)
#Autowired
CacheManager cacheManager;
place where you want to invalidate cache, use below code
Cache cache = cacheManager.getCache("employeeCache");
cache.removeAll();