Cache Abstractions and Memoization
Latest stable Based on Bluetape4k release 1.11.0
Capabilities
Section titled “Capabilities”bluetape4k-cache-core provides the contracts used before choosing a distributed provider: helpers for Caffeine, Cache2k, and Ehcache; JCache creation and configuration; the coroutine-oriented SuspendJCache; sync, async, and suspend memoizers; and common Near Cache interfaces that combine a local front cache with a remote back cache.
These APIs make ownership explicit: who fills a miss, whether same-key computations are merged, which tier is cleared, and whether a backend failure is propagated. A cache entry is still not source data. A cache put may replicate between cache tiers, but it is not persistence write-through to a database.
Decisions before adoption
Section titled “Decisions before adoption”- For repeated work inside one JVM, choose the smallest local provider or memoizer API.
- Use
SuspendJCacheor a suspend memoizer on coroutine paths; a suspend signature alone does not prove non-blocking I/O. - Distinguish caller-owned cache-aside from loader-owned JCache read-through.
- Configure capacity and expiry explicitly. The default JCache configuration is eternal.
- Move to
cache-lettuceorcache-redissonwhen processes must share values or invalidate local entries. - For hot keys, verify same-key miss merging through a memoizer or the chosen provider loader.
Coordinates
Section titled “Coordinates”Consumers manage only the central BOM version, not individual Caffeine, Cache2k, Ehcache, or JCache versions.
dependencies { implementation(platform("io.github.bluetape4k:bluetape4k-dependencies:<version>")) implementation("io.github.bluetape4k:bluetape4k-cache-core")}Cache2k and Ehcache implementations are compileOnly dependencies in 1.11.0. Add the selected provider to the application runtime when using those helpers. Caffeine and its JCache provider are API dependencies.
First local cache
Section titled “First local cache”Set capacity and expiry, then let the caller fill misses.
import io.bluetape4k.cache.caffeine.caffeineimport java.time.Duration
val users = caffeine { maximumSize(10_000) expireAfterWrite(Duration.ofMinutes(10)) recordStats()}.build<String, User>()
fun findUser(id: String): User { users.getIfPresent(id)?.let { return it } return userRepository.findById(id).also { loaded -> users.put(id, loaded) }}This is cache-aside. The cache does not update the repository, so the application must define how writes update or invalidate the cached value.
API by task
Section titled “API by task”| Task | Start with | Boundary to preserve |
|---|---|---|
| Configure Caffeine | caffeine, caffeineSpecOf, suspendLoadingCache | Capacity, expiry, and stats are explicit builder choices. |
| Configure Cache2k | cache2k, getOrCreateCache2k | The application supplies the provider at runtime. |
| Configure Ehcache | ehcacheManager, getOrCreateCache | Managers enter ShutdownQueue; the helper includes a 32 MB off-heap tier. |
| Use JCache | jcacheConfiguration, jcacheManager, JCaching | Default expiry is eternal and provider managers are lazy singletons. |
| Use a coroutine cache | SuspendJCache, CaffeineSuspendJCache | Check whether the implementation uses non-blocking I/O or a blocking bridge. |
| Reuse function results | Memoizer, AsyncMemoizer, SuspendMemoizer | Test same-key merging, failure removal, and clear(). |
| Share two-tier contracts | NearCacheOperations, SuspendNearCacheOperations | Keys are String; provider implementations own invalidation. |
| Add retry and fallback | withResilience, NearCacheResilienceConfig | Read fallback and write failure policies differ. |
Learning path
Section titled “Learning path”The chapters follow executable 1.11.0 source and tests rather than extending the README feature list. They explain defaults, concurrency, state after failures, and provider selection with short examples and direct source/test links.
- Local providers and JCache — compare Caffeine, Cache2k, Ehcache, JCache managers, and expiry defaults.
- Cache-aside and loader/writer contracts — separate caller-owned loading from JCache read/write-through configuration.
- Memoizers and same-key computation — cover sync, future, and suspend computation,
SingleFlight, failure, andclear(). - Near Cache front/back semantics — trace local hits, back lookups, front fills, invalidation, and statistics.
- Retry, failures, and lifecycle — verify read fallback, write exceptions, cancellation, and close behavior.
- Testing and ecosystem paths — move from shared fixtures to Lettuce, Redisson, Exposed, and workshops.
Read 1→2→3 for local contracts, then 4→5 for distributed-cache boundaries. Existing provider users can start with the failure table in chapter 5 and the conformance fixtures in chapter 6.
Patterns
Section titled “Patterns”Start local caches with bounded capacity and a short expiry. Measure evaluator count and eviction as well as hit rate. Do not cache failed loads. If concurrent misses can overload the backend, use same-key merging from a memoizer or a provider loading cache.
Document the order between source-data writes and cache invalidation. Do not label a cache-tier put as database write-through. Define the stale window when a source write succeeds but cache invalidation fails.
Integrations
Section titled “Integrations”bluetape4k-cache-lettuce and bluetape4k-cache-redisson implement remote storage and invalidation on top of these common contracts. Continue with bluetape4k-cache-lettuce for Lettuce and RESP3, or bluetape4k-cache-redisson for Redisson local cached maps.
Repository caching is a separate layer. Chapter 6 links JdbcCacheRepository and the Exposed workshops for real read-through, write-through, and write-behind examples.
Configuration
Section titled “Configuration”There is no module-wide properties file. Configure capacity, expiry, and statistics through provider builders or JCache MutableConfiguration. getDefaultJCacheConfiguration() uses EternalExpiryPolicy. The Caffeine front cache in NearJCacheConfig expires 30 minutes after access by default; its remote synchronization timeout defaults to 500 ms.
Resilience defaults to three attempts, a 500 ms initial wait, exponential backoff, and RETURN_FRONT_OR_NULL for read failures. Attempts and wait duration must be positive.
Failures
Section titled “Failures”Cache.getOrPut may run the supplier more than once during concurrent misses. putIfAbsent keeps the final cached value consistent, but it does not deduplicate external work. Use a same-key memoizer for expensive or side-effecting evaluation.
After retry exhaustion, read operations either return null, an empty map, or false, or propagate the exception according to GetFailureStrategy. Writes and removals propagate failure after retries. The suspend decorator always rethrows CancellationException without retrying or converting it to fallback data.
Operations
Section titled “Operations”Track capacity, hits, misses, evictions, evaluator count, and load latency. For Near Caches, separate local and back-cache hits and misses, and chart remote cache errors with source-store latency and pool saturation.
Give each manager and cache one lifecycle owner. CaffeineSuspendJCache.close() invalidates local entries and runs cleanup. Ehcache and JCache provider helpers register managers with ShutdownQueue. Use unique cache names in tests and clear or close them afterward.
Testing
Section titled “Testing”The module tests need no external Redis server.
./gradlew :bluetape4k-cache-core:test --no-build-cache --no-configuration-cacheCaffeineSuspendJCacheTest covers suspend CRUD. SingleFlightTest and provider memoizer tests cover concurrent evaluation, recovery after failure, and clear. The blocking and suspend resilience tests lock retry, fallback, cancellation, and close behavior. New providers can inherit the published fixtures to prove the same contract.
Workshops
Section titled “Workshops”The module tests are the smallest runnable examples. Continue with the cache-lettuce manual and cache-redisson manual for Redis invalidation.
For persistence strategies, use the cache chapters in exposed-workshop and the examples in bluetape4k-workshop. They introduce JdbcCacheRepository, EntityMapLoader, and EntityMapWriter, which distinguish a cache put from persistence write-through.
1.11.0 scope
Section titled “1.11.0 scope”This manual targets the bluetape4k-projects 1.11.0 release source. cache-core does not provide a Redis server, cluster invalidation, or a persistence transaction. Provider and repository modules own those concerns.
SuspendJCache is an asynchronous API shape, not a guarantee that every provider is non-blocking. Legacy NearJCache and the newer NearCacheOperations family also differ, so prefer the factory and shared fixture recommended by the selected provider manual.
Release diagrams
Section titled “Release diagrams”These diagrams are loaded directly from README assets published with the 1.11.0 release and pinned to its immutable commit. They describe this manual’s released structure and runtime flows, not later Snapshot changes. Select a preview to open the SVG at the same release commit.
NearCache Interface Hierarchy diagram
Section titled “NearCache Interface Hierarchy diagram”Release README: cache/cache-core/README.md
NearCacheOperations (Blocking) diagram
Section titled “NearCacheOperations (Blocking) diagram”Release README: cache/cache-core/README.ko.md
SuspendNearCacheOperations (Coroutine) diagram
Section titled “SuspendNearCacheOperations (Coroutine) diagram”Release README: cache/cache-core/README.ko.md
SuspendJCache coroutine interface diagram
Section titled “SuspendJCache coroutine interface diagram”Release README: cache/cache-core/README.ko.md
NearJCache synchronous two-tier cache diagram
Section titled “NearJCache synchronous two-tier cache diagram”Release README: cache/cache-core/README.ko.md
SuspendNearJCache coroutine operation map diagram
Section titled “SuspendNearJCache coroutine operation map diagram”Release README: cache/cache-core/README.ko.md
NearCache get() Sequence (front miss → back lookup → front fill) diagram
Section titled “NearCache get() Sequence (front miss → back lookup → front fill) diagram”Release README: cache/cache-core/README.md
NearCache put() Sequence (write-through) diagram
Section titled “NearCache put() Sequence (write-through) diagram”Release README: cache/cache-core/README.md







