We host a web application that serves a high-traffic website. This web app is backed by a content management system (CMS), and we use Ehcache to cache the data from this CMS.
The problem: How to return expired data if you’re unable to retrieve new data?
Ehcache holds cache elements in-memory/on-disk store until the defined expiration time. We’ve defined the time-to-live (TTL) to be 15 minutes (900 seconds) in eh-cache.xml:
<cache name="contentCache"</pre> <pre style="padding-left: 30px;">maxElementsInMemory="1"</pre> <pre style="padding-left: 30px;">maxElementsOnDisk="0"</pre> <pre style="padding-left: 30px;">timeToLiveSeconds="900"</pre> <pre style="padding-left: 30px;">memoryStoreEvictionPolicy="LRU"</pre> <pre style="padding-left: 30px;">eternal="false"</pre> <pre style="padding-left: 30px;">overflowToDisk="false"/>
Once an element in the cache expires, Ehcache does not automatically remove this element because it has exceeded its timeToLiveSeconds and timeToIdleSeconds attribute values.
Excerpt from the Ehcache documentation:
timeToIdleSeconds: Sets the time to idle for an element before it expires. In other words, the maximum amount of time between accesses before an element expires is only used if the element is not eternal. Optional attribute — a value of 0 means that an element can idle for infinity. The default value is 0.
timeToLiveSeconds: Sets the time to live for an element before it expires. In other words, the maximum time between creation time and when an element expires is only used if the element is not eternal. Optional attribute — a value of 0 means that and element can live for infinity. The default value is 0.
An expired element remains in the cache until either:
- Ehcache#evictExpiredElements() is called
- Or, a get or remove operation is called on the cache that passes in the same key as the expired element
If either of the above actions is performed, Ehcache immediately nullifies the expired element in the cache. The application would then be responsible for fetching the data for a new replacement element and repopulating the cache with this element.
In instances where the data for this new element cannot be retrieved, we want to return the stale content from the old expired element. Unfortunately, there seems to be no good extension point in Ehcache to make this change. Therefore, I decided to take control of Ehcache’s refresh mechanism to implement this desired behavior:
- Ehcache can make a cache eternal so the elements in the cache never expire. To make a cache eternal, set the following property for the cache in ehcache.xml:
eternal="true"
- Additionally, maintain timeToLive configuration for the cache in the code. Each time you retrieve an element from the cache, check if the cached element has exceeded its timeToLive period. If the cache expired, refresh the cache element.
Update the statistics on the cache element in order to minimize the number of threads attempting to refresh the cache. Then, try to retrieve the latest data for this cache element. If this fails, the stale data continues to be returned since the attempt to retrieve the latest data doesn’t overwrite the stale data.
Element element = getElementFromCache(cache, cacheEntryKey); // If the element is present in the cache, execute the refresh logic below if (element != null) { //condition for refresh if (isElementInCacheExpired(cacheName, element)) { // logic for refreshing the cache //The element.updateUpdateStatistics() method sets the last access time to now without updating the hit count. This prevents other threads from entering the refresh logic. element.updateUpdateStatistics(); try { // try to retrieve latest data for this current cache element Object returnObj = getDataForCacheElement(cacheEntryKey); If(returnObj is not null){ Element element = new Element(cacheEntryKey, returnObj); cache.replace(element); } } catch (Exception e) { System.out.println("Error creating replacement element for key” +cacheEntryKey); } } else { // retrieve data for this new cache element Object returnObj = getDataForCacheElement(cacheEntryKey); // take the value returned and store in cache Element element = new Element(cacheEntryKey, returnObj); cache.put(element); } //return element from cache element = getElementFromCache(blockingCache, cacheEntryKey); return element
Cache expiration logic
I compare the current time with (element.getLatestOfCreationAndUpdateTime() + TimeUtil.toMillis(timeToLive)).
(“timeToLive” is the custom configuration stored for the cache):
function isElementInCacheExpired(String cacheName, Element element){ // The synchronized block prevents multiple threads from checking the expiration condition at the same time. synchronized (element) { long now = System.currentTimeMillis(); return (now > element.getLatestOfCreationAndUpdateTime() + TimeUtil.toMillis(timeToLive)); } }
This is the approach I took to solve this classic Ehcache “Catch-22” problem that included having to return expired data when I am not able to retrieve new data. I would love to hear what other Ehcache users think about my solution. Please let me know what you think in the comments below!
I have exactly the same problem right now(several unnecessary update caches), and by now, this seems the only solution 🙁
@gareve: Thank you for reading my post. We have battled this problem for quite some time and this is the solution that we came up with. However, I feel that it would have been best for EhCache to provide some extension points to the eviction mechanism.
Hi Kusum: I have problem with how ehcache repopulates cache after TTL has expired. Currently in my spring web app i have specified cache in ecache.xml as follows:
I then populate the cache when the app starts. At certain points I need to add/remove items from the cache as the as app is used e.g. during update, save and delete operations. My question is:
1. How is the cache repopulated when TTL = 600 for example has expired?
2. Do you specify a method it needs to execute to populate the cache? If so, where do you do this?
3. How can I implement this on my own?
Your input is greatly appreciated.
Hi
1. Accessing the expired elements will result in them being expired (and removed from the Cache) by the ehcache listener. Your code can populate the cache entry if the entry in the cache returns null.
2. In order to populate the cache , I had a method which checks if the cache.get(cacheEntry) is null then fetch the data and populate the cache.
3. In my implementation, I created a CachingAspect which had the logic mentioned in #2 to populate the cache. Any method that needed caching was aspectized. If you are using Spring-ehcache, I believe there are annotations that can help you too.
Refer:http://ehcache.org/documentation/recipes/spring-annotations
Hi..I m currently working on externalizing ehcache timetolive and timetoidleseconds in default ehcache.xml Below is my ehcache default snipet
Here when i give timeToIdleSeconds=300,the cache is not expiring after 5 mins(which is 300)
I was using jprofiler to test the performance and when i set timeToIdleSeconds and TTL seconds to 300(approx 5mins) ,the time it takes to retrive records is lets say X micro seconds when i rerun the test immediately wiith the TTI and TTL = 300 i see the time to retive records is less than Xmicro seconds .
But when i repeat the same test after 5 mins,the time to retrive the records should be approx or equal to original 1st test case which is X micro seconds but thats not happening
the cache is not invalidated.
i want the cache to be invalidate,How do i do that?In my case i guess cache is not expiring at all?
I do i test what value is loaded at runtime into TTL and TTI?
Hi.. I am trying to do something similar with coherence. I want to use expired values when new values are not fetched. you mention in one of the above comments
“2. In order to populate the cache , I had a method which checks if the cache.get(cacheEntry) is null then fetch the data and populate the cache.”
But how does this method get called. So did you extend any of the ehcache implemtation? With coherence in spring it is a bit more abstracted. All I can see is the cachemanager.createCache
I am using Ehcache 3.0.0 (org.ehcache.*) which does not have these APIs and interfaces E.g Element
Is there a version of this implementation that will work in 3.0 ?
What if the cache is very heavy. Lets say that your Heap space is 10 GB, and your cache is also 10 GB. So when you try to refresh, if you keep the stale data in memory also then you would be storing 20 GB resulting in out of memory. How do you handle this case ??
Your case justifies getting a higher heap space; remember that your app is getting a greater value with this design by being able to use the last known response. It’s a cost vs benefit you have to choose.