MongoDB Cache
:::danger Cache authorization boundary
- Do not use shared multi-tenant caching in 0.2.0. Its cache key does not include authenticated tenant/principal or authorization-policy/role identity, and Tenant Isolation is not executed.
- Use a dedicated Proxy and cache instance per tenant/principal security boundary, or disable caching entirely.
Unsafe legacy anti-pattern: The legacy key formula hashes only database + collection + command + request body. It omits authenticated tenant/principal identity and authorization-policy/role identity, so two callers with different permissions can collide and receive the same cached response.
:::
Serve repeated eligible reads from an in-memory LRU or Redis backend.
This page belongs to the immutable 0.2.0 Private Preview documentation.
At a glance
| Property | Value |
|---|---|
| Pipeline phase | Request + response |
| Category | Cache |
| Canonical minimum tier | Pro |
| Legacy dashboard tier label | Enterprise |
| Legacy rendered name | Cache Layer |
| Legacy rendered summary | Read-through LRU cache for MongoDB — sub-millisecond reads for hot data. |
| Canonical entitlement | Yes |
| Supported deployment contract | Yes |
| Release status | Reconciled and executable. |
Release accuracy
- 0.2.0 Private Preview: Reconciled and executable.
The detailed material below preserves every section rendered by the legacy dashboard. Where it conflicts with the release status above, the release status is authoritative. Legacy field names and examples are not a substitute for the selected bundle's CRD and runtime contract. Unsafe legacy wording is retained in metadata for traceability but is corrected in the rendered guidance.
Release-aware feature flow. The diagram is explanatory; the release status on this page is authoritative.
Diagram resources: Open the SVG full screen · Download the editable Excalidraw source
Performance impact
:::warning Legacy, unverified performance claims
These numbers are preserved for documentation parity with the legacy dashboard. They are not current benchmarks or service guarantees and have not been verified by the current test suite.
:::
| Percentile | Legacy claim |
|---|---|
| P50 | 0.30ms |
| P95 | 0.80ms |
| P99 | 1.50ms |
Legacy note: Cache miss path. Cache hits return in ~0.05ms (SHA256 + map lookup). Net effect is latency REDUCTION for cached reads.
Overview
The Cache Layer step provides an in-memory read-through cache for MongoDB queries. Frequently accessed documents are served directly from the proxy's memory, reducing load on the database and providing sub-millisecond response times for hot data.
The cache uses an LRU (Least Recently Used) eviction policy with per-namespace TTL configuration. Write operations automatically invalidate relevant cache entries to maintain consistency.
When to use
- Read-heavy workloads with frequently accessed "hot" documents
- Reducing MongoDB cluster load without application-level caching
- Latency-sensitive applications where even 1-2ms matters
- Cost reduction by serving reads from proxy memory instead of database IOPS
How it works
- Security boundary: 0.2.0 caching is permitted only in a dedicated single-tenant/single-principal Proxy and cache instance. Shared multi-tenant caching is prohibited.
- Request phase (reads): Within that isolated deployment, eligible reads can return a cached response without reaching MongoDB.
- Request phase (writes): Configured write operations invalidate the namespace.
- Response phase: Eligible misses are stored with the configured TTL and capacity limits.
Configuration
The table preserves the legacy dashboard field reference. “Not specified” means the legacy source did not declare required semantics.
| Field | Legacy UI type | Legacy default | Required | Description |
|---|---|---|---|---|
max_entries | number | 10000 | No | Maximum number of cached entries before LRU eviction starts. |
max_memory_mb | number | 256 | No | Independent memory ceiling in MB; whichever capacity limit (entries or memory) is reached first triggers eviction. |
default_ttl | duration | 60s | No | Default time-to-live for cached entries when a namespace rule does not set its own TTL. |
backend | select | memory | Yes | Where cache entries are stored: "memory" (per-proxy LRU, fastest) or "redis" (shared across replicas, adds a network hop). |
redis_addr | string | localhost:6379 | No | Redis endpoint used when backend="redis". Accepts host:port (e.g. redis-master:6379) or a redis:// URL. Ignored for the memory backend. |
max_value_size | number | 1048576 | No | Maximum size in bytes of a response eligible for caching; larger responses bypass the cache. |
cacheable_commands | multiselect | ["find","aggregate","count","distinct"] | No | Read commands eligible for caching (find, aggregate, count, distinct, getMore, listCollections, listIndexes). Commands outside the list always hit MongoDB. |
invalidate_on | multiselect | ["insert","update","delete","drop"] | No | Write commands that evict matching cached entries (insert, update, delete, drop, findAndModify, replace). |
rules | json | — | No | Namespace allowlist: [{ database, collection, ttl }]. database "" matches all databases; collection supports glob (e.g. "logs_", "*"); empty collection = all collections. With no rules, nothing is cached. |
stats_headers | object | disabled | No | Adds cache diagnostic response headers. Shape: { enabled, include_key }. include_key exposes the resolved cache key for debugging — avoid if keys can contain sensitive identifiers. Requires Pro. |
negative_cache | object | disabled | No | Caches empty/not-found results to shield the backend from repeated misses. Shape: { enabled, ttl_sec, max_entries }. Keep ttl_sec short so newly-created documents are not hidden behind stale misses. Requires Pro. |
write_behind | object | disabled | No | Coalesces invalidations during bursty writes. Shape: { enabled, window_ms, max_pending }. Larger window_ms reduces churn but can serve stale data longer; max_pending forces an early flush. Requires Business. |
pattern_invalidation | object | disabled | No | Maps writes on one namespace to evictions on another (e.g. invalidate order_summary when orders change). Shape: { enabled, rules: [{ trigger_database, trigger_collection, invalidate_database, invalidate_collection }] } with glob support. Requires Business. |
Settings reference
Capacity limits
Max Entries caps the number of cached responses before least-recently-used eviction starts. Max Memory sets an independent memory ceiling in MB, so whichever capacity limit is reached first can evict older entries. Tune both for the amount of hot data you expect the proxy to hold.
Default TTL
Default TTL controls how long cached responses remain fresh when a namespace rule does not provide its own TTL. It uses duration strings such as 60s, 5m, or 1h. Rule-level TTL values override this default for matching namespaces.
Backend
The backend selects where cache entries are stored. Memory (LRU) is per-proxy and fastest, matching the current modal default — its max_entries and max_memory_mb caps are enforced in-process. Redis stores entries in a shared server so a hit warmed by one replica serves all replicas, at the cost of a network hop; set redis_addr to the endpoint (host:port or a redis:// URL). With Redis, eviction is governed by the Redis server maxmemory policy (the entry/memory caps are advisory) and TTL uses native key expiry. Redis operations are fail-open: any error or timeout degrades to a cache miss so a Redis outage never blocks requests.
Value size limit
Max Value Size prevents responses larger than the configured byte limit from being cached. This protects the cache from a few large documents evicting many smaller hot entries. Increase it only when large responses are repeatedly read and safe to cache.
Commands and invalidation
Cacheable Commands lists the read commands eligible for caching; commands outside the list always hit MongoDB. Invalidate On lists write commands that evict matching cached entries. The defaults cache common read commands and invalidate on insert, update, delete, and drop.
Cache rules
Rules are the namespace allowlist for caching. Each rule names a database and collection glob, with an optional TTL that overrides the default. With no rules configured, nothing is cached.
Stats headers
Stats headers add response headers such as cache status, age, and hit ratio so clients and dashboards can inspect cache behavior. Including the resolved cache key is useful for debugging key composition. Avoid exposing keys if they can contain sensitive identifiers.
Negative caching
Negative caching stores empty or not-found responses for a short period to shield the backend from repeated misses. Keep the TTL short so newly-created documents are not hidden behind stale empty results. The max entries setting bounds memory used by cached misses separately from normal cache entries.
Write-behind coalescing
Write-behind coalescing batches invalidations during bursty writes. The coalesce window controls how long invalidations wait before being applied, trading less churn for slightly longer potential staleness. Max Pending forces an early flush when too many invalidations are queued.
Pattern invalidation
Pattern invalidation maps writes on one namespace to evictions on another namespace. Use it for derived views or summaries, such as invalidating an order_summary cache when orders change. Each rule defines a trigger database/collection and an invalidated database/collection, with glob support.
Examples
General-purpose cache with per-namespace rules
steps:
- name: builtin:cache
config:
max_entries: 50000
default_ttl: "5m"
max_value_size: 131072
rules:
- database: "config"
collection: "settings"
ttl: "1h"
- database: "users"
collection: "profiles"
ttl: "30s"
invalidate_on: ["insert", "update", "delete"]
Best practices
- Start with short TTLs (30s-5m) and increase based on data staleness tolerance
- Use rules to allowlist which namespaces to cache — set database:"*" to cache everything, or list specific database/collection pairs with per-rule TTLs
- Monitor cache hit rate via the nexo_cache_hits_total / nexo_cache_misses_total metrics
- Set max_value_size to exclude large documents that would waste cache space
- Use invalidate_namespace (default) unless you understand the consistency trade-offs
Limitations
- In-memory only — cache is lost on proxy restart
- Conservative invalidation (invalidate_namespace) may over-invalidate
- Cannot cache queries with $currentDate or server-generated values
- Multi-proxy deployments have independent caches — no cross-instance coherence
Security and operational guidance
- Do not use a shared 0.2.0 cache for multi-tenant or differently authorized principals: the key is authorization-blind and Tenant Isolation is not executed
- Use separate Proxy and cache instances per tenant/principal security boundary or disable caching
- Do not treat namespace and request-body matching as an authorization boundary
- Define invalidation and acceptable staleness
- Protect Redis with authentication, encryption in transit, and network policy
Related steps
Release availability
- 0.2.0 Private Preview: Reconciled and executable.
See the component catalog for the complete comparison matrix.