feat: static generation and the read-through cold path #135

Merged
manlycucumber merged 1 commit from feature/13-static-generation into develop 2026-07-03 05:50:25 +00:00
manlycucumber commented 2026-07-03 05:48:59 +00:00 (Migrated from github.com)

Serves the hot calendar paths from a static tier, computing only on a cold miss (epic #13) — the cache-first model the R2 deployment depends on.

What's here

  • Cache\StaticStore (#15) — a filesystem store of pre-rendered response bodies, one JSON file per request key (v1/day/2026-09-03/1962/sspx). The whole tree is namespaced by the data version, so a corpus/engine/contract bump lands in a fresh directory and a stale file can never be served (the #28 rebuild-and-purge just drops the old dir). No root configured → the store is disabled and the service runs identically (the dev/test default).
  • Cache\ResponseCache (#16) — read-through: a hit returns the stored bytes untouched; a miss computes, writes back, and returns, with cache-friendly headers so the edge can hold it (x-cache: HIT|MISS, Cache-Control). The day/month/year handlers respond through it, keyed by the query's cacheKey().
  • bin/generate-static.php (#14) — warms the store for a year range by replaying every hot request (year, months, days × systems × calendars) through the real kernel, so generation reuses the exact response shape the endpoints serve — no second serialiser to drift — resolving each civil year once per (year, calendar).

Verification

  • composer check green: phpcs (bin now scanned too), PHPStan [OK], PHPUnit 56 tests / 199 assertions — including store round-trip, data-version isolation, read-through HIT/MISS, and end-to-end write-through at the kernel level.
  • Real run: php bin/generate-static.php 2026 2026 <dir> wrote 756 files (2 calendars × 378 paths); the SSPX Sep 3 file resolves first class; a repeat request is an x-cache: HIT with byte-identical output; the usage guard rejects out-of-range/misordered years.

Scope notes

The conservative Cache-Control here is hardened to ETag + immutable, version-keyed caching in the next PR (epic #17). In production a web-server rewrite serves an existing static file directly (try-file → public/index.php), so the hot path never enters PHP.

Refs #13 #14 #15 #16

Serves the hot calendar paths from a **static tier**, computing only on a cold miss (epic #13) — the cache-first model the R2 deployment depends on. ## What's here - **`Cache\StaticStore`** (#15) — a filesystem store of pre-rendered response bodies, one JSON file per request key (`v1/day/2026-09-03/1962/sspx`). The whole tree is **namespaced by the data version**, so a corpus/engine/contract bump lands in a fresh directory and a stale file can never be served (the #28 rebuild-and-purge just drops the old dir). No root configured → the store is disabled and the service runs identically (the dev/test default). - **`Cache\ResponseCache`** (#16) — read-through: a hit returns the stored bytes untouched; a miss computes, **writes back**, and returns, with cache-friendly headers so the edge can hold it (`x-cache: HIT|MISS`, `Cache-Control`). The day/month/year handlers respond through it, keyed by the query's `cacheKey()`. - **`bin/generate-static.php`** (#14) — warms the store for a year range by replaying every hot request (year, months, days × systems × calendars) through the **real kernel**, so generation reuses the exact response shape the endpoints serve — no second serialiser to drift — resolving each civil year once per (year, calendar). ## Verification - `composer check` green: phpcs (bin now scanned too), PHPStan `[OK]`, **PHPUnit 56 tests / 199 assertions** — including store round-trip, data-version isolation, read-through HIT/MISS, and end-to-end write-through at the kernel level. - Real run: `php bin/generate-static.php 2026 2026 <dir>` wrote **756 files** (2 calendars × 378 paths); the SSPX Sep 3 file resolves first class; a repeat request is an `x-cache: HIT` with byte-identical output; the usage guard rejects out-of-range/misordered years. ## Scope notes The conservative `Cache-Control` here is hardened to ETag + immutable, version-keyed caching in the next PR (epic #17). In production a web-server rewrite serves an existing static file directly (try-file → `public/index.php`), so the hot path never enters PHP. Refs #13 #14 #15 #16
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
Directorium/Api!135
No description provided.