Skip to content
Bluetape4k docs2.0

JCache near-cache serialization limits

Latest stable Based on Bluetape4k release 2.0.0

Direct NearJCache(config, backCache) construction registers a MutableCacheEntryListenerConfiguration so back-cache events can update the front cache. When the listener factory captures a Caffeine cache proxy, Hazelcast cannot serialize that configuration for the cluster. The public HazelcastNearJCache(...) factory no longer takes this listener-backed path.

Release tests explicitly expect HazelcastSerializationException with an underlying NotSerializableException from the direct listener-backed construction. That constructor remains unsupported for a Hazelcast client JCache back cache.

Hazelcast factory methods create a listener-free composition

Section titled “Hazelcast factory methods create a listener-free composition”

HazelcastCaches.nearJCache creates a Caffeine front JCache and Hazelcast back JCache without listener registration. The public HazelcastNearJCache(...) factory combines the caller-supplied front JCache with a Hazelcast back JCache through the same listener-free path. suspendNearJCache also creates a fixed Caffeine front and calls SuspendNearJCache.withoutListener.

Serialization-safe NearJCacheConfig and the destructive-clear authority are separate contracts. Existing Hazelcast factory calls default to NearJCacheClearAuthority.DENY; use a key-scoped removeAll(keys) for shared namespaces. Pass NearJCacheClearAuthority.EXCLUSIVE_BACK_CACHE only when the caller owns the entire back namespace before calling clear() or clearAllCache(). The authority is runtime-only and is not sent through Hazelcast configuration serialization. The wrapper closes only its supplied front cache; it does not close the Hazelcast back cache or provider.

import io.bluetape4k.cache.nearcache.jcache.NearJCacheClearAuthority
val cache = HazelcastCaches.nearJCache<String, User>(
hazelcast,
NearJCacheClearAuthority.EXCLUSIVE_BACK_CACHE,
) {
cacheName = "users-v1"
}
cache.put("42", user)
check(cache.getDeeply("42") == user)
cache.clear() // front and back
check(cache.getDeeply("42") == null)

Read-through and two-tier writes work, but another process can change the back cache without evicting this front cache. Factory success does not imply peer invalidation support.

When peer invalidation is required, use the IMap.addEntryListener path in HazelcastNearCache. That listener runs in the client JVM, so capturing Caffeine L1 does not create the JCache listener-factory serialization problem.

ChoiceBenefitLimit
factory JCache near cacheReuses JCache front/back contractsNo listener or peer L1 propagation
direct listener-backed JCache constructionIntended event propagationSerialization failure in 2.0.0
native IMap near cacheClient-side listener invalidationString keys and no JCache API

The 2.0.0 suspendNearJCache factory builds Caffeine with a 10,000-entry maximum and 30-minute expire-after-access. It uses the supplied cache name for the back cache but does not apply custom front capacity and expiry from NearJCacheConfig to this fixed front.

The listener-free factory still defaults to DENY; a key-scoped operation is the safe path for a shared namespace. An exclusive owner may opt in to a namespace clear, while the runtime-only authority remains outside serialized configuration.

val shared = HazelcastCaches.nearJCache<String, User>(hazelcast)
shared.removeAll(setOf("tenant-a:key-1"))
val owner = HazelcastCaches.nearJCache<String, User>(
hazelcast,
NearJCacheClearAuthority.EXCLUSIVE_BACK_CACHE,
) { cacheName = "users-owner" }
owner.clear()