Skip to content

fix(cloudflare): prewarm the uploaded Worker version safely#2593

Open
NathanDrake2406 wants to merge 16 commits into
cloudflare:mainfrom
NathanDrake2406:nathan/fix-workers-dev-cdn-warmup
Open

fix(cloudflare): prewarm the uploaded Worker version safely#2593
NathanDrake2406 wants to merge 16 commits into
cloudflare:mainfrom
NathanDrake2406:nathan/fix-workers-dev-cdn-warmup

Conversation

@NathanDrake2406

@NathanDrake2406 NathanDrake2406 commented Jul 11, 2026

Copy link
Copy Markdown
Contributor

Human note

I looked into this. the bug was that vinext couldn’t tell whether the new Worker version actually handled the warmup request or not. It treated any response 2xx, 3xx response as a success even if it was an old Worker version.
So this PR: stages the uploaded Worker at 0%, prewarms cache keys, then verifies the response using version metadata then promote to 100%

Overview

This fixes #2592 by prewarming the uploaded Worker version before it receives normal traffic. The deploy stages the upload at 0%, requests the real production cache keys with a verified version override, then promotes it to 100%.

Why

Cloudflare Workers Cache includes the invoked Worker version in its cache key by default. That gives each deployed version an isolated cache partition. If an override temporarily misses during deployment propagation, the request can only populate the old version cache; vinext detects the producer mismatch and retries. A timing-based post-promotion barrier cannot provide that structural guarantee.

What changed

  • Validate the selected environment metadata binding before upload.
  • Resolve the exact environment-local route and Worker name used by the override.
  • Stage the uploaded version at 0%, apply triggers, warm canonical paths, and promote afterward.
  • Verify each canonical response carries the uploaded version ID and do not follow redirects when proving the source cache key.
  • Reject prewarming when cache.cross_version_cache is enabled, because that deliberately shares cache entries across versions.
  • Skip Worker-version verification for static exports served directly by Cloudflare Assets.
Validation
  • vp check
  • 481 focused unit and deploy behavior tests
  • vp run vinext#build
  • vp run cloudflare#build
  • Repository pre-commit checks, staged tests, and knip

The deploy behavior tests observe the full command and request sequence, version override header, retry on producer mismatch, and absence of promotion after strict failure. TOML and JSONC compatibility remain ordinary parser tests.

References

@pkg-pr-new

pkg-pr-new Bot commented Jul 11, 2026

Copy link
Copy Markdown

Open in StackBlitz

npm i https://pkg.pr.new/@vinext/cloudflare@2593
npm i https://pkg.pr.new/create-vinext-app@2593
npm i https://pkg.pr.new/@vinext/types@2593
npm i https://pkg.pr.new/vinext@2593

commit: a1ad362

@github-actions

github-actions Bot commented Jul 11, 2026

Copy link
Copy Markdown
Contributor

Performance benchmarks

Compared a1ad362 against base 0252ea1 using alternating same-runner rounds. Next.js was unchanged and skipped.

0 improved · 1 regressed · 5 within ±1.5%

Scenario Framework Baseline Current Change
Client bundle size (gzip) vinext 130.4 KB 130.4 KB ⚫ -0.0%
Client entry size (gzip) vinext 117.9 KB 117.9 KB ⚫ -0.0%
Dev server cold start vinext 2.68 s 2.73 s 🔴 +1.8%
Production build time vinext 2.92 s 2.89 s ⚫ -1.3%
RSC entry closure size (gzip) vinext 99.0 KB 99.0 KB ⚫ +0.0%
Server bundle size (gzip) vinext 166.1 KB 166.1 KB ⚫ -0.0%

View detailed results and traces

🟢 improvement · 🔴 regression · ⚫ change below 1.5% · paired base/head

