Lesson 19 · Concurrency & context

Data races & the memory model

Why synchronisation isn't optional — and how Go proves it.

Your win: define a data race precisely, find it with -race, and state the happens-before guarantees that make our mutex/channel code correct — not just tidy.

What a data race actually is

Definition A data race occurs when two goroutines access the same memory location concurrently, at least one access is a write, and there's no synchronisation ordering them.1 The result is undefined behaviour — not "the wrong value sometimes", but genuinely unspecified.

(Distinguish it from a race condition — a broader logic bug about timing. A data race is the specific memory-level version.)

The memory model: happens-before

The Go memory model defines when a read is guaranteed to see a write, via a happens-before ordering.1 The guarantees you rely on every day:

SynchronisationHappens-before guarantee
Channel send → receivethe send (and everything before it) happens-before the receive completes
Mutex Unlock → next Lockwork before Unlock is visible after the next Lock
WaitGroup Waitall Done()-preceding work happens-before Wait returns
sync.Once.Dof's effects happen-before any Do returns

So proper synchronisation doesn't just prevent corruption — it creates the ordering that makes one goroutine's writes visible to another.

Anchor — why our mutex is required, not decorative Our engine guards the "which consumers are running" map with a Mutex (kafka.go:590). That's not neatness: concurrent read+write of a map is a data race, and for maps specifically Go throws a fatal "concurrent map writes" that crashes the process — even without -race. The Lock/Unlock establishes happens-before, so writes are ordered and visible. Likewise the fan-in channel (Lesson 15): send happens-before receive, so results transfer safely with no lock at all.

Find races with the detector

go test -race ./...     // instrument & watch for unsynchronised access
go run  -race main.go

The -race flag instruments memory accesses and reports races that actually happen during the run, with both goroutines' stacks.2 It's the single best tool for concurrency bugs — run your tests with it in CI. (Caveat: it only catches races that occur on that run, so it complements, not replaces, careful design.)

Classic races to name in an interview An unguarded shared counter (x++ from two goroutines); concurrent read+write of a plain map (fatal crash); and — historically — a captured loop variable in a goroutine (fixed in Go 1.22, so not a race in our 1.25, but still asked).
Read this next

The Go Memory Model + the Data Race Detector

The memory model is the authoritative source on happens-before; the detector doc shows exactly how to run and read -race.

go.dev/ref/mem
go.dev/doc/articles/race_detector

Check yourself (from memory)

Q1. A data race requires concurrent access with…

Same memory, concurrent, ≥1 write, unsynchronised → undefined behaviour. Two reads alone are fine.

Q2. You detect data races by running Go with…

go test -race / go run -race instruments memory access and reports races that occur, with stacks.

Q3. The memory model guarantees a channel send…

Send (and prior writes) happens-before the receive — so passing data over a channel is safe with no extra lock.
Why is guarding our running-state map with a mutex required for correctness, not just hygiene?
recall, then click to reveal
Concurrent read+write of the same map with no synchronisation is a data race — undefined behaviour — and for maps Go throws a fatal "concurrent map writes" that crashes the process (even without -race). The mutex creates a happens-before relationship (Unlock happens-before the next Lock), so writes are ordered and visible. Verify with go test -race.
🎓 That's Part 4 — the concurrency pillar Goroutines, channels & select, the sync primitives, context, the production patterns, and the memory model that makes them correct. This is the largest slice of most Go interviews — and now the Kafka engine reads like an open book. Part 5 is the last: testing, tooling & performance.
Ready for the final Part 5 (testing, tooling & performance), or a mixed quiz across Lessons 14–19 first? Ask me.

1. The Go Memory Model.

2. Data Race Detector.