# ADR-003: Channel-Based Buffered Task Queue **Status:** Accepted **Date:** 2026-03-18 ## Context A worker pool requires a mechanism to hand off work from callers to goroutines. Common options include a mutex-protected slice, a ring buffer, or a Go channel. The pool must support multiple concurrent producers (callers of `Dispatch`) and multiple concurrent consumers (worker goroutines), while providing a simple backpressure signal when capacity is exhausted. ## Decision The task queue is a buffered `chan Task` with capacity `Config.BufferSize` (env `WORKER_BUFFER_SIZE`, default 100). All worker goroutines receive from the same channel using `for task := range w.taskQueue`. Producers call `Dispatch` which uses a non-blocking `select` with a `default` branch: ```go select { case w.taskQueue <- task: return true default: // queue full — log and return false return false } ``` `Dispatch` returns `bool`: `true` if the task was enqueued, `false` if the queue was full. The caller decides what to do with a rejected task (retry, log, discard). Closing the channel in `OnStop` is the drain signal: `range` over a closed channel drains buffered items and then exits naturally, so no separate "stop" message is needed. ## Consequences - The channel scheduler distributes tasks across all `PoolSize` goroutines without any additional synchronisation code. - Backpressure is explicit: a full queue returns `false` rather than blocking the caller or growing unboundedly. Callers that must not drop tasks should implement retry logic at their layer. - Channel capacity is fixed at construction time. There is no dynamic resizing; if workload consistently fills the buffer, `BufferSize` or `PoolSize` must be tuned in config. - Closing the channel is a one-way signal: once `OnStop` closes it, `Dispatch` must not be called again. This is safe in practice because `launcher` ensures `OnStop` is only called after the application has stopped dispatching work, but there is no runtime guard against misuse. - The `for range` pattern requires no sentinel values and is idiomatic Go for fan-out worker pools.