Cache Patterns
Cache Patterns
This page outlines common caching patterns with Morphium and when to use them.
- Read‑through entity cache (default)
- Use
@Cacheon entities to enable read cache for queries and ID cache - Choose a clear strategy that matches your write rate and consistency needs
@Cache(timeout = 30_000, clearOnWrite = true, strategy = Cache.ClearStrategy.LRU, syncCache = Cache.SyncCacheStrategy.CLEAR_TYPE_CACHE)
public class Product { ... }
When to use
- Most read‑heavy entities, where occasional stale reads for up to the TTL are acceptable
- Precise invalidation per entity (remove or update)
- Reduce blast radius on writes by removing/updating only affected IDs instead of clearing the whole type cache
@Cache(timeout = 60_000, clearOnWrite = true, syncCache = Cache.SyncCacheStrategy.REMOVE_ENTRY_FROM_TYPE_CACHE)
public class User { ... }
or
@Cache(timeout = 60_000, clearOnWrite = true, syncCache = Cache.SyncCacheStrategy.UPDATE_ENTRY)
public class User { ... }
Notes
- With
UPDATE_ENTRY, other nodes re‑read the updated document (slightly higher cost, but fewer cache misses)
- Cluster‑wide synchronization via messaging
- Ensure every node runs messaging and attach a
MessagingCacheSynchronizer
MorphiumMessaging messaging = morphium.createMessaging();
messaging.start();
new MessagingCacheSynchronizer(messaging, morphium);
When to use
- Multi‑node deployments that need consistent caches after writes made through Morphium
- Works on any backend/driver, including a standalone MongoDB or in‑memory driver — no replica set required
- Blind spot: writes done outside Morphium's storage listener (other apps, mongosh, restores) don't trigger invalidation
- Plus: it's a real message on a topic (
cacheSyncType/cacheSyncRecord), not just a raw DB event — any other node or process that understands the message format can hook in and run extra, custom logic beyond clear/remove/update. Useful when cache‑clear logic is more involved than the built‑in strategies
3b) Cluster‑wide synchronization via change streams
- Watches the underlying collections directly instead of relying on Morphium's own write path
new WatchingCacheSynchronizer(morphium); // no messaging setup needed
When to use
- The cached collections can be written by processes other than this Morphium cluster (other services, admin tools, migrations) — this catches those writes too,
MessagingCacheSynchronizerwould not - You have a replica set available (Change Streams require one — same restriction as
messagingSettings().setUseChangeStream(true)) and don't want to depend on messaging being healthy on every node - Not for standalone/single‑node MongoDB — falls back to nothing, since there's no oplog to watch
Pick one, not both, unless you specifically want redundant coverage. See Developer Guide § Cache Synchronization for the full comparison.
- TTL tuning and hot‑set sizing
- Keep
@Cache.timeoutsmall enough to minimize staleness, large enough to reduce DB load - Use
maxEntriesandstrategyto control memory use and eviction behavior
@Cache(timeout = 15_000, maxEntries = 50_000, strategy = Cache.ClearStrategy.LRU)
public class Article { ... }
- JCache integration and layering
- Use
MorphiumCacheJCacheImplto adopt javax.cache - Optionally back it with an external provider for cross‑JVM cache layers
MorphiumCacheJCacheImpl jcache = new MorphiumCacheJCacheImpl();
cfg.cacheSettings().setCache(jcache);
- Avoiding cache stampede
- For heavy queries, consider:
- Pre‑warming popular keys at startup
- Adding small jitter to TTL at application level if you implement explicit invalidation
- Using
UPDATE_ENTRYto keep hot IDs fresh without clearing type cache
- Eventual vs strong consistency
- Read‑through caching is eventually consistent between nodes
- To minimize staleness:
- Use smaller TTLs
- Prefer
REMOVE_ENTRY_FROM_TYPE_CACHEorUPDATE_ENTRY - Ensure messaging is available and responsive (replica set + change streams is preferred)
- Batching and write buffering
- Global write buffering and async writes reduce DB pressure under load
cfg.cacheSettings().setAsyncWritesEnabled(true);
cfg.cacheSettings().setBufferedWritesEnabled(true);
Trade‑off
- Buffering can delay writes; choose TTLs and cache sync strategies accordingly
- Manual controls for maintenance
// Clear one type
morphium.clearCachefor(Product.class);
// Clear all types on all nodes
new MessagingCacheSynchronizer(messaging, morphium).sendClearAllMessage("maintenance");
- Query‑result cache keys
- Morphium computes a deterministic key for queries (criteria + sort + projection + paging)
- Prefer projections to keep cached result documents small
See also