The Race Detector
What is a data race?
A data race occurs when two goroutines access the same memory location concurrently and at least one access is a write โ with no synchronisation between them. The result is undefined behaviour: corrupted data, crashes, or silent wrong answers that appear intermittently.
Enabling the race detector
Append -race to any Go command:
The race detector is built on ThreadSanitizer. It instruments memory accesses at compile time and reports races at runtime when they actually occur.
Reading a race report
The report shows: the racing memory address, which goroutine performed each access, the source location of each access, and where each goroutine was created. Fix by adding a mutex, channel, or atomic around the shared variable.
Fixing races
Three canonical fixes:
When to run -race
| Context | Recommendation |
|---|---|
| CI pipeline | Always โ run go test -race ./... on every PR |
| Local development | Enable for concurrent packages during development |
| Production binary | Avoid โ 2โ20ร overhead; only for debugging |
| Benchmarks | Never โ overhead skews results |
Always run -race in CI. The detector only fires when a race actually executes, so 100% code coverage under -race greatly increases confidence.
-race overhead
The race detector adds approximately:
- 5โ15ร slowdown in execution speed
- 5โ10ร increase in memory usage
This is why production binaries ship without it. Accept the overhead in test environments.
Test coverage flags
While not race-related, often used together:
Knowledge Check
What condition defines a data race?
Why should -race never be used for benchmarks?
The race detector only fires when a race actually executes at runtime. What does this imply about test coverage?