Skip to content

Cache Caffeine

View Markdown

Status: Stable Package: dev.vertique.cache.caffeine Artifact: vertique-cache-caffeine Depends on: vertique-cache-core, vertique-json API reference: javadoc.io

vertique-cache-caffeine is the Caffeine provider for local (CacheMode.LOCAL) cache storage. It is intentionally separate from the provider-neutral cache contracts and uses the named JSON profile registry for defensive-copy serialization. Annotation apps install CacheAopModule themselves; vertique-cache-aop is test-scope here only.

Use this provider for application instances whose cache state is intentionally local to one process, including tests and single-instance deployments. Programmatic-only apps need no annotation module. Apps using @Cacheable / @CacheEvict also install CacheAopModule and put vertique-codegen-cache on the compiler’s annotation-processor path.

The provider owns local storage policy and lifecycle while cache core owns the provider-neutral invocation contract. This module includes only CacheCoreModule (plus JsonRuntimeModule).

The implementation maintains one bounded Caffeine cache per logical CacheRegion. Entries are JSON-serialized with the configured profile, so callers receive a defensive copy and generic declared result types remain supported. A finite TTL expires entries using a monotonic clock; TTL 0 disables time expiration while the per-region size bound remains active. Disabled caches and null results are no-ops, and codec or size failures are surfaced as failed provider futures for the runtime’s fail-open policy.

The provider edge runs the shared provider-neutral CacheStoreContractTest, so local behavior is checked against the same hit, miss, TTL, clear, failure, declared-type, value-isolation, and repeatable-eviction contract used by Redis. Core operational limits also apply here: oversized keys and values are rejected, and an explicit annotation TTL above maxTtlSeconds is rejected rather than clamped.

Dagger @Module that includes CacheCoreModule and JsonRuntimeModule. It contributes:

  • CacheStore under @CacheModeKey(CacheMode.LOCAL)
  • provider id "caffeine" under @CacheProviderIdKey(CacheMode.LOCAL)

Install it in the application component for local caching. Annotation apps also install CacheAopModule.

CacheStore backed by per-region Caffeine caches. Public constructors:

  • CaffeineCacheStore(CacheConfig) — direct use without a Dagger graph (empty JSON profile set)
  • CaffeineCacheStore(CacheConfig, JsonMapperProfileRegistry) — Dagger-injected form

new CaffeineObjectCache<>(name, objectCacheConfig, cacheFailure, observers) builds an ObjectCache<K, V> directly; there is no factory. It is a by-reference, size-bounded, single-flight cache for runtime objects that cannot or should not be serialized. It is independent of cache.enabled and reads no CacheConfig: the owner of a cache passes it an ObjectCacheConfig (dev.vertique.cache.config, from vertique-cache-core) holding maximumEntries (at least 1) and ttlSeconds (0 for no time expiry; the global cache.defaultTtlSeconds does not apply). The owner also decides where the values come from and which defaults apply to members the application leaves unset. As for the CacheStore providers, an entry expires a fixed time after it was created, not after its last read, and the expiry counts from the start of a load, so a load that outlasts it leaves no entry to share. There is no identity dimension and no serialization, so getOrLoad returns the identical instance on every hit.

  • getOrLoad(key, loader) runs the loader once per key across concurrent callers. Each caller gets its own future over the shared in-flight load, completed on that caller’s current Vert.x context, or on the completing thread when there is none.
  • A failed load is dropped so the next call loads again, unless cacheFailure accepts the error; accepted failures stay cached as failed futures until evicted or invalidated.
  • getIfPresent, invalidate, size and name complete the API. A blank name or a null argument is refused at creation, and an ObjectCacheConfig with maximumEntries below 1 or a negative ttlSeconds cannot be built.

Events go to the supplied observers under provider id "caffeine-object": a miss emits GET/MISS, a finished load emits LOAD/SUCCESS or LOAD/ERROR with the load duration, and a size eviction emits EVICT/SIZE_EVICTED with zero elapsed time. A time expiry emits nothing (it is not a size eviction), and neither do hits and invalidate, and observer failures are swallowed.

Binding Kind Description
CacheStore @Provides @IntoMap @CacheModeKey(LOCAL) Local Caffeine store
String @Provides @IntoMap @CacheProviderIdKey(LOCAL) Provider id "caffeine"
./mvnw -ntp -pl vertique-cache/vertique-cache-caffeine -am verify
Artifact Purpose
vertique-cache-core Provider-neutral cache contracts
vertique-json Named JSON mapper profiles used for value isolation