How does the race detector work, what does it cost, and what are its limits?
go test -race / go build -race instruments every memory access and uses ThreadSanitizer's happens-before (vector clock) algorithm. It reports when two goroutines access the same location, at least one writes, and no synchronization (mutex, channel, atomic, WaitGroup) orders them. Reports include both stacks and where the goroutines were created.
func TestCounterRace(t *testing.T) {
var n int
var wg sync.WaitGroup
for range 100 {
wg.Add(1)
go func() {
defer wg.Done()
n++ // WARNING: DATA RACE
}()
}
wg.Wait()
_ = n
}
// Fix: var n atomic.Int64; n.Add(1) -- or protect with a sync.Mutex
Costs: roughly 5–10x memory and 2–20x CPU; before Go 1.19 there was a hard limit of 8128 simultaneously alive goroutines; Go 1.19 moved to ThreadSanitizer v3, which removed that limit and made the detector roughly 1.5–2x faster with half the memory. Supported only on 64-bit platforms (linux, darwin, windows, freebsd amd64/arm64 and a few others).
Limits: it's dynamic — it only finds races on code paths that actually execute concurrently during that run. No false positives, but false negatives are common. It doesn't detect deadlocks, logical races (check-then-act with each step locked), or leaks. So: run CI with -race, write tests that exercise concurrency (t.Parallel, RunParallel), and optionally run a canary instance with -race under real traffic. Gotcha: "benign" races don't exist in Go — the memory model allows a racy program to observe torn writes, and racy map access crashes the process with "concurrent map writes".
More on Performance, Profiling & Testing
- Q397How does struct field ordering affect memory usage and performance?
- Q398How can you convert between []byte and string without allocating? When is it safe?
- Q400How do you separate unit tests from slow integration tests, and what are golden files?
- Q401What are Example functions, and how are they verified?
- Q402Walk through how you would investigate a service whose p99 latency doubled after a deploy.
- Q403How do you write a test that fails if a hot path starts allocating? Explain testing.AllocsPerRun and its caveats.