Skip to content

[wrangler] serialize custom build watch events to avoid concurrent runs - #12704

Closed
dhruv7539 wants to merge 3 commits into
cloudflare:mainfrom
dhruv7539:codex/serialize-custom-build-watch-events-10944
Closed

[wrangler] serialize custom build watch events to avoid concurrent runs#12704
dhruv7539 wants to merge 3 commits into
cloudflare:mainfrom
dhruv7539:codex/serialize-custom-build-watch-events-10944

Conversation

@dhruv7539

@dhruv7539 dhruv7539 commented Feb 27, 2026

Copy link
Copy Markdown
Contributor

Summary

  • serialize custom build watch-triggered runs in BundlerController
  • keep only the latest pending file-change build request when many events arrive in a burst
  • preserve stale-build suppression by retaining the existing abort-controller checks

Why

Fixes the behavior in #10944 where rapid multi-file changes can start multiple custom builds at once.

Testing

  • pnpm -C packages/wrangler test src/__tests__/api/startDevWorker/BundleController.test.ts
  • pnpm -C packages/wrangler exec eslint src/api/startDevWorker/BundlerController.ts src/__tests__/api/startDevWorker/BundleController.test.ts

Added regression test

  • custom build watcher changes are processed without concurrent custom builds
  • uses a lock file in a test custom-build script to fail if two builds overlap
  • updates multiple watched files in quick succession and verifies no Custom build failed events are emitted

Open with Devin

@dhruv7539
dhruv7539 requested a review from a team as a code owner February 27, 2026 18:45
@changeset-bot

changeset-bot Bot commented Feb 27, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 9e7dab9

The changes in this PR will be included in the next version bump.

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

devin-ai-integration[bot]

This comment was marked as resolved.

@dhruv7539

Copy link
Copy Markdown
Contributor Author

Addressed both actionable comments in the latest push (8ae0df1):\n- fixed wiring in custom-build path to use \n- added a Wrangler patch changeset ()

@dhruv7539

Copy link
Copy Markdown
Contributor Author

Addressed both actionable comments in the latest push (8ae0df1ef):

  • fixed jsxFragment wiring in BundlerController custom-build path to use config.build.jsxFragment
  • added a Wrangler patch changeset (.changeset/quick-keys-smell.md)

@petebacondarwin
petebacondarwin force-pushed the codex/serialize-custom-build-watch-events-10944 branch from 8ae0df1 to 90cb61d Compare April 15, 2026 06:28
@workers-devprod

Copy link
Copy Markdown
Contributor

Codeowners approval required for this PR:

  • @cloudflare/wrangler
Show detailed file reviewers
  • packages/wrangler/src/tests/api/startDevWorker/BundleController.test.ts: [@cloudflare/wrangler]
  • packages/wrangler/src/api/startDevWorker/BundlerController.ts: [@cloudflare/wrangler]

@devin-ai-integration devin-ai-integration 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.

Devin Review found 1 new potential issue.

View 7 additional findings in Devin Review.

Open in Devin Review

);
});