@NathanDrake2406 NathanDrake2406 changed the title fix(cloudflare): warm workers.dev only after promotion fix(cloudflare): warm only after production promotion Jul 11, 2026
@NathanDrake2406 NathanDrake2406 marked this pull request as ready for review July 11, 2026 05:52
@NathanDrake2406 NathanDrake2406 marked this pull request as draft July 11, 2026 06:04
@NathanDrake2406 NathanDrake2406 changed the title fix(cloudflare): warm only after production promotion fix(cloudflare): verify CDN warmup producer version Jul 11, 2026
@NathanDrake2406 NathanDrake2406 marked this pull request as ready for review July 11, 2026 06:21
@NathanDrake2406 NathanDrake2406 marked this pull request as draft July 11, 2026 06:54
@NathanDrake2406 NathanDrake2406 changed the title fix(cloudflare): verify CDN warmup producer version fix(cloudflare): prewarm the uploaded Worker version safely Jul 11, 2026
…fore promoting

Workers Cache is keyed per Worker version by default. `--experimental-warm-cdn-cache`
issued warmup requests against production before confirming which version answered
them, so a request routed to the still-live previous version silently warmed the
wrong cache partition. The deploy then promoted the new version anyway, reporting
success while the cache stayed cold for real traffic.

The fix enforces one invariant: a warmup only counts as successful when the
response proves it came from the uploaded Worker version.

- `vinext init` provisions a `version_metadata` binding under a fixed name
  (VINEXT_VERSION_METADATA); the Worker entry reads the current version ID from it
  and stamps every response with `x-vinext-worker-version`.
- Deploy stages the uploaded version at 0% traffic, warms build-discovered paths
  through a `Cloudflare-Workers-Version-Overrides` header, and rejects any response
  whose version header doesn't match the uploaded version — without following
  redirects, so the canonical cache key is what gets verified.
- Promotion only happens after warmup completes; in strict mode a failed or
  unverified warmup aborts before promotion, leaving the previous version at 100%
  and the new one staged at 0% — already the safe state, so no compensating
  mutation is issued. In non-strict mode the deploy promotes anyway but does not
  claim the cache was warmed.
- Static exports (`output: "export"`) skip verified warmup entirely: Cloudflare
  Assets serves them without invoking the Worker, so there's no version header to
  verify against.

Scope cut from the original draft: dropped the generic version-traffic
reconciliation module (ambiguous-failure classification, auto-rollback, manual
recovery command rendering), the configurable binding-name companion variable in
favor of the fixed binding name the runtime always reads, and cross_version_cache
config parsing/validation. None of those are required to enforce the core
invariant, and each is a separable concern better reviewed on its own.

Tests: tests/worker-version.test.ts, tests/cloudflare-cdn-warm.test.ts,
tests/cloudflare-cdn-warm-deploy.test.ts, tests/deploy.test.ts, tests/init.test.ts,
tests/deploy-prerender-config.test.ts.
@NathanDrake2406 NathanDrake2406 force-pushed the nathan/fix-workers-dev-cdn-warmup branch from 4c90d59 to 094c4a9 Compare July 11, 2026 11:01
…TPR's config reader, and stop reporting unconfirmed warm-ups as plain success

cdn-warm-deployment.ts read Worker name, production host, and legacy_env
suffixing directly out of tpr.ts's WranglerConfig/env map. tpr.ts owns
TPR-specific config extraction; CDN warmup's env-fallback and
Worker-name-suffixing rules do not belong to that module, and the coupling
made tpr.ts a de facto shared deployment-semantics resolver.

Separately, when non-strict warmup exhausted its retries without ever
confirming the uploaded version served a response, deployWithCdnWarmup still
promoted and returned a bare URL string. deploy.ts printed a generic
"Deployed to: <url>" box identical to a fully-warmed deploy, so an operator
had no way to tell a confirmed warm-up from a silent fallback short of
scrolling back through mid-deploy warnings.

Added wrangler-deployment-target.ts to resolve {workerName, productionHost,
versionMetadataBinding} on top of the existing parseWranglerConfig output,
so cdn-warm-deployment.ts no longer touches TOML/env-map shape directly.
deployWithCdnWarmup now returns {url, warmed}; deploy.ts's closing summary
states plainly when a promoted version's cache was not confirmed
pre-warmed, and a console.warn now fires at the retry-exhausted fallback
site that previously logged nothing beyond per-path failures.

Also named the decisions that were previously inlined at effectful
call-sites (promotionPhaseFor, isFullyWarmed, pickDeployedUrl in
cdn-warm-deployment.ts; describeVersionMismatch in cdn-warm.ts) so they're
independently readable and testable from the wrangler/fetch calls around
them.

