Declare that key A depends on keys B, C, D. When any source changes, every derived key invalidates automatically. No application code. No stale composites. No more hand-coded invalidation chains.
Every production cache contains derived keys that aggregate, transform, or compose data from multiple source keys. And every one of them is a staleness time bomb.
You tell the cache which keys a derived value depends on. Cachee builds a directed acyclic graph. When any source key is invalidated, every downstream dependent is evicted automatically. Transitively. Atomically.
You declare dependencies at write time using the DEPENDS_ON modifier. The cache stores the dependency edges in an internal adjacency structure. When any source key is invalidated — by CDC, by a direct SET/DEL, by coherence, by TTL — the graph is walked and every downstream key is evicted.
This means your application code never needs to know about dependency chains. The cache itself is the system of record for which keys depend on which other keys. Teams can change, add, or remove dependencies without coordinating across services.
The dependency graph is only consulted during invalidation events, never during reads. A GET for a derived key has identical latency to a GET for any other key. The graph structure is maintained as a lightweight adjacency list alongside the cache entries, adding negligible memory overhead.
The only cost is at SET time (registering dependency edges) and at invalidation time (walking the graph to find dependents). Both operations complete in sub-microsecond time for typical dependency chains of 1–10 levels deep.
The dependency graph is not a standalone feature. It is a multiplier on every other Cachee primitive. Each composition creates behavior that no other caching system can replicate.
Learn more about CDC auto-invalidation, cross-service coherence, and how these primitives compose into a system that eliminates stale data at every level.
Dependency-aware invalidation does not exist in any other caching system. Here is why.
| Platform | Dependency Model | Cachee Dependency Graph |
|---|---|---|
| Redis | None needed — single source of truth, not an L1 cache | Full DAG with transitive cascade |
| Hazelcast | Near-cache has no dependency model whatsoever | DEPENDS_ON at SET time, automatic enforcement |
| Caffeine / Guava | Local-only, no distributed invalidation at all | Distributed + dependency-aware in one primitive |
| Memcached | Key-value only, no relationships between keys | Explicit key-to-key dependency edges |
| Every L1 / Sidecar Cache | Has the stale composite problem, none have solved it | Stale composites eliminated by design |
Redis is a shared remote cache. Every service reads from the same instance, so there is no stale-copy problem across services — just latency. Redis doesn't cache derived data locally; it serves every request from the centralized store. The dependency graph problem only arises when you have local caches that hold copies of derived data, which is exactly the L1/sidecar pattern that Cachee serves.
Building a dependency graph into a distributed L1 cache requires solving three hard problems simultaneously: maintaining DAG consistency across instances, propagating invalidation events transitively through the graph, and doing it all at cache-speed latency. Every existing L1 cache stops at key-level invalidation. None of them model relationships between keys. This is why the L1 cache category hasn't cracked enterprise production at scale.
A causal dependency graph is a directed acyclic graph (DAG) that tracks relationships between cache keys. When you declare that a derived key depends on one or more source keys, the cache builds a graph of those relationships. When any source key is invalidated, every derived key that depends on it is automatically invalidated as well, transitively through the entire graph. This eliminates stale composite data without any application-level invalidation code.
DEPENDS_ON is a modifier on the SET command that declares which source keys a derived key depends on. For example: SET user:123:dashboard <value> DEPENDS_ON user:123:profile user:123:orders. When any of those source keys is invalidated or updated, the dashboard key is automatically evicted. The dependency is stored in the cache's internal DAG and requires zero application code to enforce.
Yes. Invalidation propagates transitively through the entire dependency graph. If key A depends on key B, and key B depends on key C, then invalidating C will also invalidate B and A. The graph is walked depth-first, and every downstream dependent is evicted in a single atomic operation. There is no limit to the depth of the dependency chain.
CDC auto-invalidation and the dependency graph compose naturally. CDC watches your database's change stream and invalidates the base cache key when a row changes. The dependency graph then cascades that invalidation to every derived key. For example, CDC invalidates user:123:profile when the users table row changes, and the dependency graph automatically invalidates user:123:dashboard, user:123:feed, and any other key that declared a dependency.
No. The dependency graph is only consulted during invalidation, not during reads. A cache GET for a derived key has the same latency as any other GET. The graph is maintained as a lightweight adjacency structure alongside the cache entries. The only additional cost is during SET (to register dependency edges) and during invalidation (to walk the graph and evict dependents), both of which complete in sub-microsecond time for typical dependency chains.
One DEPENDS_ON modifier replaces hundreds of lines of invalidation logic. Declare your dependency graph, and never ship stale composites again.