Cache Data in IM with Ehcache
Purpose
Ehcache is an open source, standards-based cache that boosts performance, offloads your database, and simplifies scalability. It is the most widely-used Java-based cache because it is robust, proven, full-featured, and integrates with other popular libraries and frameworks. Ehcache scales from in-process caching, all the way to mixed in-process/out-of-process deployments with terabyte-sized caches.
Use Case
Once the ITEM_UPDATE_PR is obtained, take data from it, enrich it with some data taken from another PFX table and upload enriched record to some PX table.
As there can be generated thousands of events per seconds (if data to PR table is uploaded via Excel Client or similarly), there was a need to call backend API to obtain a table for enrichment every time. That is a very expensive operation and so caching can come handy.Â
The entire implementation is mostly in one commit:Â https://bitbucket.org/pricefx/tfg-integration/commits/f3fe205d33b75861a9fae0debcfdabb4d1c98842
Additional commit adds a condition to the cache:Â https://bitbucket.org/pricefx/tfg-integration/commits/09b373f8b0dbd69e8fb91278bd0653969a8edd4e
Additional condition was added due to NoHttpResponseException which is sometimes occurs and then an empty list is returned.
Example Implementation
Add Dependencies to pom.xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
<dependency>
<groupId>javax.cache</groupId>
<artifactId>cache-api</artifactId>
</dependency>
<dependency>
<groupId>org.ehcache</groupId>
<artifactId>ehcache</artifactId>
</dependency>
Add property to properties file
spring.cache.jcache.config=classpath:ehcache.xml
Create ehcache.xml configuration file
Based on this create ehcache.xml file in the classpath (probably directly in the resource folder by default) with this content:
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.ehcache.org/v3"
xmlns:jsr107="http://www.ehcache.org/v3/jsr107"
xsi:schemaLocation="
http://www.ehcache.org/v3 http://www.ehcache.org/schema/ehcache-core-3.0.xsd
http://www.ehcache.org/v3/jsr107 http://www.ehcache.org/schema/ehcache-107-ext-3.0.xsd">
<cache alias="terminalsCache">
<expiry>
<ttl unit="minutes">30</ttl>
</expiry>
<listeners>
<listener>
<class>net.pricefx.integration.startup.config.CacheEventLogger</class>
<event-firing-mode>ASYNCHRONOUS</event-firing-mode>
<event-ordering-mode>UNORDERED</event-ordering-mode>
<events-to-fire-on>CREATED</events-to-fire-on>
<events-to-fire-on>EXPIRED</events-to-fire-on>
</listener>
</listeners>
<resources>
<heap unit="entries">2</heap>
<offheap unit="MB">10</offheap>
</resources>
</cache>
</config>
Above you can note a few things. You can configure ttl as well as persistence to disk, listeners etc. For more configurations check out the ehcache documentation.
Here we use just ttl for 30 minutes. If no event is received within 30 minutes, the table will be evicted from the cache and a new API call will be triggered to populate fresh data. Because we have listener, we will get notified and log such process.
Do not hesitate to change entries and other properties which will fit to your needs. Since we needed to store the entire table (approx. 1500 rows), just 1 entry and 10MB was enough.Â
Create listener for logging cache events
So we have configuration where a listener is mentioned. Let's create it.
CacheEventLogger.java
You can get a better log info with for example:
Enable caching in your application
You have two options. You can put a line in the camel-context.xml or add an annotation to the startup Application class. Adding annotation to the Application is proved to be working on Java 11 and IM 1.1.17. You might try to create a CacheConfig class.
Add annotation-driven into camel-context.xml
Add the following line into the camel-context.xml and its namespace.
camel-context.xml
Â
Create CacheConfig
Then create the cache config Java class:
CacheConfig.java
To explain: Spring’s auto-configuration finds Ehcache’s implementation of JSR-107. However, no caches are created by default. That is why this is needed.
Add EnableCaching annotations to your Application class
Application
Modify service class to enable caching
We are almost at the end. Now you should create your own service which will do the caching of data you want. Here is an example of data which were needed.
TerminalsService.java
In this case the most important is @Cacheable annotation and its parameters:
value – Name of the cache.
key – It can be your value which is the same as the method parameter. For example, this can be customerId or sku etc. It depends on which data you will cache. We are caching the entire table and the only key will be String "terminals", hence we will send such string to the method.
sync – Defines if cache should be synchronized. We want it to be synchronized across of all apache threads.
condition – Expression to decide when to return data from the cache and when not. For example: #terminals > 10. In this case if we would send some terminal ID as a parameter terminals which is below 10, the data will not go from the cache but the method code will run every time. In the opposite case, if we send the number 100 it will try to use a cached value if there is any.
Â
Now let's update our enricher processor class like this:
EnrichPRBean.java
Finally ensure you have @SpringBootApplication annotation in Application.java and you are ready to go.
Log output
Let's see the behavior in real time. We changed ttl to 30s for testing purposes.
main.log
Final Thoughts
Maybe are you asking why we do it like this if we could ask only for records we are interested in using a filter on terminalNameOrigin or terminalNameDestination and return a name accordingly. But if there are some 1500k records in the enrichment table and we will be out of luck, 1500 API calls would be called and cached. It should be faster to fetch the entire table into the memory and then just iterate over it every time to find the proper record.Â
IntegrationManager version 5.8.0