Skip to content

Cache the assembled packument output in KV#37

Merged
fengmk2 merged 2 commits into
mainfrom
cache-packument-output
Jun 30, 2026
Merged

Cache the assembled packument output in KV#37
fengmk2 merged 2 commits into
mainfrom
cache-packument-output

Conversation

@fengmk2

@fengmk2 fengmk2 commented Jun 30, 2026

Copy link
Copy Markdown
Member

Stacked on #36 (base cache-npm-packument; will retarget to main once #36 merges).

Why

Void doesn't edge-cache the Worker response (the packument carries no cf-cache-status), so the ~440KB packument was re-assembled and re-serialized on every request , the dominant per-request CPU + memory cost. #36 removed the npm network fetch; this removes the rebuild.

What

Cache the assembled body in KV, keyed by pkgt/<name>/<refs-index-etag>.<envHash>:

  • getConfiguredRefsWithEtag exposes the R2 index etag. The handler reads refs+etag first, checks the cache, and on a hit serves the stored string directly , no rebuild, no 440KB stringify, no per-ref meta reads.
  • Invalidation is automatic, no purge: any ref mutation (publish/register/unregister) rewrites the index → new etag → new key. R2 is strongly consistent read-after-write, so a just-published preview appears on the very next request (no publish-visibility lag).
  • envHash covers deploy-time VITE_PLUS_PREVIEW_REFS changes (KV persists across deploys); a 60s TTL bounds npm stable-version drift (preview freshness comes from the etag, not the TTL).
  • Stores a raw string (not via kvCached, which stores parsed JSON) so a hit avoids a parse + re-stringify.

Verify

tsc clean, 76 tests pass , added an explicit "rebuilds on etag change" invalidation test; the existing publish/register→fetch tests also fail loudly if a stale body were served. The staging smoke gate exercises it on the real runtime before prod.

Base automatically changed from cache-npm-packument to main June 30, 2026 15:24
Void does not edge-cache the Worker response (confirmed: the packument carries no
cf-cache-status), so the ~440KB packument was re-assembled and re-serialized on
every request, the dominant per-request CPU + memory cost.

Cache the assembled body in KV, keyed by the refs-index etag plus a hash of the
static env refs. getConfiguredRefsWithEtag exposes the R2 index etag; the handler
reads refs+etag first, checks the cache, and on a hit serves the stored string
directly (no rebuild, no 440KB stringify, no per-ref meta reads). Any ref mutation
rewrites the index so the etag changes, the key changes, and the entry is
invalidated with no explicit purge; R2 is strongly consistent read-after-write, so
a just-published preview appears on the very next request. The envHash covers
deploy-time ref changes (KV persists across deploys); a 60s TTL bounds npm
stable-version drift (preview freshness comes from the etag, not the TTL).

Stores the raw string (not via kvCached) so a hit serves without a parse +
re-stringify round-trip.
@fengmk2 fengmk2 force-pushed the cache-packument-output branch from e20c327 to ecc6697 Compare June 30, 2026 15:27
The handler hand-rolled the KV get-or-compute-then-put-with-TTL + degrade-on-error
pattern that kvCached already factors, just for a raw string instead of JSON (to
avoid a parse + re-stringify of the ~440KB body on hits). Add a kvCachedText
sibling next to kvCached and route the output cache through it: the inline
try/catch read + write blocks collapse into one call, the assembly becomes the
fetcher closure, and the KV degrade policy + warn-message convention now live in
one place for both cache variants.
@fengmk2 fengmk2 merged commit 347660b into main Jun 30, 2026
4 checks passed
@fengmk2 fengmk2 deleted the cache-packument-output branch June 30, 2026 17:31
fengmk2 added a commit that referenced this pull request Jul 1, 2026
Stacked on #37 (base `cache-packument-output`; retargets to `main` once
#37 merges). Merge order: #37 → this.

## Why

Refs are registered dynamically now (the publish action calls `/-/refs`
+ `/-/publish` → R2 index), so the static `VITE_PLUS_PREVIEW_REFS` var
is redundant for the actual preview flow. Its only remaining roles were
a never-expiring default ref and a bootstrap for the deploy tooling, and
neither needs a production config var.

## What

- **Production:** drop the var from `config.ts`/`env.ts`, drop the
env-merge in `getConfiguredRefs` (now R2-only), and simplify the F5
output-cache key to `pkgt/<name>/<etag>` (the env-refs hash is gone).
Removed the orphaned `parseConfiguredPreviewRefsSafe`.
- **Smoke test (the deploy gate):** no change needed beyond a comment ,
verified its checks don't depend on a registered ref (the packument
check passes on npm's stable versions; download/HEAD resolve any sha
without a registry lookup; `/-/refs` accepts an empty array).
- **e2e:** derives the ref to install from the live `/-/refs` instead of
the env var (skips if the bridge lists none).
- **warm.mjs:** now a manual publish helper (`pnpm warm <sha>`); dropped
from the `pnpm deploy` chain since there's nothing static to seed.
- **Tests:** the suite registers its fixture ref (`commit.a832a55`)
dynamically in `beforeEach`.

## Verify

`tsc` clean, 76 tests pass, action bundle unaffected. Net −9 lines. The
`rfcs/0001` design doc keeps its original references (historical
record).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant