Node.js HTTPS Agent keepAlive: Impact on Integration Test Performance Without Production Credentials
0 reputation · 25 Mar 2021, 11:40 UTC
0 reputation · 25 Mar 2021, 11:40 UTC
When running integration tests that hit an HTTPS endpoint without using production credentials, the default keepAlive setting on the https.Agent may influence both the number of TLS handshakes and the overall test duration. The Node.js documentation states that keepAlive defaults to false, but the actual behavior can vary between LTS and current releases (e.g., Node.js 12 through 18).
Enabling keepAlive can reduce latency by reusing TCP sockets, yet it also risks concealing bugs that manifest only when each request establishes a fresh connection. In a test environment that does not replicate production credentials, it is unclear whether the performance gains outweigh the potential masking of connection‑related issues.
Key constraints include:
keepAlive is toggled.Unresolved questions:
keepAlive consistently reduce TLS handshakes across Node.js 12‑18 in integration tests?keepAlive mask connection teardown bugs that only appear when each request uses a new socket?keepAlive setting, or should they run with keepAlive disabled to surface hidden issues?34775 reputation · 25 Mar 2021, 22:05 UTC
Enabling keepAlive: true in a Node.js https.Agent consistently reduces the number of TLS handshakes by reusing existing TCP sockets for subsequent requests. This typically results in lower overall test duration and reduced CPU overhead, regardless of whether production credentials are used. The lack of production credentials (e.g., using self-signed certificates or test stubs) does not prevent socket reuse, as keepAlive operates at the transport layer after the initial TLS handshake is complete.
While keepAlive improves speed, it introduces a trade-off between performance and bug discovery:
keepAlive can mask issues. If your production environment frequently closes connections or has strict timeout policies, a test suite using a persistent agent may fail to surface ECONNRESET errors or handshake failures that occur only during the initial connection phase.keepAlive is disabled, sockets are closed immediately after the response is consumed. With it enabled, sockets remain open in a pool. If the test runner (e.g., Jest or Mocha) does not explicitly destroy the agent or if the server closes the connection unexpectedly, you may encounter stale socket errors or hanging processes.To balance performance with reliability, mimic the production keepAlive setting but implement a shared agent to avoid ephemeral port exhaustion. For Node.js 12–18, avoid relying on global defaults and explicitly inject the agent into your HTTP client (e.g., Axios or node-fetch).
const https = require('https');
// Create a shared agent to be reused across the test suite
const testAgent = new https.Agent({
keepAlive: true,
maxSockets: 10, // Prevent overloading the test server
rejectUnauthorized: false // Use only if using self-signed test certs
});
// Pass this agent to your request library
// Example: axios.get(url, { httpsAgent: testAgent });
keepAlive agent. Compare total wall-clock time and the number of established connections on the server side.process._getActiveHandles() to see if the https.Agent is keeping sockets open.res.data read or stream ended), as sockets cannot be returned to the pool until the response is complete.Missing Diagnostic: To provide a more precise recommendation on maxSockets and timeout settings, please specify the average number of concurrent requests your integration tests trigger per second.
Use comments to ask for clarification. Post a solution as an answer.
34,775 reputation · 25 Mar 2021, 18:29 UTC
A practical way to quantify the handshake reduction is to instrument the test server itself: listen for the secureConnection event and increment a counter. With keepAlive enabled, that count should be far lower than the number of requests; with it disabled, it should equal the request count. This gives a direct, reproducible metric without needing production credentials.
Also be aware that a shared agent across parallel tests can exhaust maxSockets. Use a fresh agent per test or set maxSockets deliberately, and always call agent.destroy() in teardown to avoid lingering sockets that could cause cross-test interference.