You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
The statsd subsystem has reliability and efficiency problems that matter most in monitor mode but are visible everywhere:
Buffer of 100 with no overflow handling.queue = make(chan string, 100). The Increment/Count/Timer/Gauge helpers all do a blocking queue <- .... If StatsdSender can't drain (statsd down, slow TCP, etc.), the channel fills up and every metric call in the check path blocks. The blocking happens inside Check / Dial / HTTPS / Ping, so checks themselves stall.
New connection per metric.StatsdSender does Dial/Write/Close for every packet. That's expensive even for UDP (kernel work + ephemeral port churn) and catastrophic for TCP statsd (full 3-way handshake + close per metric, plus the connect blocks the queue drain — feeding back into problem check takes 10x longer to run than wait for the same set of URLs #1).
All errors are silently swallowed.if conn, err := net.Dial(...); err == nil — if dial fails there is no log, no fallback, no metric (it's the metric pipeline itself), and the queue grows.
Channel is never closed.StatsdSender runs forever and there's no graceful shutdown / drain on os.Exit. The last batch of metrics around a check failure may be lost.
With TCP statsd misconfigured or unreachable, connectivity check can hang in Increment after ~100 events.
Even with UDP, the connection-per-packet pattern is a needless syscall storm; over time in monitor mode this is the single largest source of overhead in the process.
validate-config does not start StatsdSender (see connectivity.go:25-39), but it also doesn't currently call any metric helpers, so it happens to work — a fragile invariant.
Suggested fix
Hold a single persistent statsd connection (or a *net.UDPConn with WriteToUDP), reconnecting on error with backoff.
Make the metric helpers non-blocking via select { case queue <- s: default: drop_and_count_drops } so a failing statsd path can never block the check path.
Log dial/write errors at most once per N seconds.
Drain the queue on shutdown (signal handler + wg.Wait()).
Summary
The statsd subsystem has reliability and efficiency problems that matter most in
monitormode but are visible everywhere:queue = make(chan string, 100). TheIncrement/Count/Timer/Gaugehelpers all do a blockingqueue <- .... IfStatsdSendercan't drain (statsd down, slow TCP, etc.), the channel fills up and every metric call in the check path blocks. The blocking happens insideCheck/Dial/HTTPS/Ping, so checks themselves stall.StatsdSenderdoesDial/Write/Closefor every packet. That's expensive even for UDP (kernel work + ephemeral port churn) and catastrophic for TCP statsd (full 3-way handshake + close per metric, plus the connect blocks the queue drain — feeding back into problemchecktakes 10x longer to run thanwaitfor the same set of URLs #1).if conn, err := net.Dial(...); err == nil— if dial fails there is no log, no fallback, no metric (it's the metric pipeline itself), and the queue grows.StatsdSenderruns forever and there's no graceful shutdown / drain onos.Exit. The last batch of metrics around acheckfailure may be lost.Code
statsd.go:11-50:Impact
connectivity checkcan hang inIncrementafter ~100 events.monitormode this is the single largest source of overhead in the process.validate-configdoes not startStatsdSender(seeconnectivity.go:25-39), but it also doesn't currently call any metric helpers, so it happens to work — a fragile invariant.Suggested fix
*net.UDPConnwithWriteToUDP), reconnecting on error with backoff.select { case queue <- s: default: drop_and_count_drops }so a failing statsd path can never block the check path.wg.Wait()).