I'm building hostprobe, a small internal service that cron and a few scripts POST to as /probe?host=NAME. Four workers, queue of 8, and the probe is a 20s sleep for now (the ssh check comes later). I want it to stay small when a script goes wild.
Test: hey -n 100 -c 100 against it. All 100 came back 202 in a few ms. I expected the 9th onward to be slowed or rejected. /stats says queue len=8 cap=8 and 4 running, so the channel and workers look right. But runtime.NumGoroutine() is about 100, and /debug/pprof/goroutine?debug=1 shows one big group sitting at the same spot. With 1000 requests it went to about 1000 while the queue stayed at 8. Activity Monitor memory also climbs a bit with each burst.
I haven't touched the handler or pool in a while, so I'm basically staring at code I forgot. Handler:
job := Job{Host: host, Enqueued: time.Now()}
go func() {
q.jobs <- job
}()
atomic.AddInt64(&q.accepted, 1)
w.WriteHeader(http.StatusAccepted)
fmt.Fprintf(w, "queued %s\n", host)
The pool is the usual for job := range q.jobs in 4 goroutines, and make(chan Job, cfg.QueueDepth).
Where are the extra goroutines coming from? I suspect the workers or the channel, but the counts say both are fine.