分布式系统中Javers缓存机制疑问及自定义缓存咨询
Default LatestSnapshotCache Behavior in Multi-Node Setups
First off, the default LatestSnapshotCache is a local in-memory cache, meaning every application node maintains its own isolated cache instance with no awareness of other nodes. In a multi-node MongoDB deployment, this creates two critical issues:
- Cache consistency risks: When one node updates data and refreshes its local cache, other nodes will still serve stale snapshot data until their own cache expires or is manually cleared. This can lead to inconsistent audit/version data across your application nodes, which is a big problem for systems relying on Javers for accurate tracking.
- Wasted resources: Each node duplicates the same GlobalId and CdoSnapshot data in memory. This adds unnecessary overhead, especially as you scale up the number of application nodes.
On top of that, the default cache's expiration rules are controlled locally—there’s no way to trigger global cache invalidation, which only makes consistency issues worse.
Can You Replace It with a Distributed Cache Like Hazelcast?
Absolutely! Javers was built with extensibility in mind, so swapping out the default cache for a distributed alternative like Hazelcast is fully supported. Here’s how to do it:
1. Implement the LatestSnapshotCache Interface
You’ll need to implement Javers’ org.javers.repository.cache.LatestSnapshotCache interface. It has three core methods to handle cache operations:
get(GlobalId globalId): Fetch a cached snapshot by its GlobalIdput(GlobalId globalId, CdoSnapshot snapshot): Store a snapshot in the cacheevict(GlobalId globalId): Clear a specific entry from the cache (optional but highly recommended for proactive invalidation)
Here’s a sample implementation using Hazelcast:
import com.hazelcast.core.HazelcastInstance; import com.hazelcast.map.IMap; import org.javers.core.metamodel.object.GlobalId; import org.javers.repository.cache.LatestSnapshotCache; import org.javers.core.json.typeadapter.commit.CdoSnapshot; public class HazelcastLatestSnapshotCache implements LatestSnapshotCache { private final IMap<GlobalId, CdoSnapshot> cacheMap; public HazelcastLatestSnapshotCache(HazelcastInstance hazelcastInstance) { this.cacheMap = hazelcastInstance.getMap("javers-latest-snapshots"); } @Override public CdoSnapshot get(GlobalId globalId) { return cacheMap.get(globalId); } @Override public void put(GlobalId globalId, CdoSnapshot snapshot) { cacheMap.put(globalId, snapshot); } @Override public void evict(GlobalId globalId) { cacheMap.remove(globalId); } }
2. Register Your Custom Cache with Javers
Next, wire your custom cache into the Javers instance using JaversBuilder:
import com.hazelcast.core.Hazelcast; import com.hazelcast.core.HazelcastInstance; import org.javers.core.Javers; import org.javers.core.JaversBuilder; public class JaversConfiguration { public Javers buildJaversInstance() { HazelcastInstance hazelcastInstance = Hazelcast.newHazelcastInstance(); LatestSnapshotCache customCache = new HazelcastLatestSnapshotCache(hazelcastInstance); return JaversBuilder.javers() .registerJaversRepositoryCustomizer(repository -> repository.setLatestSnapshotCache(customCache)) .build(); } }
3. Key Additional Considerations
- Cache expiration & eviction: Configure Hazelcast’s TTL (time-to-live) rules based on your business needs to prevent unbounded cache growth. Make sure to call
evict()whenever data is updated to immediately invalidate stale entries. - Hazelcast cluster setup: Ensure your Hazelcast cluster is properly connected to all application nodes. This creates a single shared cache space, eliminating consistency issues between nodes.
- Performance tuning: Adjust Hazelcast settings like backup counts and memory limits to balance performance and reliability based on your traffic patterns.
Final Recommendations
If your application runs on multiple nodes, replacing the default local cache with a distributed solution like Hazelcast is strongly advised. It solves consistency problems, reduces redundant memory usage, and improves overall performance. Just be diligent about handling cache invalidation to keep your Javers snapshot data accurate across all nodes.
内容的提问来源于stack exchange,提问作者Romil Sandal

