node: make the isolate the port namespace for http and net servers - #7357
Merged
Merged
Conversation
The virtual port table is shared state, and the isolate is the one unit that shares state: module scope and top-level listen() run once per isolate, and the platform routes between isolates before a port is consulted. #7306 gave each Durable Object instance its own table, which made a listen() at module top-level and one in a Durable Object handler bind in different tables in the same worker, and left a server registered during module evaluation inside a Durable Object invisible to httpServerHandler from a stateless request. One table per isolate, for http and net alike, as in Node. Removes the IoContext scope key, the per-scope host address, and the table threading through socket owners.
Contributor
|
I'm Bonk, and I've done a quick review of your PR. Makes Node HTTP/net ports isolate-scoped across Durable Objects.
Time for a pun! This port scope needs an actor to keep it in context. |
jasnell
approved these changes
Sep 14, 2026
petebacondarwin
approved these changes
Sep 14, 2026
petebacondarwin
left a comment
Contributor
There was a problem hiding this comment.
This should resolve the Vite/Vitest regression, thanks!
Are there any concerns about ports being broken when a DO gets moved from one metal to another? I assume that it doesn't matter since the DO must have died (or is killed) and so any sockets will already get closed and need to be re-opened.
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.
Follow-on to #7306, which made the virtual port table per-DO, when instead it should be per-isolate.
#7306 gave each Durable Object instance its own virtual port table, with the isolate's table underneath it. That made the scope of a
listen()depend on where it was called: at module top-level it bound in the isolate table, in a Durable Object constructor or handler it bound in that instance's table, in the same worker. It also broke the Vite and Vitest plugins, which evaluate user modules inside a runner Durable Object: a top-levelserver.listen(8080)landed in the runner instance's table andhttpServerHandlerfrom the stateless entrypoint could not find it (cloudflare/workers-sdk CI).This fixes the regressed DO behavior, with the simpler per-isolate scoping model, which is also the correct model since the isolate is the VM abstraction not the DO:
tcpPortstable per isolate;IoContext::getPortScopeKeyandSocketsModule::getPortScopeKeyare removed240.1.0.1) rather than one per Durable Object instance; the gateway address is unchangedbindPort()drops its table parameter,BoundSocket,net.Server,net.Socket, andhttp.ServerusetcpPortsdirectly, andkBoundTablebecomes akBoundReservedflagTests:
testDurableObjectScopingbecomestestDurableObjectsShareIsolatePorts, covering anet.Serverlistened on in one instance holding the port against another instance, the entrypoint, and aBoundSocket, being reached throughstub.connect()on either instance with the isolate host address and gateway peer, reservations shared and released across instances and the entrypoint, and anhttp.Serverlistened on inside an instance served throughhttpServerHandlerfrom the entrypoint and another instance, with the reverse direction and the not-found error after close.