Overview
A Cache Contract is a declarative rule attached to a key (or key prefix) that guarantees freshness within a defined window. Instead of relying on TTL-based expiration and cache misses, contracts proactively refresh values in the background before they become stale. When the upstream source is unavailable, the contract's degrade policy controls whether to serve stale data, return an error, or serve a fallback value.
Contracts are evaluated by a background tick loop running every 50ms. Each tick scans all active contracts, identifies keys approaching their freshness deadline, and triggers background refresh for keys that have consumed 80% or more of their FRESH_WITHIN window.
Use contracts when your application has hard freshness requirements — stock prices that must be <500ms old, session data that must reflect the latest permissions, or pricing that must update within 2 seconds of a change. Contracts move freshness enforcement from application code into the cache layer.
Contract Structure
Commands
Tick Loop & Proactive Refresh
The contract engine runs a background loop at a fixed 50ms interval. Each tick performs the following:
- Scan contracts: Iterate all active contracts and compute each key's age as
now - last_refresh. - 80% threshold check: If a key's age exceeds 80% of its
fresh_withinwindow, the key is enqueued for proactive refresh. For a 5-second contract, refresh fires at 4 seconds — a full second before the value would become stale. - Background HTTP refresh: The refresh task issues an HTTP GET to the
refresh_fromURL, parses the response body, and writes the new value to the cache engine via the standardSETpath. Thelast_refreshtimestamp is updated atomically. - Failure handling: If the refresh HTTP call fails (timeout, 5xx, network error), the degrade policy is applied.
The 80% threshold gives the refresh task a full 20% of the freshness window to complete the HTTP call and write the new value. For a 5-second contract, that is 1 second of headroom. This absorbs upstream latency spikes without ever serving stale data to clients under normal conditions.
Degrade Policy
When a refresh fails and the grace period is active, the degrade policy determines what clients see.
| Policy | Behavior | Use Case |
|---|---|---|
ServeStale |
Return the last known value with a X-Cache-Stale: true header |
Prices, dashboards — slightly stale is better than nothing |
ReturnError |
Return an error response to the caller | Compliance-critical data where stale = incorrect |
ServeFallback |
Return a pre-configured static fallback value | Feature flags, default configs — safe defaults exist |
Once the grace period expires and the refresh has still not succeeded, all three policies escalate to ReturnError. The grace period is a time-limited buffer, not an indefinite stale-serving window. Monitor the contract.grace_exhausted metric to detect persistent upstream failures.
Compliance Metrics
Each contract tracks its own compliance statistics, available via the CONTRACT STATS command.
| Metric | Description |
|---|---|
checks_total |
Total number of freshness checks performed by the tick loop |
checks_fresh |
Checks where the key was within its fresh_within window |
refreshes_triggered |
Number of proactive background refreshes initiated |
refreshes_failed |
Refresh attempts that failed (timeout, HTTP error, etc.) |
compliance_pct |
Percentage of checks where the value was fresh: checks_fresh / checks_total * 100 |
grace_exhausted |
Number of times the grace period expired without a successful refresh |
Configuration
| Parameter | Default | Description |
|---|---|---|
contracts.enabled |
false | Enable the contract engine and background tick loop |
contracts.tick_interval_ms |
50 | Tick loop interval in milliseconds. Lower = faster detection, more CPU |
contracts.refresh_threshold |
0.8 | Fraction of fresh_within at which proactive refresh triggers (0.0–1.0) |
contracts.max_concurrent_refreshes |
32 | Maximum number of concurrent background HTTP refresh tasks |
contracts.refresh_timeout_ms |
3000 | HTTP timeout for each refresh call |