--- name: job_dequeue kind: function lang: go domain: infra version: "1.0.0" purity: impure signature: "func JobDequeue(q *JobQueue, jobTypes []string) (*Job, error)" description: "Extrae atomicamente el siguiente job disponible usando SELECT+UPDATE en una transaccion exclusiva. Retorna nil, nil si la cola esta vacia o no hay jobs que cumplan el filtro. jobTypes limita los tipos dequeued; slice vacio significa todos." tags: [job, queue, dequeue, atomic, transaction, sqlite, async, background, infra] uses_functions: [] uses_types: [job_queue_go_infra, job_go_infra, job_status_go_infra] returns: [job_go_infra] returns_optional: true error_type: "error_go_core" imports: [database/sql, errors, fmt, strings, time] params: - name: q desc: "cola de jobs creada con JobQueueCreate" - name: jobTypes desc: "lista de tipos a desencolar; slice vacio o nil = todos los tipos" output: "puntero a Job con status=running, o nil si la cola esta vacia" tested: true tests: - "enqueue_dequeue_atomicidad" - "dequeue_empty_queue_returns_nil" - "dequeue_priority_order" - "dequeue_jobtype_filter" - "dequeue_scheduled_in_future_waits" test_file_path: "functions/infra/job_queue_test.go" file_path: "functions/infra/job_dequeue.go" --- ## Ejemplo ```go // Desencolar cualquier tipo job, err := JobDequeue(q, nil) if job == nil { // cola vacia } // Filtrar por tipo job, err = JobDequeue(q, []string{"send_email", "send_sms"}) ``` ## Notas Usa `db.Begin()` + `tx.Rollback()` (deferred) + `tx.Commit()`. El SELECT filtra por `status='pending' AND scheduled_at <= now` y ordena por `priority DESC, scheduled_at ASC`. El UPDATE atomico cambia el status a 'running' y setea `started_at`. Safe para multiples workers concurrentes — SQLite serializa la transaccion.