The fix for bug
CACHE-170 shipped within version 2.1.1 misses a mechanism to remove the UpdateStateEntry instances when an entry is removed either:
- explicitly throught a removeEntry() method method call
- implicitly when the cache is full and one of the mechanism to handle full cache condition triggers (such as LRU algorithm)
As a result, if the cache is used at its full capacity (i.e. the full cache condition is often reached, and the amount of keys is larger than the maximum number of items in the cache), then the Cache.updateStates Map might grow to the total number of keys requested to be cached (even if no corresponding content is stored in the cache).
For application with a large range of keys (or with high key volatility) this might result into high memory usage which can look like a memory leak.
As discussed in details in bug
CACHE-170, the possible fixes for this regression are:
1- handle the case of entries removed epxlicitly and implicity. The the implicit case, this would require to modify the AbstractConcurrentReadCache implementations so that they either clean up the Cache.updateState map or notify the Cache itself through a listener mechanism.
2- deeper rework described by Andres in bug
CACHE-170 which includes moving EntryStateUpdate into the Cache.cacheMap and remove the Cache.updateState map.
As we discussed, I have built an internal patch version (based on version 2-0-1, which I called internally 2-0-3) applying the following design:
---------------------------
Since the EntryStateUpdate instances are used to coordinate threads during the cache miss/cache stale/putInCache/cancelUpdate conditions, the EntryStateUpdate instances should not be removed from the map until all threads being coordinated are done dealing with the key associated with a given EntryStateUpdate instance.
As a result, an explicit usage count is maintained into the EntryStateUpdate, it represent the number of usages on a given EntryStateUpdate instance among the following:
- looking up a stale entry
- waiting for an update of an entry
- [about to be] performing an update
Note that a single Thread may potentially account for more than one usage count for a given EntryStateUpdate instance (this case was not optimized and usage is not bound to threads, clients are still responsible for making balanced number of calls to cancelUpdate() or putInCache()).
When the usage count reaches zero, then the EntryStateUpdate instance is removed from the updateMap.
--------------------------
I attach the diff ("cvs-diff-2-0-1_to_2-0-3.txt") along with a tgz containing the modified classes ("modified_sources_in_2-0-3.tgz"). It includes the modifications for bugs
CACHE-170and CACHE-177, the corresponding enhancements to the unit tests. I also modified the LRUMap and FIFOMap to support JDK 1.3 compilation (i.e. no static reference to 1.4 classes, pure introspected instancation).I hope this can help and could be integrated in a future release.
Regards,
Guillaume.