3–4×
the useful work, spent on set-up
A new client pays DNS, TCP and TLS before the request, 92 ms at a 30 ms round trip, against 30 ms on a warm connection.
Dispose per call, as reviews ask?
Bad: using around each call
A new pool, connection and parked port on every request.
Good: Own it per process
Create at startup, share across threads, dispose at shutdown.
28,232 ports ÷ 60 s ≈ 470 new connections a second
Each closed connection holds its port in TIME_WAIT for a minute. At 400 a second that’s 24,000 parked ports, and a 20% bump tips you over.
Behind a NAT gateway, the whole subnet shares one budget
Ten VMs each open 150 connections a second to one bank, all under 470. Together, 1,500 a second fill the gateway in about 43 s.
Dashboards show slow calls, not why. Which one number points straight at per-request clients?
The ratio
New outbound connections ÷ requests, per dependency. Near 1 is this anti-pattern; near 0 is a shared pool.
Create once, share safely
- One client per dependency, built at startup
- Per-request headers on the request, not the client
- Recycle connections every few minutes for DNS
- Cap each pool; dispose once at shutdown
0.1/s
new connections after the fix, down from 400
Twelve pooled connections, recycled every 120 s. TIME_WAIT falls from 24,000 sockets to a few dozen, and outbound p50 from 92 ms to about 30.
Say it like this
Clients that own connections live as long as the process. Create one per dependency at startup, share it, cap its pool, recycle connections for DNS, and put per-request data on the request.