fix(runtime): clamp fixed-port applications to one worker when reusePort is not available - #5076
Open
mcollina wants to merge 1 commit into
Open
Conversation
…sePort The guard that clamped a multi-worker application to a single worker on platforms without SO_REUSEPORT (macOS, Windows) was removed with the entrypoint in #5014, since it was gated on the entrypoint flag. Restore it gated on the application listener configuration: since the listener is owned by the capability, the runtime sets up the first worker, inspects its configuration and, if the application would share a fixed port between multiple workers (i.e. `server.portAssignment` is not `perWorkerIncrement`), warns and clamps the workers to 1, disabling dynamic scaling as well. Also make the failure meaningful when workers of the same application still collide on a port (e.g. after a manual scale up): - the start error sent by the worker keeps its properties (like `port`) when crossing the thread boundary; - the runtime reports `PLT_RUNTIME_WORKER_EADDR_IN_USE`, naming the application, the reusePort constraint and the `portAssignment` fix, when the port is held by another worker of the same application; - `@platformatic/node` no longer reports a listen error of a factory application as an unhandled rejection, which made the worker exit before the start error could reach the runtime (start timeout). Fixes #5070 Assisted-by: Claude Code:claude-fable-5 Signed-off-by: Matteo Collina <hello@matteocollina.com>
mcollina
force-pushed
the
fix/clamp-workers-without-reuseport
branch
from
August 16, 2026 00:46
447127d to
edf8393
Compare
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.
Fixes #5070
Summary
The guard that clamped a multi-worker application to a single worker on platforms without
SO_REUSEPORT(macOS, Windows) was removed with the entrypoint in #5014, because it was gated onapplication.entrypoint. On those platforms an application with a fixedserver.portandworkers > 1failed to start its additional workers with a raw (and, for@platformatic/nodefactories, delayed) error instead of degrading with a warning.Changes
packages/runtime/lib/runtime.js,#setupApplication): the listener is owned by the capability, so whenreusePortis not available and the application hasworkers > 1(or dynamic scaling), the runtime sets up the first worker, reads its configuration via ITC (getApplicationConfig) and, ifserver.portis a fixed port andserver.portAssignmentis notperWorkerIncrement, logs a warning naming the OS limitation and theportAssignmentalternative, setsworkersto{ dynamic: false, static: 1 }(which also disables the dynamic scaler for that application) and skips the other workers. Applications withperWorkerIncrement, port0, or no managed listener are unaffected.updateApplicationsResourcesscale up, or withreuseTcpPorts: false):packages/runtime/lib/worker/itc.js: thestarthandler now sends the failure as a plain object, so error properties such asport/addresssurvive the structured clone (previously theerror.portcheck in#startWorkercould never match through ITC).#startWorkerreports the newPLT_RUNTIME_WORKER_EADDR_IN_USE(WorkerAddressInUseError) when the port is held by another worker of the same application, keepsPLT_RUNTIME_EADDR_IN_USEfor another application, and leaves the originalEADDRINUSEwhen the owner is an external process (instead of the odd"another process" and "app"message). Both codes are non-retryable.@platformatic/nodeunhandled-rejection fix, which turned a listen error into a 30s start timeout, moved to fix(runtime): restore per-worker port assignment (portAssignment: perWorkerIncrement) #5075 since its Windows CI needed it.)workers/reuseTcpPortssections anderrors.md.Tests
packages/runtime/test/multiple-workers/no-reuse-port.test.js: simulates the missingreusePortfeature (the runtime runs in-process, sofeatures.node.reusePortis patched) and covers the clamp with static workers, the dynamic scaling case, no clamp withperWorkerIncrementand with an ephemeral port, plus the same-application collision error (forced withreuseTcpPorts: false, so it runs on Linux too).packages/runtime/test/ports.test.jsnow asserts the exactPLT_RUNTIME_EADDR_IN_USEerror for two applications on the same port.Assisted-by: Claude Code:claude-fable-5