[miniflare] Reconcile dev registry entries instead of deleting and recreating them - #15037
Conversation
…creating them Applying options called `unregisterWorkers()` unconditionally, unlinking every entry this instance had registered, before `register()` wrote them all back. Peers discover Workers by watching that directory, so every config update published a window in which this session appeared to have no Workers at all. `updateRegistryPath()` now only clears entries when the registry path actually changes, since those entries live in the directory being left behind, and `register()` reconciles the set: retained Workers are overwritten in place and only genuinely removed ones are unlinked.
🦋 Changeset detectedLatest commit: 13b5273 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 implementation is sound. Let me do a final analysis of edge cases and check for any real logic bug. One thing worth scrutinizing: the PR claims Let me verify there's no issue where the reconcile loop deletes a worker that should be kept because The code is correct, well-commented, tested, and has a proper changeset. Let me do a final confirmation there are no missed concerns with the The The change is correct, minimal, well-tested, and addresses a real bug (spurious registry churn causing peer crashes). The reconcile logic properly handles: workers removed (unlinked), workers kept (overwritten in place), path changes (cleared from old dir), and debug-port-unavailable (explicit withdrawal). No logic bugs, security issues, or backward-compat violations found. LGTM |
@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: |
workers-devprod
left a comment
There was a problem hiding this comment.
Codeowners reviews satisfied
|
Validation result: this does not fix the Windows flake, so it should be judged only as the churn fix it is. I lifted #15018's Windows skip on a throwaway branch (#15038) and ran the What the change does deliver, measured locally with a watcher on the registry directory during startup of three
So deletions are gone, and the aborts continue. That rules out deletion as the sole trigger, which is worth knowing: the earlier evidence that aborts followed a shrinking registry payload was real but incomplete. Every remaining event is a Worth noting how noisy those changes are: because the Vite plugin's internal Workers share hard-coded names (#15035), every session rewrites I'd still land this on its own merits — a config update shouldn't tell every peer that all your Workers vanished — but the Windows skip needs to stay. |
`name in workers` walks the prototype chain, so a Worker legitimately named
after an inherited property (`constructor`, `toString`) was reported as still
configured and its entry was never withdrawn. Use `Object.hasOwn`, with a test
covering it.
Also call `devRegistry.unregisterWorkers()` directly on the path where the debug
port is unavailable, rather than expressing "withdraw everything" as
`register({})`.
Dropping the unconditional unregister also dropped the safety net it provided when a reload never completes. `#runtime.updateConfig()` stops the existing workerd before starting its replacement and throws if the replacement fails, so `#registerWorkers()` is never reached and the entries written for the previous run stay on disk advertising a debug port nothing is listening on. Their 30s heartbeats keep touching the files, so the 5 minute stale sweep never reclaims them either. Withdraw this instance's entries when a config update or a crash restart fails to produce a running runtime, mirroring the missing-debug-port case.
|
Good catch — you're right, and it reproduces. Fixed in 13b5273. I confirmed the sequence you described: And the heartbeat detail matters exactly as you say: Now withdrawing the entries on both paths you identified — the failed update in Added a test that forces a rejected compatibility flag and asserts the entry is gone; verified it fails without the guard ( Full miniflare suite is green (1019). One unrelated flake in |
Applying options to a Miniflare instance removed every dev registry entry it had registered and then wrote them all straight back. Other dev sessions find Workers by watching that directory, so each config update published a window in which this session looked like it had no Workers at all.
Why that matters
A peer watching the registry sees the deletion, refreshes, and pushes the reduced set into its own running
workerd. If it had resolved one of the Workers that just vanished, it acts on that — and if the binding is atail_consumersedge, on Windows the peer's runtime aborts outright with*** std::terminate() called with no exception, taking down a Worker that never stopped running. That abort is reported upstream as cloudflare/workerd#6913.The delete was unconditional:
updateRegistryPath()calledunregisterWorkers()before checking whether the path had even changed, and#setOptions()calls it on the way through before re-registering.The change
updateRegistryPath()only clears entries when the registry path actually changes, since in that case they live in the directory being left behind.register()reconciles instead: Workers still present are overwritten in place, and only the ones genuinely gone are unlinked.Effect
Two
vite devsessions sharing a registry, counting deletions of entries belonging to a live session during normal startup:exported-handlerandworker-entrypoint-with-assetsBoth of those are Workers that were up and serving at the time, and each is one end of a mutual
tail_consumerspair — the shape that triggers the abort above.The new test asserts what a peer actually observes: it watches the registry directory with chokidar, the way the dev registry itself does, and fails if a config update produces an
unlinkfor a Worker that still exists. Onmainit reportsunlink:stable-worker. A second test covers the other direction, so removing a Worker from the config still withdraws its entry.Scope
This removes one source of spurious registry churn. I'm not claiming it fixes the
fixtures/dev-registryWindows flake on its own — that suite is skipped on Windows by #15018 and there is a second contributor in #15035, where the Vite plugin's internal Workers share hard-coded names across sessions and so overwrite and delete each other's entries. I'll validate against the un-skipped suite separately before proposing lifting that skip.A picture of a cute animal (not mandatory, but encouraged)
Note
This is a contribution from an AI agent: OpenCode, claude-opus-5.