From d539692fd3a82cef28d01533d67bda6abaedb59a Mon Sep 17 00:00:00 2001 From: Zoltan Kochan Date: Fri, 9 Oct 2026 13:30:23 +0200 Subject: [PATCH] fix(network): stop capping the package manager's sockets at 15 by default `Http.getNetworkConfig()` filled in `maxSockets: 15` when `network.max_sockets` was unset, and that value reached the pnpm engine through the dependency resolver's network config. The Rust engine applies `maxSockets` as a hard per-registry limit on in-flight requests, so every install downloaded through at most 15 connections to the registry. The network config now leaves `maxSockets` unset unless the user configured it, as it already does for `networkConcurrency`, and the engine applies its own default. Bit's own HTTP agent keeps the default of 15, applied where the agent is built. Co-Authored-By: Claude Opus 5.5 --- pnpm-lock.yaml | 6 ++++++ scopes/scope/network/http/http.spec.ts | 12 ++++++++++++ scopes/scope/network/http/http.ts | 18 ++++++++++++++++-- 3 files changed, 34 insertions(+), 2 deletions(-) create mode 100644 scopes/scope/network/http/http.spec.ts diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 02491e81f76d..a3dddb9b55c6 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -23626,6 +23626,9 @@ importers: '@teambit/node.envs.node-babel-mocha': specifier: 2.1.3 version: 2.1.3(@babel/core@7.28.3)(supports-color@9.4.0) + '@types/chai': + specifier: 4.2.15 + version: 4.2.15 '@types/http-proxy-agent': specifier: 2.0.2 version: 2.0.2 @@ -23635,6 +23638,9 @@ importers: '@types/uuid': specifier: 8.3.4 version: 8.3.4 + chai: + specifier: 5.2.1 + version: 5.2.1 scopes/scope/objects: dependencies: diff --git a/scopes/scope/network/http/http.spec.ts b/scopes/scope/network/http/http.spec.ts new file mode 100644 index 000000000000..e7021838e78b --- /dev/null +++ b/scopes/scope/network/http/http.spec.ts @@ -0,0 +1,12 @@ +import { expect } from 'chai'; +import { DEFAULT_AGENT_MAX_SOCKETS, withAgentSocketDefault } from './http'; + +describe('withAgentSocketDefault()', () => { + it('applies the default when network.max_sockets is not configured', () => { + expect(withAgentSocketDefault({}).maxSockets).to.equal(DEFAULT_AGENT_MAX_SOCKETS); + }); + + it('keeps a configured value', () => { + expect(withAgentSocketDefault({ maxSockets: 40 }).maxSockets).to.equal(40); + }); +}); diff --git a/scopes/scope/network/http/http.ts b/scopes/scope/network/http/http.ts index 9218f26a0363..d17b357908c4 100644 --- a/scopes/scope/network/http/http.ts +++ b/scopes/scope/network/http/http.ts @@ -175,7 +175,11 @@ export class Http implements Network { fetchRetryMaxtimeout: getAsNumber(CFG_FETCH_RETRY_MAXTIMEOUT) ?? 60000, fetchTimeout: getAsNumber(CFG_FETCH_TIMEOUT) ?? 60000, localAddress: obj[CFG_LOCAL_ADDRESS], - maxSockets: getAsNumber(CFG_MAX_SOCKETS) ?? 15, + // No fallback on purpose, as for networkConcurrency below: the pnpm + // engine applies maxSockets as a hard per-registry cap, so a default + // of 15 throttled every install's downloads to 15 connections. Bit's + // own agent applies its default in `withAgentSocketDefault`. + maxSockets: getAsNumber(CFG_MAX_SOCKETS), // No fallback on purpose: when the user hasn't configured it, leave it // undefined so the package manager applies its own adaptive default // (pnpm: min(96, max(cpuCores * 3, 64))). A hardcoded 16 here overrode @@ -202,7 +206,7 @@ export class Http implements Network { } static async getAgent(uri: string, agentOpts: AgentOptions): Promise { - const agent = await getAgent(uri, agentOpts); + const agent = await getAgent(uri, withAgentSocketDefault(agentOpts)); return agent; } @@ -956,6 +960,16 @@ export class Http implements Network { } } +/** + * The socket limit of Bit's own HTTP agent when `network.max_sockets` is not + * configured. + */ +export const DEFAULT_AGENT_MAX_SOCKETS = 15; + +export function withAgentSocketDefault(agentOpts: T): T & { maxSockets: number } { + return { ...agentOpts, maxSockets: agentOpts.maxSockets ?? DEFAULT_AGENT_MAX_SOCKETS }; +} + export function getAuthHeader(token: string) { return { Authorization: `${DEFAULT_AUTH_TYPE} ${token}`,