Skip to content

ppr capture budget must be configurable — deferred shell material (async Meta / top-level handle pushes) beyond 5s makes a route uncapturable #715

Closed

Description

@ivogt

The consumer pattern (explicit design requirement from a production storefront)

Handlers push DEFERRED shell material — ctx.use(Meta)(dataPromise.then(...)), top-level handle pushes carrying promises — and on the live path React 19 hoisting streams the resolved metas late while the body renders. Under PPR the intended semantic is: the capture awaits the deferred material and bakes the settled values (shell fully static afterward; TTL/SWR govern freshness). The bake-lane mechanism already does exactly this — top-level pushes are awaited at capture.

The gap

SHELL_CAPTURE_MAX_WAIT_MS is a hard-coded 5s. Deferred material slower than that makes the capture refuse forever (eternal MISS with backoff + the no-usable-shell warning). Routes fit today only when their upstreams happen to be fast; there is no way to DECLARE "this route's shell material takes ~8s to settle — wait for it". Capture is background work (waitUntil): a longer budget costs latency-to-HIT only, never a served response.

Fix shape

  • Per-route knob: ppr: { ttl, swr, captureTimeout?: number } (ms), default the current 5s; plumb into the descriptor → captureShellHTML maxWaitMs and the handle-encode barrier so BOTH the fizz deadline and the handle settle window honor it.
  • The workerd waitUntil lifetime is the physical ceiling — document that.
  • Producer B parity: build-time captures should honor the same knob (build has no waitUntil bound).
  • e2e: a route with deliberately slow (>5s) deferred Meta + top-level handle push, captureTimeout set — MISS→HIT, baked meta present in the stored prelude, values frozen across HITs until TTL; and the negative (no knob → documented refusal warning). Both apps, dev + production.

Found with # on the same route; the two together are what "adopt the app's async pattern to ppr" requires.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions