feat: cola de jobs asincrona basada en SQLite (issue 0013)

Implementa el subsistema completo de background jobs para apps Go en el
dominio infra. 9 funciones + 3 tipos + 17 tests, todos pasando.

- Tipos: Job (product), JobQueue (product), JobStatus (sum) con
  JobHandler, EnqueueOption y WorkerOption usando functional options pattern
- job_queue_create: CREATE TABLE + indices + WAL mode
- job_enqueue: INSERT con UUID (github.com/google/uuid), WithPriority/WithScheduledAt/WithMaxAttempts
- job_dequeue: SELECT+UPDATE atomico en transaccion exclusiva, filtro por jobTypes
- job_complete / job_fail: transiciones de estado; fail → dead cuando attempts >= max_attempts
- job_status_summary: pura, formatea conteo de jobs por estado
- job_worker: poll loop bloqueante, context-cancelable, graceful shutdown
- job_worker_pool: N workers con golang.org/x/sync/errgroup
- job_cleanup: DELETE jobs terminales mas viejos que olderThan

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-04-13 02:00:44 +02:00
parent 092f14eff0
commit f7a4f26cf0
24 changed files with 1469 additions and 0 deletions
+31
View File
@@ -0,0 +1,31 @@
---
name: job
lang: go
domain: infra
version: "1.0.0"
algebraic: product
definition: |
type Job struct {
ID string
Type string
Payload string
Status JobStatus
Priority int
Attempts int
MaxAttempts int
ScheduledAt time.Time
StartedAt *time.Time
CompletedAt *time.Time
Result *string
Error *string
CreatedAt time.Time
}
description: "Unidad de trabajo asincrono almacenada en SQLite. Payload es un JSON string. Status evoluciona de pending a running y luego a completed, failed o dead."
tags: [job, queue, async, background, sqlite, infra]
uses_types: [job_status_go_infra]
file_path: "functions/infra/job_queue_types.go"
---
## Notas
Tipo producto. Los campos `StartedAt`, `CompletedAt`, `Result` y `Error` son punteros — son nil hasta que el job alcanza el estado correspondiente. `Payload` es siempre un JSON string (minimo "{}").
+20
View File
@@ -0,0 +1,20 @@
---
name: job_queue
lang: go
domain: infra
version: "1.0.0"
algebraic: product
definition: |
type JobQueue struct {
DB *sql.DB
TableName string
}
description: "Handle para una cola de jobs SQLite. Wrappea un *sql.DB y el nombre de la tabla de jobs. Se crea con JobQueueCreate."
tags: [job, queue, sqlite, infra, async]
uses_types: []
file_path: "functions/infra/job_queue_types.go"
---
## Notas
Tipo producto minimo — todas las funciones del subsistema de jobs lo reciben como primer argumento. TableName permite tener multiples colas en la misma DB. Se crea con `JobQueueCreate`.
+25
View File
@@ -0,0 +1,25 @@
---
name: job_status
lang: go
domain: infra
version: "1.0.0"
algebraic: sum
definition: |
type JobStatus string
const (
JobStatusPending JobStatus = "pending"
JobStatusRunning JobStatus = "running"
JobStatusCompleted JobStatus = "completed"
JobStatusFailed JobStatus = "failed"
JobStatusDead JobStatus = "dead"
)
description: "Estado del ciclo de vida de un job. Tipo suma con cinco variantes: pending, running, completed, failed, dead."
tags: [job, status, queue, async, infra]
uses_types: []
file_path: "functions/infra/job_queue_types.go"
---
## Notas
Tipo suma. Ciclo de vida normal: pending → running → completed. En error: pending → running → failed (reintentos posibles) → dead (sin mas intentos). Un job con `attempts >= max_attempts` pasa directamente a dead.