# flash-ext-cache-caffeine In-process caching backed by [Caffeine](https://github.com/ben-manes/caffeine). Implements [`flash-ext-cache-core`](../../flash-ext-cache-core/docs/README.md). ## Dependency ```xml dev.relism flash-ext-cache-caffeine ${flash.version} ``` ## Quick start ```java FlashApp.create(8080) .install(new CaffeineCacheExtension()) .scan("dev.example.api"); ``` ```java @GET("/api/users/{id}") public final class GetUser extends RequestHandler { private Cache users; private UserRepository repo; @Override protected void onInit() { repo = require(UserRepository.class); users = require(CacheManager.class).build("users", spec -> spec .maxSize(10_000) .ttl(Duration.ofMinutes(10))); } @Override public Object handle(Request req, Response res) { return users.get(req.param("id"), repo::findById); } } ``` The extension takes no configuration. Each cache declares its own size and TTL where it is built. ## Why Caffeine and not a `LinkedHashMap` An LRU on top of `LinkedHashMap` is about sixty lines, and for a cache that is genuinely low-traffic it is the right answer — `ConcurrentHashMap::computeIfAbsent` is one line and has no hit rate to get wrong. This module exists for the case where that stops being true. Caffeine's W-TinyLFU admission, striped frequency counters and amortised eviction are not a weekend's work to reproduce, and the failure mode of getting them wrong is a cache that is *slower* than no cache — lock contention on every lookup, or an eviction policy that throws away exactly the entries you were about to want. ## Lifecycle Caches are released through `FlashContext.onClose`, so `app.stop()` drops every entry. That is invisible in production with one app per process and matters immediately under test, where many apps start and stop in one JVM. ## Statistics ```java CacheStats stats = users.stats(); stats.hitRate(); // 0.0 until something is looked up ``` Requires `recordStats()` on the spec. Without it you get `CacheStats.DISABLED`, which is honest about being unmeasured rather than reporting zeroes that look like a cold cache. `manager.names()` lists every cache built so far, for an ops endpoint. ## What this is not **HTTP caching.** If what you want is for the *client* to stop asking — `Cache-Control`, `ETag`, `304 Not Modified` — that is a middleware, not an object cache, and it saves the whole request rather than the lookup inside it. Reach for that first: it is cheaper, and the two solve different problems. **A shared cache.** Every replica has its own. Two instances will hold different values for the same key, and an invalidation on one does not reach the other. When that becomes a problem the answer is a networked backend — see the note on `flash-ext-cache-redis` — and the semantics change with it: a cache that can fail is no longer transparent.