test("custom build watcher changes are processed without concurrent custom builds", async () => {

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.

🟡 New test uses global expect instead of test context ({ expect }), violating AGENTS.md rule

The new test at line 258 uses async () => { without destructuring expect from the test context, then calls expect(customBuildErrors).toHaveLength(0) at line 313 using the global expect. This violates the explicit rule in packages/wrangler/AGENTS.md: "Never import expect from vitest — use test context ({ expect }) => {}". Every other test in this file (lines 72, 132, 185, 318, 371, 493, 603, 644) correctly uses the async ({ expect }) => { pattern.

Suggested change
test("custom build watcher changes are processed without concurrent custom builds", async () => {
test("custom build watcher changes are processed without concurrent custom builds", async ({ expect }) => {
Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

@petebacondarwin
petebacondarwin requested review from a team and vicb and removed request for a team April 15, 2026 06:53
@petebacondarwin
petebacondarwin force-pushed the codex/serialize-custom-build-watch-events-10944 branch from 90cb61d to c5495bd Compare April 15, 2026 08:18
@github-project-automation github-project-automation Bot moved this to Untriaged in workers-sdk Apr 15, 2026
@pkg-pr-new

pkg-pr-new Bot commented Apr 15, 2026

Copy link
Copy Markdown
create-cloudflare

npm i https://pkg.pr.new/create-cloudflare@12704

@cloudflare/kv-asset-handler

npm i https://pkg.pr.new/@cloudflare/kv-asset-handler@12704

miniflare

npm i https://pkg.pr.new/miniflare@12704

@cloudflare/pages-shared

npm i https://pkg.pr.new/@cloudflare/pages-shared@12704

@cloudflare/unenv-preset

npm i https://pkg.pr.new/@cloudflare/unenv-preset@12704

@cloudflare/vite-plugin

npm i https://pkg.pr.new/@cloudflare/vite-plugin@12704

@cloudflare/vitest-pool-workers

npm i https://pkg.pr.new/@cloudflare/vitest-pool-workers@12704

@cloudflare/workers-editor-shared

npm i https://pkg.pr.new/@cloudflare/workers-editor-shared@12704

wrangler

npm i https://pkg.pr.new/wrangler@12704

commit: c5495bd

@workers-devprod

Copy link
Copy Markdown
Contributor

Codeowners approval required for this PR:

  • @cloudflare/wrangler
Show detailed file reviewers
  • packages/wrangler/src/tests/api/startDevWorker/BundleController.test.ts: [@cloudflare/wrangler]
  • packages/wrangler/src/api/startDevWorker/BundlerController.ts: [@cloudflare/wrangler]

@devin-ai-integration devin-ai-integration 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.

Devin Review found 1 new potential issue.

View 10 additional findings in Devin Review.

Open in Devin Review

Comment on lines +67 to +74
while (this.#pendingCustomBuild) {
const nextBuild = this.#pendingCustomBuild;
this.#pendingCustomBuild = undefined;
await this.#runCustomBuild(
nextBuild.config,
nextBuild.filePath,
nextBuild.buildAborter
);

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.

🟡 Stale pending custom build executes its external command when aborter is already aborted

When a config update (#startCustomBuild) occurs while a custom build is pending in the queue, #startCustomBuild at BundlerController.ts:230 aborts this.#customBuildAborter (which is the same AbortController stored in #pendingCustomBuild.buildAborter). However, #runPendingCustomBuilds at line 67-74 does not check buildAborter.signal.aborted before dequeuing and running the stale pending build, and #runCustomBuild at line 88-89 emits a bundleStart event and executes the external build command before checking the abort signal at line 101. This means a build known to be cancelled still runs its external command, emits a spurious bundleStart (without a matching bundleComplete), and if the external command fails, emits a confusing error event to downstream consumers (ProxyController, RuntimeControllers per DevEnv.ts:101-104). Adding an early abort guard would avoid this unnecessary work.

Suggested change
while (this.#pendingCustomBuild) {
const nextBuild = this.#pendingCustomBuild;
this.#pendingCustomBuild = undefined;
await this.#runCustomBuild(
nextBuild.config,
nextBuild.filePath,
nextBuild.buildAborter
);
while (this.#pendingCustomBuild) {
const nextBuild = this.#pendingCustomBuild;
this.#pendingCustomBuild = undefined;
if (nextBuild.buildAborter.signal.aborted) {
continue;
}
await this.#runCustomBuild(
nextBuild.config,
nextBuild.filePath,
nextBuild.buildAborter
);
}
Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

@petebacondarwin

Copy link
Copy Markdown
Contributor

There are outstanding review comments from Devin to address. Marking as draft while these are resolved.

@petebacondarwin
petebacondarwin marked this pull request as draft April 25, 2026 13:09
@petebacondarwin

Copy link
Copy Markdown
Contributor

Hi @dhruv7539 — we're doing some cleanup of stale draft PRs.

This PR has been quiet since 2026-04-25. Could you let us know within 14 days whether you're still planning to continue?

  • If yes: please leave a comment with a status update or push a rebase, and we'll keep it open.
  • If no: no need to reply — we'll close it after 14 days, and you can always reopen later if you pick it back up.

Thanks!

@github-project-automation github-project-automation Bot moved this from Untriaged to Done in workers-sdk Jun 29, 2026
@github-project-automation github-project-automation Bot moved this from Done to Untriaged in workers-sdk Jun 29, 2026
@petebacondarwin

Copy link
Copy Markdown
Contributor

Thanks for this @dhruv7539, and sorry it sat for so long. The code this touches was rewritten by #14462 and #14561 in the meantime, so rather than ask you to untangle the conflicts I've re-implemented your fix on top of current main in #14936, with you as co-author.

Your approach (serialize the watcher-triggered builds, keep only the latest pending request) is what landed there. On top of it: the watcher events are now debounced so a burst collapses to a single build, and the regression test needed a liveness-aware lock file — since #14462 a superseded build is killed and leaves its lock behind, which would have made the original test's existsSync check fail spuriously.

Closing this as superseded by #14936. Thanks again for the fix and for the report triage on #10944.

@github-project-automation github-project-automation Bot moved this from Untriaged to Done in workers-sdk Jul 30, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

4 participants