Runtime · 2026-10-01 · go / concurrency / race-condition / atomics / runtime · medium

100,000 requests in. The counter says 97,832 — every run, a different shortfall.

01Symptom

A single `counter++` in a Go HTTP handler backs a site-wide hit counter. Under load it is never exact: 100,000 requests produce 97,832 the first run, 98,041 the second. The shortfall changes every run and is never above the true count. No database, no unharnessed resource — just one shared `int` and the default net/http server.

02Constraints

  • Go's default `net/http` server: one goroutine per incoming request — nothing pools or serializes handlers
  • Load test sends exactly 100,000 requests with high concurrency — thousands in flight at once
  • Single machine, multi-core
  • No locks, no atomics, no channels: the handler is exactly the shown `counter++`
  • The counter must be exactly correct, and the fix is not acceptable without naming the mechanism

03Evidence

  • Two identical load runs land on different shortfalls (97,832 then 98,041), and neither ever exceeds 100,000
  • `go build -race` reports an unsynchronized read/write on `counter` on the first request batch
  • amd64 disassembly of the handler shows three instructions for the increment: a load into a register, an add of one, and a store back — not a single in-memory increment
  • Verified at low concurrency (one request at a time) the counter is exact for all 100,000 — the loss scales with overlap, not with volume
→

Recent

Previously diagnosed.