Result Cache — High-Level Design
Scope decision: This standalone component is dropped from the binder package plan. The design below is historical context. Consumer applications own this responsibility; see the current package architecture.
Status: Draft for review · Part of: Astro Binder · Updated: 2026-10-24
The cache stores normalised provider results for an exact request, with the provenance that produced them. A hit returns the same canonical values without a provider call.
1. Purpose
Repeated position and transit workloads ask for the same instant and observer many times, yet recomputation costs a provider call and a normalisation pass. The cache removes that work:
- exact key, so a hit is the same request;
- canonical value, so a hit skips normalisation;
- provenance travels with it, so
cacheHitis the only difference; - an engine upgrade invalidates by construction, not cleanup.
2. Where it sits
calculation facade ──▶ result cache ── hit ──▶ stored canonical result + provenance
│ │ miss
▼ ▼
provider router → adapter → normalizer ──▶ write-back
- Upstream: the facade, with the router’s cache key.
- Stores: canonical values plus provenance.
- Never: chooses a provider, normalises, derives astrology, stores raw payloads.
3. Responsibilities
| Component | Owns | Notes |
|---|---|---|
| Key contract | Provider + full canonical request + engine version | Instant never rounded |
| Lookup | Read-through before a provider call | No fuzzy or partial matching |
| Value contract | Canonical result + provenance snapshot | One provider per entry |
| Write-back | Persist after normalisation succeeds | Failures not cached |
| Retention | Lifetime and eviction | Fixed instants kept; evicted on upgrade |
| Metrics | Hit ratio, latency, entry age | No payload logging |
4. Architecture
Diagram: reference design, not deployed behaviour.
The cache sits beside the request path: when it cannot answer, the facade continues to the provider.
5. Rules
- Exact key only. Provider id, bodies, instant in UT, observer, zodiac, ayanamsa, frame, house system and flags all participate; the instant is never rounded.
- Engine version is identity. An upgrade changes the key; old entries cannot answer and are evicted.
- Store canonical output. Entries hold normalised values and provenance, not raw tables; a hit never re-normalises.
- One provider per entry. One provenance block, matching the one-provider-per-response rule.
- Success only. Provider errors, normalizer rejections and partial answers are never cached.
- A hit is transparent. Only
cacheHit: trueand the stored provenance differ. - The cache is optional. Unavailability counts as a miss and never fails a calculation.
- Retention follows the workload. Fixed instants persist until an engine upgrade; later transit keys get a short lifetime.
6. Edge cases
| Case | Behaviour |
|---|---|
| Entry from an older engine | Unreachable by the new key; evicted |
| Cache unavailable or erroring | Treated as a miss; compute normally |
| Two identical misses at once | Both compute; exact key makes the last write idempotent |
| Degraded answer | Stored with degraded provenance; a hit never upgrades precision |
| Cache write fails after compute | Response returned; miss counted |
| Houses and positions, same instant | Separate entries; full request is the key |
| Very large body list | Same rules; key length bounded by the request |
| Short-lived transit keys (later) | TTL per workload, same value contract |
7. Out of scope
- Provider selection, fallback, breaker state (router).
- Unit, frame, zodiac, precision rules (normalizer).
- Response assembly, degradation policy (facade).
- Provenance retention, audit queries (provenance store).
- Drift validation and alerting.
8. Planned code
| Piece | Responsibility |
|---|---|
cache/ResultCache |
Lookup, hit metadata, write-back |
cache/CacheKey |
Exact key contract and validation |
cache/CacheEntry |
Canonical values + provenance snapshot |
cache/RetentionPolicy |
Lifetime, engine-version eviction, TTLs |
cache/CacheMetrics |
Hit ratio, latency, entry age |