Cache
A coroutine-based in-memory cache with composable behaviours. Thread-safe, multiplatform, and designed for hot paths.
Why a custom cache?
Most JVM caches (Caffeine, Guava) are excellent but JVM-only. When you need the same caching API in
commonMain for code shared between JVM and JS, your options narrow quickly. Cache fills that gap
with a clean builder DSL and composable behaviours that work on all Kotlin platforms.
The core idea: composable behaviours, async eviction
Instead of one cache with many configuration flags, Cache uses composable behaviours. Stack them to get exactly the policy you need. Eviction runs asynchronously in a coroutine background loop — reading and writing the cache is fast because the calling thread never pays for eviction work.
val cache = fastCache<String, UserProfile> {
expireAfterWrite(10.minutes) // evict 10 min after last write
maxEntries(1000) // LRU after 1000 entries
onEviction { key, _ -> // react to evictions
logger.debug("evicted $key")
}
}
cache.put("user:42", fetchUser("42"))
val user = cache.getOrPut("user:99") {
fetchUser("99") // only called on miss
} Behaviour matrix
Seven behaviours, four eviction strategies, two observers, one async refresh:
| Behaviour | Builder DSL | Trigger | TTL resets on |
|---|---|---|---|
| expireAfterAccess | expireAfterAccess(5.minutes) | No read or write within TTL | Read + Write |
| expireAfterWrite | expireAfterWrite(5.minutes) | No write within TTL | Write only |
| maxEntries | maxEntries(1000) | Entry count exceeds limit | n/a (LRU by access) |
| maxMemoryUsage | maxMemoryUsage(50_000_000) | Estimated bytes exceed budget | n/a (LRU by access) |
| refreshAfterWrite | refreshAfterWrite(ttl) { ... } | Soft: async reload; Hard: evicts | Write only |
| onEviction | onEviction { k, v -> ... } | Observer — no eviction | n/a |
| statistics | statistics() | Observer — no eviction | n/a |
What you get
Four Eviction Strategies
TTL by access, TTL by write, max entry count (LRU), and max memory with object size estimation.
Background Refresh
Stale-while-revalidate: serve old values while refreshing in the background. No reader ever waits.
Observability
Eviction callbacks and statistics (hit rate, miss count, eviction count) for tuning and debugging.
Thread-Safe
All operations are safe for concurrent access from multiple coroutines.
Kotlin Multiplatform
Works in commonMain, jvmMain, and
jsMain.
Coroutine-Based
Eviction loop integrates with structured concurrency. Cancel the scope, the evictor stops cleanly.