Added a test covering the previously-uncovered non-strict-promotes-without-
confirmed-warmup path. vp check clean; targeted suite 480/480.
@NathanDrake2406 NathanDrake2406 marked this pull request as ready for review July 11, 2026 11:20
…ing config mid-warm-up

resolveCdnWarmupTargetUrl called resolveWranglerDeploymentTarget a second
time inside warmAndPromote's warm attempt, reparsing wrangler.jsonc/.toml
from disk even though validateCdnWarmupConfiguration had already resolved
the same target moments earlier in the same deploy. The duplicate I/O made
the WranglerDeploymentTarget abstraction ceremonial rather than real: two
call sites could theoretically observe different config state despite
describing one deploy.

Deleted resolveCdnWarmupTargetUrl. deployWithCdnWarmup now resolves the
target once and threads it through; the production-host fallback
(`target.productionHost ? https://... : staged.deployedUrl`) is inlined
where it's used. Migrated its direct tests in deploy.test.ts onto
resolveWranglerDeploymentTarget's `.productionHost` field, which is what
they were actually exercising — the deployedUrl fallback itself stays
covered by the existing end-to-end warmAndPromote tests.

Also split deployWithCdnWarmup into promoteWithoutWarmup and warmAndPromote:
they are genuinely different deployment modes (no safe staging split vs.
staged-warm-promote) with different guarantees, not just a long function
that happened to have an early return. Removed isFullyWarmed — it restated
`result.failed === 0` without adding a decision worth naming separately.

vp check clean; targeted suite green (deploy.test.ts, cdn-warm-deploy,
deploy-prerender-config, cdn-warm, version-deploy, worker-version, init).
getZeroPercentStagingTraffic didn't check the uploaded version ID against
the currently-live one. If upload ever returned an ID matching the version
already at 100%, it built a traffic split with the same version listed
twice (v@100% v@0%) and handed that straight to `wrangler versions deploy`.

Added the equality check so that case falls back to promoteWithoutWarmup
like any other unsafe traffic shape, instead of reaching wrangler with a
malformed split.

Also stopped re-deriving previousVersionId from currentVersions[0] in
deployWithCdnWarmup — that index is only safe because a non-null
stagingTraffic already proved current has exactly one entry, and that proof
lived in a different function. Read it from stagingTraffic[0] instead, the
value that was actually checked.
Upstream moved TPR KV key construction to the shared key-space helper while this branch added Wrangler deployment-target parsing in the same import block. Keep both APIs so TPR uses the current cache key contract without dropping CDN warmup config support.
@NathanDrake2406

Copy link
Copy Markdown
Contributor Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 4926519547

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread packages/cloudflare/src/wrangler-deployment-target.ts
Comment thread packages/cloudflare/src/cdn-warm-deployment.ts Outdated
Comment thread packages/cloudflare/src/cdn-warm-deployment.ts Outdated
Comment thread packages/cloudflare/src/deploy.ts
Comment thread packages/cloudflare/src/tpr.ts Outdated
NathanDrake2406 and others added 2 commits July 15, 2026 16:45
Generated environment-specific Wrangler configs flatten the selected environment, failed strict warmups leave zero-percent versions behind, and direct fallback promotion can lose trigger recovery context. That can reject a valid retry or obscure the deployed state after a trigger failure.

The deployment flow assumed selected environments always remained nested and that every version in a retry status had positive traffic. Recognize generated configs through Wrangler's targetEnvironment marker, replace stale zero-percent versions when staging a fresh attempt, reuse post-promotion trigger diagnostics, and accept trailing TOML route comments when resolving the production host.
@james-elicx

Copy link
Copy Markdown
Member

/bigbonk review for issues

@ask-bonk

ask-bonk Bot commented Jul 15, 2026

Copy link
Copy Markdown
Contributor

UnknownError: ProviderInitError

github run

@ask-bonk

ask-bonk Bot commented Jul 15, 2026

Copy link
Copy Markdown
Contributor

@james-elicx Bonk workflow failed. Check the logs for details.

View workflow run · To retry, trigger Bonk again.

@james-elicx

Copy link
Copy Markdown
Member

/bigbonk review for issues

