Caffeine Cache in Spring Boot Cache : Get all cached keys - java

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();
}

Related

Redis cache implementation using Spring boot with hashing

I am trying to create and delete a cache using RedisCacheManager with spring-boot and want to use HSET programmatically but am unable to do it. I am able to do it as a simple SET but not as HSET.
This is the bean that I have created.
#Bean
public RedisCacheManager cacheManager(RedisConnectionFactory connectionFactory) {
RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig() //
.entryTtl(Duration.ofHours(1)) //
.disableCachingNullValues();
return RedisCacheManager.builder(connectionFactory) //
.cacheDefaults(config) //
.build();
And even made the class where I am making the call as #RedisHash but no luck.
#Service
#Slf4j
#RedisHash(value = "CURRENT_CALLS")
public class CacheCleanupService implements Serializable {
#CacheEvict(value = "CURRENT_CALLS" ,key = "(#cacheKey)")
public void redisCacheNumberCleanup(String cacheKey) {
log.info("Key CLEANUP from the cache: {}", cacheKey);
}
#Cacheable(value = "CURRENT_CALLS", key = "(#cacheKey)")
public String redisCacheNumberStore(String cacheKey) {
log.info("Key Add from the cache: {}", cacheKey);
return cacheKey;
}
}
The o/p I am getting is this when calling these above methods from another #Service class.
127.0.0.1:6379> keys CURRENT_CALLS:*
1) "CURRENT_CALLS::+15109100689:+15134631989"
2) "CURRENT_CALLS::+15109100648:+15134631989"
3) "CURRENT_CALLS::+15109100688:+15134631988"
127.0.0.1:6379> get "CURRENT_CALLS::+15109100648:+15134631989"
"+15109100648:+15134631989"
However, I want the o/p like this
127.0.0.1:6379> keys CURRENT_CALLS
1) "CURRENT_CALLS"
127.0.0.1:6379> hgetall "CURRENT_CALLS"
1) "+15109100648:+15134631989"
2) "1"
3) "+15109100688:+15134631988"
4) "2"
5) "+15109100689:+15134631989"
6) "3"
7) "+17326667726:+17722915819"
8) "4"
How to achieve this through spring-boot annotations.
It seems not likely to play around Redis Hashes data type using the annotation based way.
In the Cache Abstraction section of Spring Framework documentation, all the usages are based on basic cache operation (get key and set key value, if in the context of Redis)
As you mentioned that you are marking your service class with #RedisHash, though,
#RedisHash is used on entities, i.e., the models to be persisted in Redis.
See also
another SO question Spring Boot Caching with Redis and Store value as Redis Hash
tut on developer.redis.com Caching REST Services with Redis

Caffeine cache does not prevent from database queries

I've implemented basic set up of Caffeine cache in Spring Boot app. Below you will find configuration and service method annotated with #Cachable. Unfortunately every call for this method generates database query and new input to cache (with same key):
Debug result with cache content
#Cacheable(cacheNames = {"pic"})
public PictureModel loadPictureById(String id) {
var loadedInstance = pictureRepo.findById(id).orElseThrow(() -> new CustomNotFoundException(PictureModel.class));
loadedInstance.setBody(pictureCompressor.decompressBytes(loadedInstance.getBody()));
return loadedInstance;
}
Configuration:
#EnableCaching
#Configuration
public class CaffeineConfig {
#Bean
public CacheManager cacheManager() {
CaffeineCacheManager cacheManager = new CaffeineCacheManager("pic");
cacheManager.setCaffeine(caffeineCacheBuilder());
return cacheManager;
}
Caffeine <Object, Object> caffeineCacheBuilder() {
return Caffeine.newBuilder()
.initialCapacity(100)
.maximumSize(500)
.expireAfterAccess(10, TimeUnit.MINUTES)
.weakKeys()
.recordStats();
}
}
Database queries after three hits:
Hibernate:
select
picturemod0_.id as id1_10_0_,
picturemod0_.account_id as account_5_10_0_,
picturemod0_.body as body2_10_0_,
picturemod0_.name as name3_10_0_,
picturemod0_.public_platform_id as public_p6_10_0_,
picturemod0_.service_type_id as service_7_10_0_,
picturemod0_.type as type4_10_0_
from
image picturemod0_
where
picturemod0_.id=?
Hibernate:
select
picturemod0_.id as id1_10_0_,
picturemod0_.account_id as account_5_10_0_,
picturemod0_.body as body2_10_0_,
picturemod0_.name as name3_10_0_,
picturemod0_.public_platform_id as public_p6_10_0_,
picturemod0_.service_type_id as service_7_10_0_,
picturemod0_.type as type4_10_0_
from
image picturemod0_
where
picturemod0_.id=?
Hibernate:
select
picturemod0_.id as id1_10_0_,
picturemod0_.account_id as account_5_10_0_,
picturemod0_.body as body2_10_0_,
picturemod0_.name as name3_10_0_,
picturemod0_.public_platform_id as public_p6_10_0_,
picturemod0_.service_type_id as service_7_10_0_,
picturemod0_.type as type4_10_0_
from
image picturemod0_
where
picturemod0_.id=?
I would like to achieve one database query and other hits to be served by cache.
Remove weakKeys() from your CaffeineConfig.
From weakKeys() documentation,
Warning: when this method is used, the resulting cache will use identity (==) comparison to determine equality of keys.
In your config , you are computing keys as Object but weakKeys() is comparing them with ==, so keys are matched as not eqaul and cache miss is happening.

Using Spring Cache with Hazelcast Near Cache

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.

How to invalidate an entry from Spring cache based on hourly basis ?

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();
}
}

Spring 3.2 + Guava : Get the Guava cache from cache Manager

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 ?

Categories