Frontend Labs
⋯

Day 58 — Race Conditions in Network Servers

Learning Objectives

  • Find races in the Week 5 TCP proxy using -race
  • Understand the three most common race patterns in network code
  • Fix races without over-locking

Common Race Patterns

Race 1: Shared connection state
  Two goroutines (read + write) both check conn.closed
  Fix: sync/atomic or sync.Once for close

Race 2: Concurrent map writes
  Handler goroutine reads map while health checker writes
  Fix: sync.RWMutex around the map

Race 3: Deadline race
  Read goroutine sets read deadline
  Write goroutine also sets deadline (via SetDeadline)
  They race to set the deadline
  Fix: separate SetReadDeadline / SetWriteDeadline

Race 4: Goroutine closure variable capture
  for i := range items { go func() { fmt.Println(i) }() }
  All goroutines see the last value of i
  Fix: pass i as argument: go func(id int) { fmt.Println(id) }(i)

Exercise: Race Audit Checklist

# Run with race detector
go test -race -v ./...
go build -race . && ./proxy  # run the binary with race detection

# Common findings and fixes:
# DATA RACE: concurrent map read and write
# Fix: add sync.RWMutex

# DATA RACE: conn.closed written from two goroutines
# Fix: use atomic.Bool

# DATA RACE: per-connection stats map
# Fix: use sync.Map or per-connection struct with single owner goroutine

Annotated fixes for the Week 5 proxy:

// Before (race): both goroutines write to same conn field
type connection struct {
    conn     net.Conn
    closed   bool        // RACE: read and written from two goroutines
    bytesIn  int64       // RACE: written from read goroutine, read from stats goroutine
    bytesOut int64
}

// After (fixed):
type connection struct {
    conn     net.Conn
    closed   atomic.Bool // atomic — safe from multiple goroutines
    bytesIn  atomic.Int64  // atomic counter — no lock needed
    bytesOut atomic.Int64
}

Real-World Context

The race detector finds bugs that appear 1 in 10,000 times in production — intermittent corruptions and occasional panics. Running -race in CI is mandatory for any networked service. A data race in a proxy can silently corrupt responses — security-relevant because it could mix up authenticated and unauthenticated responses.


Generated from week8.md · 8 Oct 2026 16:36