Weekly Review — Month 3 · Week 3 (Days 071–077)¶
📅 The Week in One Line¶
Composed the Week 1–2 concurrency primitives into named patterns — worker pool, fan-out/fan-in, pipelines, semaphores — and made them cancellable and leak-free with context and errgroup.
✅ What I Completed¶
- Day 071 — Worker pool pattern (bounded
Ngoroutines over a jobs channel) - Day 072 — Fan-out / fan-in (
mergewith a single closer goroutine) - Day 073 — Pipelines (stage ownership, cancellation, goroutine-leak avoidance)
- Day 074 — Bounded parallelism & counting semaphores (
chan struct{}) - Day 075 —
contextcancellation, deadlines & propagation - Day 076 —
errgroup(first-error + cancel) & rate limiting - Day 077 — Week review + closed-book recall
- Examples: workerpool, fanin-fanout, pipeline-cancel, bounded-sem, context-cancel (stdlib-only,
-raceclean) - Exercises solved: 3 (semaphore, boundedmap, taskgroup)
💡 Lessons Learned¶
- Channel ownership is the through-line: the goroutine that sends on a channel is the one that closes it — never a worker, never a forwarder. Violations are send-on-closed panics.
- Single closer goroutine is the canonical fan-in idiom:
wg.Wait(); close(out)runs exactly once after every contributor finishes. - Goroutine leaks are the default failure mode of pipelines: any blocking send must
selectagainst<-ctx.Done(), or an early consumer freezes the whole upstream chain. deferthe cleanup the instant you acquire: a semaphore token (defer func(){ <-sem }()) and a contextcancelfunc (defer cancel()) both leak silently if you forget.- Two different limits: bounded parallelism caps how many at once (semaphore /
SetLimit); rate limiting caps how often (time.Ticker/ token bucket). They compose. errgroup=WaitGroup+ first-error + cancel-the-rest; re-implementing it withsync.Once+contextmade the mechanism click.
💪 Strengths (what clicked)¶
- Recognising worker pool / fan-out-in / pipeline as the same primitives recomposed.
- The
ctx-first /defer cancel()/errors.Is(err, context.Canceled)conventions feel automatic now. - Preserving input order with index-addressed result slices (no locks).
🧩 Weaknesses (what's still fuzzy)¶
- Choosing pool size / semaphore limit empirically — I still guess instead of benchmarking.
- Burst-tolerant token buckets (
x/time/rate) vs steadyTicker— only conceptual so far. - Reasoning about leaks in a branching pipeline (multiple fan-outs) without
pprof.
🔁 Spaced-Repetition Re-quiz (topics from earlier weeks)¶
- Q: (Wk1) Unbuffered vs buffered channel send semantics?
A
Unbuffered: send blocks until a receiver is ready (rendezvous). Buffered: send blocks only when the buffer is full.
2. Q: (Wk2) Why wg.Add before go, not inside the goroutine? A
Add inside races with Wait, which may observe a zero counter and return before the goroutine is even counted.
3. Q: (Wk2) What does CompareAndSwap(old, new) return and how is it used? A
true if the value still equalled old and was set to new, else false; loop-and-retry on false for lock-free updates.
4. Q: (Wk1) Is receiving from a closed channel an error? A
No — it returns the zero value immediately; the two-value form v, ok := <-ch reports ok == false once drained.
5. Q: (Wk2) Is a clean go test -race a proof of race-freedom? A
No — it's a dynamic detector; it only catches races that actually occurred on that run.
🎯 Action Items¶
- Write a micro-benchmark that sweeps semaphore limit (1, 2, 4, 8, 16) over an I/O-bound task and plot the crossover.
- Add a
TryGo-style non-blocking variant to thetaskgroupexercise. - Add a
context.WithTimeoutdeadline to theboundedmaptest to exerciseDeadlineExceeded. - Read the real
golang.org/x/sync/errgroupsource and diff it against mytaskgroup.
🚀 Next Week Goals¶
- Build the Week 4 mini-project: a concurrent web crawler — bounded worker pool + single-owner dedup (no mutex) + graceful shutdown via
signal.NotifyContext. - Profile it with
runtime/pprofand tune the worker/limit counts with data.
📊 Metrics¶
| Hours | Days hit | Exercises | Commits | Avg confidence |
|---|---|---|---|---|
| 9 | 7/7 | 3 | 7 | 3.⅗ |
Suggested commit: docs(journal): week 3 review