Near Cache architecture and regions
Latest stable Based on Bluetape4k release 1.11.0
The two tiers
Section titled “The two tiers”Each LettuceNearCache combines a process-local Caffeine L1 with a shared Redis L2. Reads check L1 first and populate it from Redis on a miss. Writes update Redis before populating L1.
Hibernate SessionFactory └─ LettuceNearCacheRegionFactory ├─ entity region ─ Caffeine L1 + Redis L2 ├─ collection region ─ Caffeine L1 + Redis L2 └─ query region ─ Caffeine L1 + Redis L2If Redis SET fails, LettuceNearCache.put throws before changing L1. StorageAccess logs and absorbs that failure, allowing the database transaction to continue without completing the cache put.
One instance per region
Section titled “One instance per region”LettuceNearCacheRegionFactory uses ConcurrentHashMap.computeIfAbsent, so repeated storage-access requests for a region share one Redis connection and Caffeine cache.
val nearCache = caches.computeIfAbsent(regionName) { LettuceNearCache(client, codec, properties.buildNearCacheConfig(regionName))}getCaches() exposes an unmodifiable view for Metrics and Actuator. Application code cannot bypass factory ownership by adding or removing entries.
Redis key space
Section titled “Redis key space”Near Cache prefixes Redis keys with {regionName}:. StorageAccess first converts the Hibernate key to hck2:<SHA-256 digest>, producing keys such as:
io.example.Product:hck2:K3...digestRegion eviction scans ${regionName}:* and uses UNLINK; it never uses FLUSHDB, so other region prefixes remain intact.
Separate first- and second-level tests
Section titled “Separate first- and second-level tests”The persistence context in a Hibernate Session is the first-level cache. Two finds in one Session do not prove that the Lettuce second-level cache is working.
repeat(2) { sessionFactory.openSession().use { session -> session.beginTransaction() checkNotNull(session.find(Product::class.java, id)) session.transaction.commit() }}Use new Sessions and inspect secondLevelCacheHitCount.