---
name: job_worker_pool
kind: function
lang: go
domain: infra
version: "1.0.0"
purity: impure
signature: "func JobWorkerPool(ctx context.Context, q *JobQueue, n int, handler JobHandler, opts ...WorkerOption) error"
description: "Lanza n workers concurrentes que comparten la misma cola y handler. Usa errgroup para concurrencia estructurada y soporta graceful shutdown via context. Bloquea hasta que todos los workers terminen."
tags: [job, queue, worker, pool, concurrency, errgroup, async, background, infra, graceful-shutdown, pendiente-usar]
uses_functions: [job_worker_go_infra]
uses_types: [job_queue_go_infra]
returns: []
returns_optional: false
error_type: "error_go_core"
imports: [context, fmt, golang.org/x/sync/errgroup]
params:
  - name: ctx
    desc: "context que al cancelarse detiene todos los workers; si uno falla, los demas tambien se detienen"
  - name: q
    desc: "cola de jobs compartida por todos los workers"
  - name: n
    desc: "numero de workers concurrentes (debe ser > 0)"
  - name: handler
    desc: "funcion de procesamiento compartida por todos los workers"
  - name: opts
    desc: "opciones aplicadas a cada worker: WithPollInterval, WithJobTypes"
output: "primer error no-context de cualquier worker, o ctx.Err() si todos terminaron por cancelacion"
tested: true
tests:
  - "concurrency_multiples_goroutines_dequeue"
test_file_path: "functions/infra/job_queue_test.go"
file_path: "functions/infra/job_worker_pool.go"
---

## Ejemplo

```go
ctx, cancel := context.WithCancel(context.Background())
defer cancel()

// 5 workers procesando emails en paralelo
err := JobWorkerPool(ctx, q, 5, func(job Job) error {
    return sendEmail(job)
}, WithPollInterval(100*time.Millisecond))
```

## Notas

Usa `golang.org/x/sync/errgroup`. Si un worker retorna un error no-context, `errgroup` cancela el context derivado y todos los demas workers se detienen. El graceful shutdown ocurre al cancelar el context original.
