[vite-plugin] Don't advertise a dev session's runtime until it is final - #15024
[vite-plugin] Don't advertise a dev session's runtime until it is final#15024petebacondarwin wants to merge 1 commit into
Conversation
🦋 Changeset detectedLatest commit: c1da2d7 The changes in this PR will be included in the next version bump. This PR includes changesets to release 8 packages
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 |
|
Codeowners approval required for this PR:
Show detailed file reviewers
|
|
✅ All changesets look good |
|
The config includes an One thing worth verifying: The implementation and tests are correct and well-reasoned. Let me do a final check on whether there could be an issue with Let me verify the The change is well-designed, correct, tested, and documented. Let me confirm there are no lint concerns like floating promises in the new code (the fixture sampler IIFE is awaited via The code is clean. No actionable logic bugs, security issues, backward-compat violations, or incorrect API behavior. The implementation correctly:
LGTM |
d699b3f to
cb72c40
Compare
@cloudflare/autoconfig
@cloudflare/build-output-utils
@cloudflare/config
create-cloudflare
@cloudflare/deploy-helpers
@cloudflare/kv-asset-handler
miniflare
@cloudflare/pages-functions
@cloudflare/pages-shared
@cloudflare/unenv-preset
@cloudflare/vite-plugin
@cloudflare/vitest-pool-workers
@cloudflare/workers-auth
@cloudflare/workers-editor-shared
@cloudflare/workers-utils
wrangler
commit: |
The plugin starts workerd twice: once to discover each Worker's exports by running it, then again with a config built from what it found. The first runtime was published to the dev registry and then torn down, leaving peers holding a debug port that no longer existed. On Windows a peer with a tail_consumers edge to it aborts its own workerd with std::terminate. Miniflare gains unsafeDeferDevRegistryRegistration to hold back self-advertisement, and unsafeRegisterInDevRegistry() to release it once the runtime is final. Reading the registry is unaffected, so a starting session still resolves Workers from sessions already running.
cb72c40 to
c1da2d7
Compare
A
vite devsession publishes aworkerddebug port to the dev registry and then immediately replaces the runtime behind it, so another dev session can be left holding an address that no longer exists.What happens
The Vite plugin brings
workerdup twice while starting: once to discover each Worker's exports by running it, then again with a config assembled from what it found (dev.ts, thehasChangedbranch). The first runtime is advertised in the dev registry and then torn down. A peer that resolves the Worker during that window gets a debug port that is about to disappear — and on Windows, a peer with atail_consumersedge to it can abort its own runtime with*** std::terminate() called with no exception(reported upstream as cloudflare/workerd#6913).Miniflare already unregisters before restarting, but peers only learn by watching the registry directory (polling on Windows) and then pushing the update into their own proxy Worker. That propagation loses the race against a ~150ms restart, so this closes the window rather than trying to shrink it.
The change
miniflaregainsunsafeDeferDevRegistryRegistration, which holds back advertising this instance's Workers, andunsafeRegisterInDevRegistry()to release the hold once the runtime is final. The hold is re-armed by eachsetOptions()that asks to defer, so a dev server restart gets the same protection. Reading the registry is untouched, so a starting session still resolves Workers from sessions already running.@cloudflare/vite-plugindefers registration and releases it at the end ofconfigureServer, unconditionally — so it applies whether or not the export types turned out to differ.The fixture test asserts the invariant directly: exactly one debug port is ever advertised for a Worker during startup. Without the fix it observes two.
Scope — this does not fix the Windows flake
fixtures/dev-registryis currently skipped on Windows (#15018). I temporarily re-enabled it and ran thevite dev <-> vite devsuite four times on a Windows runner to check whether this change is enough to lift that skip. It is not. One of four rounds ofsupports exported handler fetch over service bindingstill timed out at 50s, and threestd::terminateaborts still occurred.So this is a real bug with a targeted fix and a regression test, but the skip in #15018 should stay until the remaining trigger is found. I'm continuing to investigate that separately; the leading suspect is that
#registerWorkersadvertises every named Worker, and the plugin names its internals with global constants (__asset-worker__,__router-worker__,__vite_proxy_worker__), so concurrent vite sessions overwrite and delete each other's entries in the shared registry.unsafe*internal APIs for consumers that stage their runtime startup, and the user-visible effect is a bug fix.Four Miniflare tests plus one fixture test, all confirmed to fail without the change.
A picture of a cute animal (not mandatory, but encouraged)
Note
This is a contribution from an AI agent: OpenCode, claude-opus-5.