[miniflare] Share local storage across processes via a single owner (experimental)#14449
Draft
penalosa wants to merge 4 commits into
Draft
[miniflare] Share local storage across processes via a single owner (experimental)#14449penalosa wants to merge 4 commits into
penalosa wants to merge 4 commits into
Conversation
… props Move the per-binding configuration for storage and remote (mixed-mode) bindings out of per-resource workerd services and into runtime `ctx.props`, so a single shared service can serve any number of bindings. - Local KV namespaces now share one entry service; the namespace id is passed via props and resolved in object-entry.worker.ts (idFromName). - remoteProxyClientWorker() is now script-only; the connection string, binding name and trace id travel via props (buildRemoteProxyProps), read in remote-proxy-client.worker.ts and the dispatch-namespace proxy. - All remote-binding plugins emit one shared remote-proxy service instead of one per resource. - explorer.ts reads the KV namespace id from binding props rather than parsing it out of the service name.
🦋 Changeset detectedLatest commit: ae287f4 The changes in this PR will be included in the next version bump. This PR includes changesets to release 6 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 |
Contributor
|
UnknownError: ProviderInitError |
Contributor
|
@penalosa Bonk workflow failed. Check the logs for details. View workflow run · To retry, trigger Bonk again. |
Contributor
|
✅ All changesets look good |
@cloudflare/autoconfig
create-cloudflare
@cloudflare/deploy-helpers
@cloudflare/kv-asset-handler
miniflare
@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: |
…boundary Adds an experimental option that makes a single detached process own local storage for a given persist root, so multiple Miniflare instances (e.g. several wrangler dev / vite dev sessions) no longer each open the same SQLite/blob files — the root cause of cross-process SQLITE_BUSY errors. The first instance to find no published owner elects itself (via a per-persist -root spawn lock) and launches a detached owner process. Every instance then routes its storage operations to the owner over the remote-bindings boundary — fetch for KV/R2/D1/Images and capnweb JSRPC for Streams/Secrets Store — and skips standing up its own local storage. Bindings are repointed by id (type:id) so the owner avoids binding-name collisions and resolves dynamic ids via the MF-Storage-Owner-Namespace header. The owner publishes its address, heartbeats it, and self-terminates once no instances remain. Cache, Durable Objects and Workflows stay per-instance via isolateLocalStorage.
c8f6d2d to
cb05917
Compare
Unit tests for owner election, presence, routing and teardown, plus a high -concurrency oracle that runs multiple instances against one persist root and asserts KV/R2/D1/Streams/Secrets/Images sharing with exactly-correct results under contention. The oracle runs under MINIFLARE_TEST_SHARED_OWNER (added to turbo.json globalPassThroughEnv) so it gates both feature-on and baseline modes.
cb05917 to
0e7a7ed
Compare
Extract the shared server logic for the remote-bindings boundary (JSRPC vs fetch dispatch, MF-Binding / MF-URL / MF-Header-* handling) into a single parameterised module, `remote-bindings-proxy-server.ts`, and have both consumers adapt to it: - Miniflare's storage-owner server passes a "<type>:<id>" resolver that forwards the id via the MF-Storage-Owner-Namespace header. - Wrangler's ProxyServerWorker passes the existing env[binding] resolver with its SendEmail / Dispatch Namespace special-cases. The two previously near-identical implementations now share one source of truth; behaviour is unchanged (wrangler remote-bindings and miniflare storage-owner suites both pass).
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What this does
Adds an experimental option to Miniflare that makes a single process own local storage for a given persist root, so multiple Miniflare instances (e.g. several
wrangler dev/vite devsessions) no longer each open the same SQLite/blob files — the root cause of cross-processSQLITE_BUSYerrors under concurrent access.When enabled:
Transport
Routing reuses the existing remote-bindings boundary rather than a bespoke channel:
Bindings are repointed by id (
type:id) against the owner server, so the owner avoids binding-name collisions across instances and resolves dynamic ids via anMF-Storage-Owner-Namespacerequest header. This replaces an earlier workerd-debug-port prototype and drops the previousunsafeDevRegistryPathrequirement.The owner-side server logic is shared with Wrangler's remote-bindings proxy server — both are thin adapters over one parameterised
remote-bindings-proxy-servermodule (JSRPC-vs-fetch dispatch and theMF-Binding/MF-URL/MF-Header-*wire protocol), each supplying its own binding-resolution strategy.What is and isn't shared
isolateLocalStorage): Cache, Durable Objects, Workflows — when the feature is on and no explicit*Persistpath is set, these use a per-instance temp path.*Persistpath for a given type always takes precedence and excludes that type from routing.This also lands a supporting refactor: storage and remote (mixed-mode) bindings now carry their per-resource config (namespace/bucket/database id, remote connection string) via
ctx.propsagainst a single shared object-entry / proxy service, instead of one workerd service per resource.Validation
A high-concurrency oracle runs multiple instances against one persist root and asserts cross-instance sharing with exactly-correct results under contention:
MINIFLARE_TEST_SHARED_OWNERadded toturbo.jsonglobalPassThroughEnvso it gates CI. The same tests also pass in baseline (feature-off) mode, where sharing is provided by the persist root.Status — draft
Known follow-ups before this is review-ready:
unsafe*flag not intended for general use yet; user-facing docs will follow once the design stabilises and it is opted into by Wrangler.