Step 01 of 06
Learn the concept
The scheduler now creates a stream of results. Without storage, every result disappears after a log line. The first store is deliberately small: memory only, race-free, and bounded so success cannot become a leak.
The ideas this is made of
A data race means no one owns the truth
In Go, a data race occurs when two goroutines access the same memory at the same time, one writes, and there is no synchronization. The race detector instruments a run and reports these conflicts. Do not dismiss it because the printed results look fine. Races are timing-dependent; production supplies timing you did not test.
Mutexes make a critical section explicit
A sync.Mutex protects a small region where shared state is read or written. Lock, change the slice or map, unlock. Keep that region short and never call slow network code while holding it. A mutex is not primitive or shameful; it is a clear ownership rule for memory.
Channels move ownership instead of sharing it
With a channel design, check goroutines send results to one store goroutine. That goroutine alone mutates the buffer. This is excellent when writes are stream-shaped. Reads require request channels or snapshots, which can add ceremony. Choose the simpler rule your access pattern can defend.
A ring buffer turns memory into a fixed budget
An unbounded slice grows forever in a daemon. A ring buffer with capacity 1000 stores the newest thousand results and overwrites the oldest slot when full. That is not data loss by accident; it is a retention policy. Later SQLite keeps history. Memory is for the hot recent view.
package main
import "fmt"
type Ring struct{ values []int; next int; full bool }
func (r *Ring) Add(v int) {
if len(r.values) == 0 { return }
r.values[r.next] = v
r.next = (r.next + 1) % len(r.values)
if r.next == 0 { r.full = true }
}
func main() {
r := Ring{values: make([]int, 3)}
for i := 1; i <= 5; i++ { r.Add(i) }
fmt.Println(r.values, r.next, r.full)
}After five writes into three slots, the ring still uses three integers of memory. The order needs a snapshot method, but the memory budget is already enforced.
Mutex or channel
| Choice | Good at | Watch for |
|---|---|---|
Mutex | Simple reads and writes | Holding lock too long |
Channel owner | Serializing streams | Awkward snapshots |
No guard | Nothing | Races and panics |
What these are called on the job
Critical section — Code that reads or writes shared state while protected by a lock.
Race detector — Go instrumentation enabled with
-racethat reports unsynchronized shared memory access.Ring buffer — A fixed-size buffer that wraps around and overwrites its oldest entries.
