JCache CacheResult - creating caches with the default name - java

I'm using JCache annotations with Spring and EhCache. If I use #CacheResult on a method without specifying the cache name (or a #CacheDefaults on the class), then the default cache name resolves to the fully qualified method name. However such a cache is not found, unless explicitly created using the CacheManager. This may be manageable for a few such cache-enabled methods, but not if I have to create 50 different caches manually.
Is there a way to tell Spring (or any JCache implementer) to automatically create caches with the default name, if not found? This would allow me to use #CacheResult on any method without having to go update the cache configuration every time.

Is there a way to tell Spring (or any JCache implementer) to automatically create caches with the default name, if not found?
This problem is covered in cache2k. You can configure cache2k to use a default configuration, if the requested cache name is not known.
Here is an example XML configuration for this scenario, which is placed at /cache2k.xml in the class path:
<cache2k xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance'
xmlns='https://cache2k.org/schema/v1.x'
xsi:schemaLocation="https://cache2k.org/schema/v1.x https://cache2k.org/schema/cache2k-core-v1.x.xsd">
<version>1.0</version>
<ignoreMissingCacheConfiguration>true</ignoreMissingCacheConfiguration>
<defaults>
<!-- default settings for every cache -->
<cache>
<entryCapacity>100_000</entryCapacity>
<expireAfterWrite>5m</expireAfterWrite>
</cache>
</defaults>
<templates>
<cache>
<name>shortExpiry</name>
<expireAfterWrite>5m</expireAfterWrite>
</cache>
<cache>
<name>lessResilient</name>
<resilienceDuration>1m</resilienceDuration>
</cache>
</templates>
<caches>
<cache>
<name>products</name>
<entryCapacity>10_000</entryCapacity>
<include>shortExpiry,lessResilient</include>
</cache>
<cache>
<name>users</name>
<entryCapacity>1000</entryCapacity>
<include>shortExpiry</include>
</cache>
</caches>
</cache2k>
By default, according to this configuration, a cache without a specific configuration entry will get 100K entry limit and 5 minutes expiry. The configuration may be suitable for the trails in production. Later on, you, or a system operator, can check what caches are existing and their performance via JMX and then add a more specific cache configuration as soon as there is need. If you have many caches, the configuration has a template mechanism to avoid repetition.
It may happen that caches need a specific configuration upfront and some don't. I suggest separating them into different cache managers.
If you need an in process cache, cache2k is a a good alternative to EHCache. It is way faster and more memory efficient.
You can use cache2k via JCache, however there is also direct Spring framework support. See the User Guide Spring Framework Section for details.
I am the author of cache2k and not a heavy Spring user at the moment, however, I am happy to help if there are any issues.

Related

How to test inmemory caching using Caffeine cache manager? (i.e. getting the count of entries in cache after caching, cache evict)

I want to get the details of entries in my custom cache after caching data or eviction of data.
I tried using actuator dependency to get 'actuator/metrics' path to get details but I'm getting empty tomcat server cache. There is no sign of my custom cache say myCache (the name which I passed into #Cacheable annotation value argument).
You tagged your question with Caffeine and Spring Boot, so I assume you use those two products.
If you use a recent Spring Boot and Caffeine, statistics will be automatically available at actuator/caches. If not, double check you have the needed libraries on your classpath and no configuration that enables another cache or disables caching at all, like spring.cache.type=none.
If you don't use Spring Boot, but just Spring, you need to add a CacheManager to your configuration, otherwise Spring defaults to the ConcurrentHashMap which does not have cache statistics.

How to disable Spring/Hibernate/Hazelcast joint cache?

We use Hazelcast as our Hibernate 2nd level cache manager and we have some configurations for it in our Spring context files. Our code is also instrumented by Spring #Cacheable annotations (for business level cache) and we configured it to use Hazelcast. The problem is that in development environment we have multiple database instances and sometimes we should switch our application between them. Each time we switch to another database we should also restart the Hazelcast to be filled with new data and it is a REALY annoying work :(( This is more annoying when we need to have multiple instances of our application up on different databases! so we need also multiple instances of Hazelcast!!
As our code is tightly coupled with cache stuffs, it is so hard to remove cache configurations from the code for new instances. Is there any way to tell Hazelcast, Spring and Hibernate do not use/fill cache in presence of our configurations?
You should be able to turn off caching with the Spring and Hibernate configurations. In case of Spring Boot, you can do it by setting the following properties in your application-dev.properties:
spring.cache.type=NONE
spring.jpa.properties.hibernate.cache.use_second_level_cache=false

hazelcast : changing configuration programatically doesnt work

I am unable to configure/change the Map(declared as part of hazelcast config in spring) properties after hazelcast instance start up. I am using hazelcast integrated with spring as hibernate second level cache. I am trying to configure the properties of map (like TTL) in an init method (PostConstruct annotated) which is called during spring bean initialization.
There is not enough Documentation , if there is please guide me to it.
Mean while i went through this post and found this Hazelcast MapStoreConfig ignored
But how does the management center changes the config, will it recreate a new instance again ?
Is hazelcast Instance light weight unlike session factory ? i assume not,
please share your thoughts
This is not yet supported. JCache is the only on-the-fly configuration data structure at the moment.
However you'll most probably be able to destroy a proxy (DistributedObject like IMap, IQueue, ...), reconfigure it and recreate it. Anyhow at the time of recreation you must make sure that every node sees the same configuration, for example by storing the configuration itself inside an IMap or something like that. You'll have to do some wrapping on your own.
PS: This is not officially supported and an implementation detail that might change at later versions!
PPS: This feature is on the roadmap for quite some time but didn't made it into a release version yet, it however is still expected to have full support at some time in the future.

