Skip to content

PPR capture: tags recorded after an await in async shell content miss the shell tag snapshot #676

Closed

Description

@ivogt

Problem (consumer-observable)

A cacheTag() recorded only AFTER an await inside an async server component that renders into a PPR shell is not collected into the shell entry — revalidateTag() with that tag will not evict the shell. Synchronous recording (the canonical #648 form) works; the loss is silent and async-only.

Mechanism

The shell capture snapshots the derived context's _requestTags synchronously at collection time (shell-capture.ts — the collected = [...derivedCtx._requestTags] at the putShell site). React renders synchronous components before that snapshot, so their tags land; a tag recorded after an await (microtask or later) lands after the snapshot and is dropped. Pre-existing behavior: async cache()/"use cache" reads at capture share the same window (their propagated tags can also post-date the snapshot). The document cache does NOT share the edge — it buffers the full response body before collectRequestTags, so late tags are included there.

Documented as a known edge in packages/rangojs-router/docs/design/ppr-shell-resume.md ("A tag recorded only AFTER an await …", ~line 370), landed with the render-callable cacheTag() work (#648).

Fix direction

Defer the shell's tag snapshot to a render-complete barrier (the capture already has quiesce machinery — handles settled + Flight task-quiet; the tag snapshot can move behind the same gate), mirroring the document cache's buffer-then-collect ordering. The capture's write path already awaits quiesce before putShell, so the move is ordering-only — no new synchronization.

Acceptance

  • Unit (shell-capture suite): an async shell component recording a tag after an await → tag present on the stored ShellCacheEntry; an async "use cache" read at capture → its tags present.
  • The existing sync-tag tests and the hole/bake invariant tests stay green (a hole still cannot tag — masked loaders never execute regardless of snapshot timing).
  • e2e (dev + production, cloudflare-basic): extend ppr-tag-eviction.test.ts with an async-recorded tag eviction case.

Notes

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

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