1 similar comment
@james-elicx

Copy link
Copy Markdown
Member

/bigbonk review for issues

@ask-bonk

ask-bonk Bot commented Jul 15, 2026

Copy link
Copy Markdown
Contributor

UnknownError: ProviderInitError

github run

@james-elicx

Copy link
Copy Markdown
Member

/bigbonk review for issues

@ask-bonk

ask-bonk Bot commented Jul 15, 2026

Copy link
Copy Markdown
Contributor

UnknownError: "Overloaded"

github run

@james-elicx

Copy link
Copy Markdown
Member

/bigbonk review for issues

@ask-bonk ask-bonk Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review: staged CDN warmup deployment

I reviewed the full diff and ran the affected test files locally — tests/worker-version.test.ts, tests/cloudflare-cdn-warm.test.ts, tests/cloudflare-cdn-warm-deploy.test.ts, tests/deploy.test.ts, tests/init.test.ts, and tests/deploy-prerender-config.test.ts all pass (474 tests).

The overall design is solid. The core insight — that a 2xx/3xx response alone can't prove the uploaded version served it, because Workers Cache partitions by version ID — is correct, and verifying via x-vinext-worker-version + Cloudflare-Workers-Version-Overrides is the right structural fix. The staging-at-0% / promote-after-warm ordering, the fail-closed strict mode, and the retry-after-stale-0%-version recovery are all handled well and well-tested. The Codex P1/P2 items appear genuinely addressed in e22bc00.

Dev/prod parity check: fetch-handler (the recommended default entry) resolves to app-router-entry/pages-router-entry via virtual:vinext-worker-entry, and custom workers that delegate to handler.fetch() inherit the stamp, so stampWorkerVersion coverage is complete for Worker-served responses. Good.

A few non-blocking observations below.

Comment thread packages/cloudflare/src/cdn-warm.ts
Comment thread packages/cloudflare/src/cdn-warm-deployment.ts Outdated
Comment thread packages/vinext/src/server/worker-version.ts
Comment thread packages/cloudflare/src/deploy.ts
@james-elicx

Copy link
Copy Markdown
Member

/bigbonk review for issues

@ask-bonk

ask-bonk Bot commented Jul 15, 2026

Copy link
Copy Markdown
Contributor

UnknownError: ProviderInitError

github run

@james-elicx

Copy link
Copy Markdown
Member

/bigbonk review for issues

@ask-bonk ask-bonk Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed the full diff and read the new/changed source (cdn-warm-deployment.ts, cdn-warm.ts, worker-version.ts, wrangler-deployment-target.ts, utils/toml.ts, tpr.ts, deploy wiring) and ran tests/worker-version.test.ts + tests/cloudflare-cdn-warm.test.ts locally (26 pass).

Overall: the design is correct and no blocking issues found. The core insight is sound: because Workers Cache partitions its key by invoked version ID (unless cross_version_cache), a plain 2xx/3xx cannot prove the uploaded version populated the cache. Verifying via the x-vinext-worker-version header + a Cloudflare-Workers-Version-Overrides override is the right structural fix, and the stage-at-0% / warm / promote / triggers-after-promotion ordering keeps the risky window in the already-safe state. Fail-closed strict mode, stale-0%-version retry recovery (getZeroPercentStagingTraffic), path-scoped-route refusal, and the static-export carve-out are all handled and well-tested.

Parity confirmed: stampWorkerVersion runs in both app-router-entry and pages-router-entry, and fetch-handlervirtual:vinext-worker-entry resolves to those, so all Worker-served responses are stamped. The Node prod-server correctly does not stamp (no version-metadata binding off-Cloudflare). The prior Codex/bigbonk rounds appear genuinely addressed.

A few small non-blocking observations below.

Comment thread packages/cloudflare/src/cdn-warm-deployment.ts Outdated
Comment thread packages/cloudflare/src/cdn-warm.ts
@ask-bonk

ask-bonk Bot commented Jul 15, 2026

Copy link
Copy Markdown
Contributor

fatal: could not read Username for 'https://github.com': No such device or address

github run

@james-elicx

Copy link
Copy Markdown
Member

/bigbonk review for issues

@ask-bonk ask-bonk Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Independent review of the staged CDN-warmup deployment.

