mirror of
https://github.com/MedUnes/go-kata.git
synced 2026-03-12 21:55:53 +07:00
update links under the root README file
This commit is contained in:
@@ -0,0 +1,56 @@
|
||||
# Kata 17: The Context-Aware Channel Sender (No Leaked Producers)
|
||||
**Target Idioms:** Pipeline Cancellation, Select-on-Send, Channel Ownership, Goroutine Leak Prevention
|
||||
**Difficulty:** 🟡 Intermediate
|
||||
|
||||
## 🧠 The "Why"
|
||||
A goroutine sending on a channel blocks until a receiver is ready (unless buffered space is available). If the receiver exits early (timeout, HTTP cancel, upstream error), producers can block forever and leak.
|
||||
|
||||
Idiomatic Go fixes this by:
|
||||
- threading `context.Context` through the pipeline
|
||||
- **selecting on every send** (`case out <- v` vs `case <-ctx.Done()`), as recommended in Go’s pipeline cancellation patterns and real leak writeups.
|
||||
|
||||
## 🎯 The Scenario
|
||||
You’re building a data pipeline step that fetches N URLs concurrently and streams results downstream. If the request is canceled (client disconnect, global timeout), **all fetchers must stop immediately** and no goroutine may remain blocked on `out <- result`.
|
||||
|
||||
## 🛠 The Challenge
|
||||
Implement:
|
||||
- `type DataFetcher struct { ... }`
|
||||
- `func (f *DataFetcher) Fetch(ctx context.Context, urls []string) <-chan Result`
|
||||
|
||||
Where:
|
||||
- `Result` contains `URL`, `Body []byte`, `Err error` (or similar).
|
||||
|
||||
### 1. Functional Requirements
|
||||
- [ ] Start concurrent fetchers for all URLs (or bounded concurrency if you choose).
|
||||
- [ ] Send results as they complete (order doesn’t matter).
|
||||
- [ ] Stop promptly on `ctx.Done()`.
|
||||
- [ ] Close the output channel exactly once after all producers exit.
|
||||
- [ ] Return partial results that already completed before cancellation.
|
||||
|
||||
### 2. The "Idiomatic" Constraints (Pass/Fail Criteria)
|
||||
- [ ] **Every send uses select:** no bare `out <- x`.
|
||||
- [ ] **Channel ownership:** only the producer side closes `out`.
|
||||
- [ ] **No goroutine leaks:** all goroutines exit when ctx is canceled.
|
||||
- [ ] **No double close:** prove it structurally (single closer goroutine). Avoid `sync.Once` unless you can justify it.
|
||||
- [ ] **Buffer choice is intentional:** if you buffer, document why and how you chose the size.
|
||||
|
||||
### 3. Hints (Allowed Tools)
|
||||
- You may use `errgroup` or a simple worker pattern, but the key is: **send must be cancel-aware**.
|
||||
- If you do bounded concurrency, prefer `x/sync/semaphore` or a worker pool (but don’t turn this kata into a rate-limiter kata).
|
||||
|
||||
## 🧪 Self-Correction (Test Yourself)
|
||||
1. **Forgotten Sender**
|
||||
- Start 50 fetchers, consume only 1 result, then cancel.
|
||||
- **Pass:** goroutine count returns near baseline quickly (use `runtime.NumGoroutine()` as a sanity check).
|
||||
|
||||
2. **Cancellation Before First Receive**
|
||||
- Cancel ctx immediately after calling `Fetch`.
|
||||
- **Pass:** no goroutine blocks trying to send.
|
||||
|
||||
3. **Close Discipline**
|
||||
- Cancel ctx from multiple places.
|
||||
- **Pass:** no `panic: close of closed channel`.
|
||||
|
||||
## 📚 Resources
|
||||
- https://go.dev/blog/pipelines
|
||||
- https://www.ardanlabs.com/blog/2018/11/goroutine-leaks-the-forgotten-sender.html
|
||||
Reference in New Issue
Block a user