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.

When to Use

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

Rust struct Contract { key_pattern: String, // exact key or prefix pattern ("price:*") fresh_within: Duration, // max age before value is considered stale grace_period: Duration, // extra time to serve stale during refresh on_degrade: DegradePolicy, // what to do when source is down refresh_from: String, // HTTP URL or internal function for refresh created_at: Instant, // contract creation timestamp last_refresh: AtomicU64, // epoch ms of last successful refresh compliance: ComplianceMetrics, }

Commands

RESP # Create a contract: key must be refreshed every 5 seconds FRESH_WITHIN "price:AAPL" 5000 # Allow 2 extra seconds of stale data during refresh failures GRACE_PERIOD "price:AAPL" 2000 # When source is down: serve stale, return error, or serve fallback ON_DEGRADE "price:AAPL" ServeStale # HTTP endpoint to fetch fresh value REFRESH_FROM "price:AAPL" "https://api.internal/prices/AAPL"

Tick Loop & Proactive Refresh

The contract engine runs a background loop at a fixed 50ms interval. Each tick performs the following:

  1. Scan contracts: Iterate all active contracts and compute each key's age as now - last_refresh.
  2. 80% threshold check: If a key's age exceeds 80% of its fresh_within window, 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.
  3. Background HTTP refresh: The refresh task issues an HTTP GET to the refresh_from URL, parses the response body, and writes the new value to the cache engine via the standard SET path. The last_refresh timestamp is updated atomically.
  4. Failure handling: If the refresh HTTP call fails (timeout, 5xx, network error), the degrade policy is applied.
Why 80%?

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
Rust enum DegradePolicy { ServeStale, // return last known value ReturnError, // fail the read ServeFallback(Bytes), // return pre-set fallback }
Grace Period Expiry

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
RESP CONTRACT STATS "price:AAPL" # → checks_total: 17280 # → checks_fresh: 17264 # → compliance_pct: 99.91% # → refreshes_triggered: 8640 # → refreshes_failed: 3 # → grace_exhausted: 0

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