Overall: the design is correct and I found no blocking issues. I traced the core mechanism end-to-end and it holds up:

  • The structural insight is sound: because Workers Cache partitions its key by invoked version ID (unless cross_version_cache), a plain 2xx/3xx can't prove the uploaded version populated the cache. Verifying via x-vinext-worker-version + a Cloudflare-Workers-Version-Overrides override, with redirect: "manual" so a redirect is proven at its own cache key, is the right fix.
  • The stage-at-0% -> warm -> promote -> triggers-after-promotion ordering keeps the risky window in the already-safe state (previous version at 100%, new at 0%), and strict mode fails closed by leaving that split rather than issuing an undo mutation.
  • getZeroPercentStagingTraffic correctly rejects partial traffic splits and drops stale 0% leftovers so a fresh attempt retries from the safe state.
  • The path-scoped-route carve-out is correct: for app.example.com/api/*, cleanDomain sets customDomain (so hasProductionRoute is true) while routePatternToWarmupHost returns null (so productionHost is undefined), and warmAndPromote then refuses to warm workers.dev on a different cache key. Good.

I also confirmed stampWorkerVersion parity: both app-router-entry and pages-router-entry stamp at the top-level fetch, and the recommended default fetch-handler resolves to those, so all Worker-served responses carry the header. The prior Codex P1/P2 and bigbonk items appear genuinely addressed.

One new, non-blocking finding below (a test-coverage regression), plus a small parser observation.

Comment thread packages/cloudflare/src/cdn-warm-deployment.ts
Comment thread packages/cloudflare/src/cdn-warm-deployment.ts
Comment thread packages/cloudflare/src/tpr.ts
@ask-bonk

ask-bonk Bot commented Jul 15, 2026

Copy link
Copy Markdown
Contributor

fatal: could not read Username for 'https://github.com': No such device or address

github run

@james-elicx

Copy link
Copy Markdown
Member

/bigbonk review for issues

@ask-bonk ask-bonk Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review: staged CDN warmup deployment

I reviewed the full diff, read the new/changed source (cdn-warm-deployment.ts, cdn-warm.ts, worker-version.ts, wrangler-deployment-target.ts, utils/toml.ts, tpr.ts, deploy.ts, init-cloudflare.ts, entry stamping) and ran tests/worker-version.test.ts + tests/cloudflare-cdn-warm.test.ts locally (27 pass).

Overall: the design is correct and I found no blocking issues. I re-verified the core mechanism end-to-end and it holds up:

  • The structural insight is right: because Workers Cache partitions its key by invoked version ID (unless cross_version_cache), a plain 2xx/3xx cannot prove the uploaded version populated the cache. Verifying via x-vinext-worker-version + a Cloudflare-Workers-Version-Overrides override, with redirect: "manual" so a redirect is proven at its own cache key, is the correct fix.
  • The stage-at-0% -> warm -> promote -> triggers-after-promotion ordering keeps the risky window in the already-safe state (previous @100%, new @0%), and strict mode fails closed by leaving the split rather than mutating.
  • getZeroPercentStagingTraffic rejects partial splits, drops stale 0% leftovers, and fails closed when the upload is already the 100% version.
  • The path-scoped-route carve-out is correct: hasProductionRoute && !productionHost refuses to warm workers.dev on a different cache key.
  • The strict + missing-target path throws inside the try and is re-wrapped by the catch, so promotion is correctly skipped.
  • stampWorkerVersion parity confirmed: both app-router-entry and pages-router-entry stamp at the top-level fetch, and fetch-handler resolves to those, so all Worker-served responses carry the header.

The prior Codex P1/P2 and /bigbonk rounds all appear genuinely addressed. A few non-blocking observations below.

Comment thread packages/cloudflare/src/tpr.ts
Comment thread packages/cloudflare/src/cdn-warm-deployment.ts Outdated
Comment thread packages/vinext/src/server/worker-version.ts
@ask-bonk

ask-bonk Bot commented Jul 15, 2026

Copy link
Copy Markdown
Contributor

fatal: could not read Username for 'https://github.com': No such device or address

github run

@james-elicx

Copy link
Copy Markdown
Member

/bigbonk review for issues

@ask-bonk

ask-bonk Bot commented Jul 15, 2026

Copy link
Copy Markdown
Contributor

UnknownError: ProviderInitError

github run

@james-elicx

Copy link
Copy Markdown
Member

/bigbonk review for issues

@ask-bonk

ask-bonk Bot commented Jul 15, 2026

Copy link
Copy Markdown
Contributor

UnknownError: ProviderInitError

github run

@james-elicx

Copy link
Copy Markdown
Member

/bigbonk review for issues

@ask-bonk ask-bonk Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review: staged CDN warmup deployment

I read the full diff and the changed source (cdn-warm-deployment.ts, cdn-warm.ts, worker-version.ts, wrangler-deployment-target.ts, utils/toml.ts, tpr.ts, deploy.ts, init-cloudflare.ts, entry stamping) and ran tests/worker-version.test.ts + tests/cloudflare-cdn-warm.test.ts locally (27 pass).

Overall: the design is correct and I found no blocking issues. The structural insight holds: because Workers Cache partitions its key by invoked version ID (unless cross_version_cache), a plain 2xx/3xx cannot prove the uploaded version populated the cache. Verifying via x-vinext-worker-version + a Cloudflare-Workers-Version-Overrides override, with redirect: "manual" so a redirect is proven at its own cache key, is the right fix. The stage-at-0% → warm → promote → triggers-after-promotion ordering keeps the risky window in the already-safe state (previous @100%, new @0%), and strict mode fails closed by leaving the split rather than issuing an undo mutation. getZeroPercentStagingTraffic correctly rejects partial splits, drops stale 0% leftovers, and fails closed when the upload is already at 100%. The path-scoped-route carve-out (hasProductionRoute && !productionHost) is correct, and the custom_domains fallback now applies the same host-wide guard as routes.

Parity confirmed: stampWorkerVersion runs in both app-router-entry and pages-router-entry, and fetch-handler resolves to those, so all Worker-served responses carry the header. The prior Codex P1/P2 and the many /bigbonk rounds all appear genuinely addressed.

Two small, non-blocking observations below.

Comment thread packages/cloudflare/src/cdn-warm-deployment.ts Outdated
Comment thread packages/vinext/src/server/worker-version.ts
@ask-bonk

ask-bonk Bot commented Jul 15, 2026

Copy link
Copy Markdown
Contributor

fatal: could not read Username for 'https://github.com': No such device or address

github run

@james-elicx

Copy link
Copy Markdown
Member

/bigbonk review for issues

@ask-bonk ask-bonk Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review: staged CDN warmup deployment

I read the full diff and the new/changed source (cdn-warm-deployment.ts, cdn-warm.ts, worker-version.ts, wrangler-deployment-target.ts, utils/toml.ts, tpr.ts, deploy.ts, init-cloudflare.ts, entry stamping) and cross-checked the version-metadata binding shape and the version-override header format.

Overall: the design is correct and I found no blocking issues. The structural fix is right — because Workers Cache partitions its key by invoked version ID (unless cross_version_cache), a plain 2xx/3xx can't prove the uploaded version populated the cache, so verifying via x-vinext-worker-version + a Cloudflare-Workers-Version-Overrides override (with redirect: "manual" so a redirect proves at its own cache key) is the correct approach. The stage-at-0% -> warm -> promote -> triggers-after-promotion ordering keeps the risky window in the already-safe state, strict mode fails closed by leaving the split, and getZeroPercentStagingTraffic correctly rejects partial splits / drops stale 0% leftovers / fails closed when the upload is already 100%. stampWorkerVersion parity is complete: both app-router-entry and pages-router-entry stamp at the top-level fetch, and fetch-handler resolves to those. The .id field read from the version-metadata binding matches Cloudflare's binding shape.

The prior Codex P1/P2 and the many /bigbonk rounds all appear genuinely addressed. Everything below is non-blocking.

Comment thread packages/cloudflare/src/cdn-warm-deployment.ts
Comment thread packages/cloudflare/src/cdn-warm-deployment.ts
Comment thread packages/cloudflare/src/tpr.ts
@ask-bonk

ask-bonk Bot commented Jul 15, 2026

Copy link
Copy Markdown
Contributor

fatal: could not read Username for 'https://github.com': No such device or address

github run

@james-elicx

Copy link
Copy Markdown
Member

/bigbonk review for issues

@ask-bonk

ask-bonk Bot commented Jul 15, 2026

Copy link
Copy Markdown
Contributor

Independent review: staged CDN warmup deployment

I reviewed the full diff and read the new/changed source (cdn-warm-deployment.ts, cdn-warm.ts, worker-version.ts, wrangler-deployment-target.ts, utils/toml.ts, tpr.ts, deploy.ts, init-cloudflare.ts, entry stamping) and ran tests/worker-version.test.ts + tests/cloudflare-cdn-warm.test.ts locally (27 pass).

Overall: the design is correct and I found no blocking issues. The structural insight is sound — because Workers Cache partitions its key by invoked version ID (unless cross_version_cache), a plain 2xx/3xx cannot prove the uploaded version populated the cache, so verifying via x-vinext-worker-version + a Cloudflare-Workers-Version-Overrides override (with redirect: "manual" so a redirect proves at its own cache key) is the right fix. The stage-at-0% → warm → promote → triggers-after-promotion ordering keeps the risky window in the already-safe state, strict mode fails closed by leaving the split rather than issuing an undo mutation, and getZeroPercentStagingTraffic correctly rejects partial splits / drops stale 0% leftovers / fails closed when the upload is already at 100%.

Things I verified independently:

  • Traffic-split replacement: getZeroPercentStagingTraffic returns a 2-entry split which is handed to wrangler versions deploy a@100% b@0%; that command sets the full split, so any stale third version is correctly dropped.
  • Env-local cache inheritance: resolveCacheConfig only falls back to the top-level cache object when the env omits it entirely (config.env?.[envName]?.cache ?? config.cache), mirroring Wrangler's whole-object inheritance — so an env that sets cache = { enabled = true } does not inherit a top-level cross_version_cache = true. Matches the tests.
  • stampWorkerVersion parity: both app-router-entry and pages-router-entry stamp at the top-level fetch, and fetch-handler resolves to those, so all Worker-served responses carry the header. The status 0/101 guards for Response.error() and WebSocket upgrades, plus the immutable-headers clone fallback, are correct.
  • Path-scoped carve-out: hasProductionRoute && !productionHost refuses to warm workers.dev on a different cache key, and extractWarmupHostFromCustomDomains now routes custom_domains through the same routePatternToWarmupHost host-wide guard as routes.

The prior Codex P1/P2 and the many /bigbonk rounds all appear genuinely addressed. Two small non-blocking observations (both on unchanged lines, hence a top-level comment):

  1. cdn-warm.ts strict-mode throw (~line 321): the thrown strict error names only failures[0], and the warmAndPromote catch re-wraps it into the "staged at 0% and was not promoted" message, dropping the failure count entirely. The non-strict path already surfaces the warmed/total ratio in its warn; consider threading that ratio into the strict re-wrapped message too, so a CI log that truncates stdout still shows how many paths missed. Diagnostics only.

  2. tpr.ts extractTomlCacheConfig (~line 634): the non-greedy /^cache\s*=\s*\{([\s\S]*?)\}/m stops at the first }, which is correct for a flat cache table but would truncate if a value inside were itself an inline table (cache = { enabled = true, nested = { a = 1 } }). Wrangler's cache table has no nested-table fields today, so valid config can't hit this — just flagging the same non-nesting assumption shared with extractTomlVersionMetadataBinding. No change needed unless the schema gains a nested field.

@ask-bonk

ask-bonk Bot commented Jul 15, 2026

Copy link
Copy Markdown
Contributor

fatal: could not read Username for 'https://github.com': No such device or address

github run

Strict CDN warmup errors named the first failure but did not make the verified path count obvious after deployment wrapped the error with its safe-state message. This made truncated CI logs harder to diagnose.\n\nInclude the warmed path count alongside the failure count in the original error so the deployment wrapper preserves both values. Extend the strict staged-deployment regression test to assert the diagnostic and the existing safe-state guidance.
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.

--experimental-warm-cdn-cache re-caches the previous version on workers.dev (version-override header ignored), extending staleness instead of fixing it

2 participants