We are using Spring cache for Caching few elements. So whenever user requests same key of element, it goes to cache and check if it is available or not. If it is available it fetches from cache otherwise it executes the method. But before all this I want to implement one more functionality in my cache.
Requirement : On hourly basis my spring cache will check, if any element in the cache exists for more than an hour, it will remove it.
I searched on google but did not find any satisfactory link. Can someone help me or provide me a link for same ?
You need to set the time to live(TTL) for your cache. How you do this depends on your cash provider. A couple examples can be found here:
Can I set a TTL for #Cacheable
#EnableCaching
#Configuration
public class CacheConfiguration implements CachingConfigurer {
#Override
public CacheManager cacheManager() {
ConcurrentMapCacheManager cacheManager = new ConcurrentMapCacheManager() {
#Override
protected Cache createConcurrentMapCache(final String name) {
return new ConcurrentMapCache(name,
CacheBuilder.newBuilder().expireAfterWrite(30, TimeUnit.MINUTES).maximumSize(100).build().asMap(), false);
}
};
return cacheManager;
}
#Override
public KeyGenerator keyGenerator() {
return new DefaultKeyGenerator();
}
}
Related
I'm trying to configure Spring CacheManager with Hazelcast. Also, I want to configure Hazelcast's Near Cache so I can retrieve the (already deserialized) instance of my cached object.
Here is my configuration
#Bean
public HazelcastInstance hazelcastConfig() {
val config = new Config().setInstanceName("instance");
val serializationConfig = config.getSerializationConfig();
addCacheConfig(config, "USERS")
serializationConfig.addSerializerConfig(new SerializerConfig()
.setImplementation(getSerializer())
.setTypeClass(User.class)
return Hazelcast.newHazelcastInstance(config);
}
#Bean
public CacheManager cacheManager(HazelcastInstance hazelcastInstance) {
return new HazelcastCacheManager(hazelcastInstance);
}
#Bean
public PlatformTransactionManager chainedTransactionManager(PlatformTransactionManager jpaTransactionManager, HazelcastInstance hazelcastInstance) {
return new ChainedTransactionManager(
jpaTransactionManager,
new HazelcastTransactionManager(hazelcastInstance)
);
}
// Configure Near Cache
private void addCacheConfig(Config config, String cacheName) {
val nearCacheConfig = new NearCacheConfig()
.setInMemoryFormat(OBJECT)
.setCacheLocalEntries(true)
.setInvalidateOnChange(false)
.setTimeToLiveSeconds(hazelcastProperties.getTimeToLiveSeconds())
.setEvictionConfig(new EvictionConfig()
.setMaxSizePolicy(ENTRY_COUNT)
.setEvictionPolicy(EvictionPolicy.LRU)
.setSize(hazelcastProperties.getMaxEntriesSize()));
config.getMapConfig(cacheName)
.setInMemoryFormat(BINARY)
.setNearCacheConfig(nearCacheConfig);
}
Saving and retrieving from the Cache is working fine, but my object is deserialized every time I have a cache hit. I want to avoid this deserialization time using a NearCache, but it doesn´t work. I also tried BINARY memory format.
Is this possible with Hazelcast? Or is this deserialization always executed even if I have a NearCache?
Thanks
So after a few changes, it is working now. Here is my conclusion:
So in order to have NearCache working with Spring Cache, all your cached objects should be Immutable. This means final classes and final fields. Also, they all should extend the Serializable interface.
I'm using Caffeine Cache library for Spring Cache. Is there a way to get all the cached keys?
My current application works on a near-realtime data, with the flow as :
In the Cache Updater Thread(which runs at a fixed interval, irrespective of the user request), I need to get all the keys currently in the Cache, fetch their latest data from Db & then use #CachePut to update the cache.
Yo can inject CacheManager and obtain native cache from it.
#AllArgsConstructor
class Test {
private CacheManager cacheManager;
Set<Object> keys(String cacheName){
CaffeineCache caffeineCache = (CaffeineCache) cacheManager.getCache(cacheName);
com.github.benmanes.caffeine.cache.Cache<Object, Object> nativeCache = caffeineCache.getNativeCache();
return nativeCache.asMap().keySet();
}
}
Of course you should add some class casting checks.
You can return keyset by using asMap().keySet() method as follows.
import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;
class Test{
private Cache<String,String> testCache;
Test(){
testCache = Caffeine.newBuilder().expireAfterWrite( 3000, TimeUnit.SECONDS).build();
}
// return keys as a set
public Set<String> getCacheKeySet(){
return testCache.asMap().keySet();
}
I try to create folders in the redis cache of my project.
I want something similar to what spring automatically do with spring session.
I have a lot of "DisplayItem" entries and I want to store them in a "displayitem" folder.
Here is my code :
#Cacheable(value = "displayItem", cacheManager = "longLifeCacheManager")
public DisplayItem getDisplayItem(String displayItemCode) {
// Do a lot of things
}
#Cacheable(cacheManager = "mediumLifeCacheManager", value = "preferences:userPreferences", key = "#zenithName")
public UserPreferences getUserPreferencesByZenithName(String zenithName) {
// Do something
}
If I replace the value by "foldername:displayItem", it create a folder, but only for the keys (like I did for the preferences in the screenshot).
I don't find how to store all the values in this same folder.
How can I do this ?
I finally found a solution.
In the configuration of the CacheManager, I need to set UserPrefix with True.
#Bean(name = "mediumLifeCacheManager")
public CacheManager mediumLifeCacheManager(RedisTemplate redisTemplate) {
RedisCacheManager cacheManager = new RedisCacheManager(redisTemplate);
cacheManager.setDefaultExpiration(redisExpirationMedium);
cacheManager.setUsePrefix(true);
return cacheManager;
}
Now this works like I want.
I have a problem when trying to get back a Guava cache from a cache manager, instead I get a Spring Cache.
This is the bean in my SpringConfig file :
#Bean
public CacheManager cacheManager() {
ConcurrentMapCacheManager cacheManager = new ConcurrentMapCacheManager() {
#Override
protected Cache createConcurrentMapCache(final String name) {
return new ConcurrentMapCache(name, CacheBuilder.newBuilder().expireAfterWrite(1440, TimeUnit.MINUTES)
.maximumSize(100).recordStats().build().asMap(), false); }
};
return cacheManager;
}
And then I'm able to use the #Cacheable :
#Cacheable(value = "myCache")
public void myCacheMethod(String key){
// call a web service
}
Everything works fine, but I can't get the cache Guava object created by the CacheBuilder in order to call the stats() method.
This is how I get the cache :
Cache myCache = cacheManager.getCache("myCache");
ValueWrapper wrapper = myCache.get("key");
WebServiceType myCachedObject= (WebServiceType) wrapper.get();
The last cache is a Spring cache, and I get an error if I cast it to Guava cache.
Is this possible ? Or did I do something wrong ?
In my application, I have a scenario where I have to refresh cache each 24hrs.
I'm expecting database downtime so I need to implement a use case to refresh cache after 24hrs only if the database is up running.
I'm using spring-ehache and I did implement simple cache to refresh for each 24 hrs, but unable to get my head around to make the retention possible on database downtime .
Conceptually you could split the scheduling and cache eviction into two modules and only clear your cache if certain condition (in this case, database's healthcheck returns true) is met:
SomeCachedService.java:
class SomeCachedService {
#Autowired
private YourDao dao;
#Cacheable("your-cache")
public YourData getData() {
return dao.queryForData();
}
#CacheEvict("your-cache")
public void evictCache() {
// no body needed
}
}
CacheMonitor.java
class CacheMonitor {
#Autowired
private SomeCachedService service;
#Autowired
private YourDao dao;
#Scheduled(fixedDelay = TimeUnit.DAYS.toMillis(1))
public conditionallyClearCache() {
if (dao.isDatabaseUp()) {
service.evictCache();
}
}
}
Ehcache also allows you to create a custom eviction algorithm but the documentation doesn't seem too helpful in this case.