body-drain: add -c concurrency and -idle-per-host to the client

Bounded concurrency via semaphore+WaitGroup, atomic counters, stop-on-first-
error without cancelling in-flight requests (cancel would hide the leaks).
Exposes MaxIdleConnsPerHost so pool thrash under concurrency is observable.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GbnJj9ZqEx9nUvzkjdwfMq
This commit is contained in:
2026-08-29 00:08:27 +02:00
co-authored by Claude Fable 5
parent f80ba127d8
commit 9230d0cd3d
2 changed files with 100 additions and 23 deletions
+27
View File
@@ -8,6 +8,33 @@ go run ./99-experiments/body-drain -mode all
go run ./99-experiments/body-drain -mode leak -n 200
```
## Concurrency: `-c` and `-idle-per-host`
`-c N` keeps up to N requests in flight (semaphore + WaitGroup, the same
shape as exercise 01). `-idle-per-host` sets `Transport.MaxIdleConnsPerHost`
(default 10; Go's own default is only **2**).
```
go run ./99-experiments/body-drain -mode drain -n 200 -c 8
# httptrace GotConn : reused=192 fresh=8 ← one conn per worker, then reused
# server new conns : 8
go run ./99-experiments/body-drain -mode drain -n 200 -c 8 -idle-per-host 2
# server new conns : 13+ ← pool too small: conns returned
# while 2 are already idle get CLOSED
```
That second run is the trap in exercise 01: `limit` goroutines against one
host with the default transport means only 2 conns are ever kept, the rest are
dialed and thrown away on every round — even though you drained the body
correctly. On loopback the effect is small; against a TLS host each throwaway
is a full handshake. Raise `MaxIdleConnsPerHost` to your concurrency.
With `-c` the leak modes also exhaust fds N times faster, and failures no
longer stop in-flight requests: cancelling a request context closes its body
and would clean up the very leaks we want to see, so the client only stops
*launching* new requests after the first error.
## Two processes: watch each side run out of fds on its own
In one process the client and server share one fd limit and each leaked