Unravel โบ Java concepts โบ NoSQL and caches
In-memory caches (Caffeine, Guava, Ehcache)
A local cache keeps results of expensive work (a database read, a remote call) in memory next to your code.
Learn these first
At a glance
A sticky note on your monitor with the phone number you look up daily.
- 1Caffeine.newBuilder().maximumSize(10_000).expireAfterWrite(Duration.ofMinutes(10)).build() creates a bounded cachecache.get(key, k -> load(k)) computes a missing value once, even when many threads ask at the same time.
- 2A LoadingCache knows its loader, so cache.get(key) always worksrefreshAfterWrite reloads a value in the background and keeps serving the old one meanwhile.
- 3recordStats() exposes hit rate and load times, which tell you whether the cache helps at all
- 4Ehcache adds tiers (heap, off-heap, disk) and XML or fluent configuration; Spring @Cacheable can use any of them as the backend
- Every cache is a consistency decision
- Cache stampede: when a popular entry expires, many threads load it at once
- Caffeine uses a Window TinyLFU admission policy: it estimates how often each key is used with a compact frequency sketch and evicts the entry least likely to be reused, giving higher hit rates than plain LRU
private final Cache<String, ExchangeRate> rates = Caffeine.newBuilder() .maximumSize(5_000) .expireAfterWrite(Duration.ofMinutes(5)) .recordStats() .build();public ExchangeRate rate(String pair) { return rates.get(pair, p -> client.fetchRate(p));}- line 1 uses Caffeine, the core of In-memory caches (Caffeine, Guava, Ehcache)
- line 2 uses maximumSize, the core of In-memory caches (Caffeine, Guava, Ehcache)
- line 3 uses expireAfterWrite, the core of In-memory caches (Caffeine, Guava, Ehcache)
- line 4 uses recordStats, the core of In-memory caches (Caffeine, Guava, Ehcache)
Read the full explanation (about 2 min)
A local cache keeps results of expensive work (a database read, a remote call) in memory next to your code. It must have a size limit and an expiry rule, otherwise it becomes a memory leak or serves stale data.
A mental model
A sticky note on your monitor with the phone number you look up daily. Fast, but you need to throw away notes that are out of date and not cover the whole desk with them.
How it works
Caffeine.newBuilder().maximumSize(10_000).expireAfterWrite(Duration.ofMinutes(10)).build() creates a bounded cache. cache.get(key, k -> load(k)) computes a missing value once, even when many threads ask at the same time.
A LoadingCache knows its loader, so cache.get(key) always works. refreshAfterWrite reloads a value in the background and keeps serving the old one meanwhile.
recordStats() exposes hit rate and load times, which tell you whether the cache helps at all.
Ehcache adds tiers (heap, off-heap, disk) and XML or fluent configuration; Spring @Cacheable can use any of them as the backend.
Going deeper
Every cache is a consistency decision. Entries stay stale for up to the expiry time; after a write you must evict or accept the delay. With several instances each has its own copy, so a local cache needs short expiry or a shared invalidation message.
Cache stampede: when a popular entry expires, many threads load it at once. Caffeine and Guava load once per key; a hand-written ConcurrentHashMap check-then-put does not.
Cache immutable values or defensive copies. Mutating an object that is stored in a cache changes it for everybody.
Under the hood
Caffeine uses a Window TinyLFU admission policy: it estimates how often each key is used with a compact frequency sketch and evicts the entry least likely to be reused, giving higher hit rates than plain LRU. Maintenance such as eviction is batched through buffers to keep reads nearly lock free.
Common mistakes
- No maximum size.
- Check-then-put on a map instead of an atomic load.
- Mutating cached objects.
- Expecting multiple instances to see each other changes.
How Unravel recognises it
When you paste code, Unravel looks for these signs of in-memory caches (caffeine, guava, ehcache). Spotting them yourself is a good habit when you read unfamiliar code:
- Types:
LoadingCacheAsyncLoadingCacheCacheBuilderCacheLoaderCacheManagerBuilderCacheConfigurationBuilderResourcePoolsBuilderCacheStatsEhcacheExpiryRemovalListenerWeigherCaffeine - Method calls:
expireAfterWrite()expireAfterAccess()maximumSize()refreshAfterWrite()recordStats()
Part of a learning path
- Data stores beyond SQL ยท previous: Redis clients and data structures ยท next: MongoDB documents and queries
Related concepts
Frequently asked questions
What is In-memory caches (Caffeine, Guava, Ehcache)?
A local cache keeps results of expensive work (a database read, a remote call) in memory next to your code. It must have a size limit and an expiry rule, otherwise it becomes a memory leak or serves stale data.
What are the most common mistakes with In-memory caches (Caffeine, Guava, Ehcache)?
No maximum size. Check-then-put on a map instead of an atomic load. Mutating cached objects. Expecting multiple instances to see each other changes.
How can I see In-memory caches (Caffeine, Guava, Ehcache) in real code?
Paste your own Java into Unravel and it will point out where in-memory caches (caffeine, guava, ehcache) appears, explain how it works at the detail level you choose, and draw the flow of the code around it.