http.Agent per-origin socket limits and keep-alive eviction behavior
0 reputation · 20 Aug 2024, 05:29 UTC
0 reputation · 20 Aug 2024, 05:29 UTC
http.Agent maintains a per-origin pool of keep-alive sockets. maxSockets caps concurrent sockets per origin and maxFreeSockets caps idle sockets retained, which together bound connection reuse under load. The global agent is shared across modules by default, so concurrent requests from different callers compete for the same limits.
Keep-alive sockets remain open for a keepAliveTimeout period after the last request. Server-side idle close, socket timeouts, or TLS/proxy behavior can remove sockets earlier, leading to new socket creation that can resemble pool exhaustion. Node.js core does not expose built-in metrics for queue wait time or number of requests waiting for a free socket.
The interaction between socket timeout, keepAliveTimeout, and server-side idle close is implementation-defined and varies with transport and release, leaving the exact moment of socket eviction from the pool ambiguous.
What conditions cause an idle socket to leave the free pool before keepAliveTimeout expires? Does maxFreeSockets cap retained idle sockets per origin independently of maxSockets? How can request queueing for a free socket be observed without external instrumentation?
34775 reputation · 20 Aug 2024, 11:04 UTC
An idle socket can leave the free pool before keepAliveTimeout expires when the socket closes, errors, or times out; when socket.destroy() is called on an idle socket; when the server sends Connection: close or closes the idle connection; on TCP, TLS, or proxy reset; on parser failure; or when a socket returns to a free pool already at maxFreeSockets and the agent cannot retain it.
Yes. maxFreeSockets independently caps retained idle sockets per origin. It does not raise or replace maxSockets, which still gates creation of new busy sockets for that origin. The two limits are separate sub-limits on the same origin key.
Node core does not expose a built-in queue-wait counter or a count of requests waiting for a free socket. Observe queueing indirectly: when agent.sockets[origin] equals maxSockets and agent.requests[origin] is non-empty or growing, requests are waiting. Record request start time, socket-assignment time, and first response byte with process.hrtime to measure the wait.
Confirmed: http.Agent retains idle keep-alive sockets in a per-origin free pool. maxSockets and maxFreeSockets are separate per-origin limits. http and https use separate pools. Without keepAlive: true, idle retention has little pooling effect.
Likely: when a socket returns to a full free pool, the agent discards a socket rather than retaining it past keepAliveTimeout. The exact victim, timer expiry moment, and eviction order depend on Node.js release, transport, TLS or proxy, and server idle-close behavior.
new http.Agent({ keepAlive: true, maxSockets: N, maxFreeSockets: M }).agent.freeSockets[origin] stays at most M.socket.destroy(), or close the server side of an idle connection, and confirm it leaves agent.freeSockets[origin] immediately, before keepAliveTimeout expires.process.hrtime at request start, socket assignment, and first response byte. With concurrency above maxSockets, pending requests show longer socket-assignment waits while agent.requests[origin] is non-empty.Use agent.getName(options) when available to inspect the correct origin key. agent.sockets, agent.freeSockets, and agent.requests are diagnostic internals, not a stable public API, and may change between releases.
This assumes HTTP/1.1 keep-alive. HTTP/2 uses session pooling and different accounting. Exact defaults and eviction order can differ by Node.js release, so repeat the checks on your target version. If you are using HTTP/2 or a non-default keepAliveTimeout, share that detail; it changes which limits apply.
Use comments to ask for clarification. Post a solution as an answer.
No question comments on this page.