Hazelcast cache implementation in my application

Below is my scenario from Application perspective.
We have 2 applications (.war) files will be running in a same instance of Application server (mostly Tomcat 8), In production we may deploy App1 on 100 servers and App2 only on 50 server out of those 100 (The App2 does not need to be distributed so much)
Now this 2 applications (.war) depends on a common custom jar (some utility classes)
I am planning to use Jcache API and hazelcast implementation in our apps. I have added following dependency in my pom.xml
<!-- JSR 107 JCache -->
<dependency>
<groupId>javax.cache</groupId>
<artifactId>cache-api</artifactId>
<version>1.0.0</version>
</dependency>
<!-- Hazelcast dependency -->
<dependency>
<groupId>com.hazelcast</groupId>
<artifactId>hazelcast</artifactId>
<version>3.4</version>
</dependency>
Plan is to write a utility CacheManager in this common custom jar which will be shared by App1 and App2.
I am planning to use only the hazelcast server provider as I am doing in-memory cluster i.e. the caching will be in application memory.
Below is the snippet of my code.
public class PPCacheManager {
// Loads the default CacheProvider (HazelCast) from hazelcast.xml which is
// in classpath
private static CachingProvider defaultCachingProvider = Caching.getCachingProvider(); //
// Loads the default CacheManager from hazelcast.xml which is in classpath
private static CacheManager defaultCacheManager = defaultCachingProvider.getCacheManager();
// Some more code goes here...
My hazelast.xml
<hazelcast xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.hazelcast.com/schema/config
http://www.hazelcast.com/schema/config/hazelcast-config-3.4.xsd"
xmlns="http://www.hazelcast.com/schema/config">
<cache name="commonClientCache">
<key-type class-name="java.lang.String"></key-type>
<value-type class-name="java.lang.Object"></value-type>
<statistics-enabled>true</statistics-enabled>
<management-enabled>true</management-enabled>
<read-through>true</read-through>
</cache>
</hazelcast>
Now I have several question around this approach.
Is this a good way to implement the in memory caching (currently we are not looking for cluster caching), should this code be in the common custom jar or somewhere else?
There is some master data from DB which I am planning to load (both applications need this data) so not sure how and where I should load this data into memory. Note: I do not want to do lazy loading; I want to load this master data very first.
Where should I add the cache shutdown code to avoid memory leak issues, as this cache is shared by both the applications.
Update
Also by implementing this approach will I have 2 copies of cache each for application or a single copy will be shared across both?
I have already implemented this approach in my application and from Hazelcast management console I can see that there is only 1 cache is created but it says GET is executed on this cache twice.
Hazelcast is the perfect solution for what you are trying to do. Definitely no lazy loading. You don't need anything like that if you have shared memory.
As far, as how many instances you'd have inside one (Tomcat) JVM, you'd have two if you instantiate Hazelcast twice. It'd autoincrement the port. However both will belong to the same cluster (you call "cache"), as long as the cluster name is the same. So other than looking a little silly (sharding on a single JVM), you are fine. To avoid it, you can configure one of the wars to instantiate a HazelcastClient. The utility jar can be the same. It should all be in some e.g. Spring config - which every war would have its own copy of. Or you can put that config into two external directories and add to the catalina classpath.
The shutdown code belongs to the same place you instantiated Hazelcast i.e. your two wars will have two shutdown calls. You can do it in Spring's destroy() of any of your high-level config (or autowired) beans or put it in the Web App session listener.

How I can disable the second-level cache of some certain entities in Hibernate without changing annotations

I'm using Hibernate second level cache in my application, for certain business reason I can't change the entity annotation any more.
In my project, apart from changing the Database from Hibernate, there exist also other native SQL that do not go through Hibernate. Therefore, the Hibernate second-level cache data could be stale after database being updated from native SQL. That's why I want to disable the second-level cache for certain entities (programmatically or other way than changing annotation).
Thanks in advance!
WARNING: As Jens Schauder noted, it is impossible to configure Ehcache to store 0 elements in memory by setting maxElementsInMemory="0" as it effectively causes opposite effect - sets unlimited size for the cache. This behaviour is not mentioned on the Hibernate Caching page but is documented on Cache Configuration page.
I have quickly reviewed the documentation and haven't found alternative approach yet. I am unable to delete this answer by myself. :-(
My original suggestion:
You can configure the implementation provider of second level cache to short TTL times and/or to store 0 entries of particular entity type.
E.g. if you are using the Ehcache, you can configure it in ehcache.xml:*
<cache
name="com.problematic.cache.EntityName"
maxElementsInMemory="0" <<== this should effectively disable caching for EntityName
overflowToDisk="false" <<== Do not overflow any entries to disk
/>
See Hibernate Caching in Ehcache documentation.
In Terracotta 3.1 and above, you can enable/disable Hibernate 2nd Level Caches on a per region basis, both in the configuration (statically) and at runtime, using the Terracotta Developer Console.
You can also monitor in realtime statistics about the cache and Hibernate, for individual nodes in a cluster or cluster-wide.
Terracotta is open source. For more details, check out Terracotta for Hibernate.

Categories