Caching with Guava
Basic overview of how to use caching APIs provided by google's guava library for java
Guava
Guava is an open-source release of Google's core Java libraries. It has helpful customizations for collections, string processing, concurrency, caching, etc. package `com.google.common.cache`
is a caching library for simple in-memory caching which is also thread-safe.
Cache
Caching is a technique in computing, which stores data at a temporary location for faster access to any request for it. When there is a request for data and we find it in the cache, it is termed a Cache Hit, if we don't find the data it is termed a Cache Miss. It is a heavily used technique for optimized and faster application performance.
Types of Cache by Guava
Loading Cache: Can automatically load entries when a cache miss occurs,
LoadingCache.get(key)
=> will return the value for the key, and load it if requiredCache: Does not automatically loads entry, needs to be explicitly
put(Obect, Object)
Loading Cache
Let's discuss Loading Cache, which is the most commonly used.
As the java-doc goes for it:
"A semi-persistent mapping from keys to values. Values are automatically loaded by the cache, and are stored in the cache until either evicted or manually invalidated."
We write the loading logic by overriding the load()
of CacheLoader which is used by the CacheBuilder when a cache miss occurs, i.e., it will automatically put(Object, Object)
if the value is not found when LoadingCache.get(key)
is called. The internal implementation is similar to ConcurrentHashMap.
Sample for LoadingCache in Groovy:
LoadingCache<String, String> cache =
CacheBuilder.newBuilder()
.maximumSize(Constants.MAXIMUM_CACHE_SIZE)
.expireAfterAccess(Constants.EXPIRATION_TIME_DURATION, TimeUnit.HOURS)
.removalListener(
new RemovalListener<String, String>() {
@Override
void onRemoval(RemovalNotification<String, String> notification) {
log.info("Cache for key ${notification.key} evicted because of ${notification.getCause()}")
}
}
)
.build(
new CacheLoader<String, String>() {
@Override
String load(String key) throws Exception {
return key.toUpperCase()
}
}
)
Eviction
To remove an entry from the cache after a certain condition is met, we can configure such eviction conditions by using the methods provided by CacheBuilder:
Maximum Size:
CacheBuilder.maximumSize()
- Specifies the maximum number of entries the cache may contain, will remove the entry as per LRU (Least Recently Used) strategy once the limit of cache is reached.Maximum Weight:
CacheBuilder.maximumWeight()
- Need to code logic to weigh, weight is measured only once when an entry is added. Entries are removed once the maximum total weight of entries the cache may contain is reached. This feature cannot be used in conjunction with maximumSizeTime to Idle:
CacheBuilder.expireAfterAccess()
- Specifies that each entry should be automatically removed from the cache once a fixed duration has elapsed after the entry's creation, the most recent replacement of its value, or its last access, i.e., both read and write events.Time to Live:
CacheBuilder.expireAfterWrite()
-Specifies that each entry should be automatically removed from the cache once a fixed duration has elapsed after the entry's creation, or the most recent replacement of its value, i.e., only the write events.
RemovalNotification
For any action which needs to be explicitly performed after each removal of entry, we can code that logic in RemovalListener by overriding its onRemoval()
. If an entry is manually removed, overwritten or evicted then this method is called. It can be set using CacheBuilder.removalListener()
. It can be used if any resource need to be closed or for logging purposes. (Can refer to the above code snippet)
Refresh Cache
When we need to explicitly reload a value for a key, we can use CacheLoader.refresh()
. If any thread tries to access the value for that key while the refresh is happening, the older value will be returned.
Automatic Refresh- When we know that the value loaded in the cache will not be relevant after some specific amount of time and needs to be reloaded, we can use the below methods to achieve this:
CacheBuilder.refreshAfterWrite()
- Specifies that active entries are eligible for automatic refresh once a fixed duration has elapsed after the entry's creation or the most recent replacement of its value.CacheBuilder.expireAfterWrite()
- Specifies that each entry should be automatically removed from the cache once a fixed duration has elapsed after the entry's creation or the most recent replacement of its value.
I hope this would have given you a basic overview and understanding of caching using the APIs provided by guauva library.