Apresentação
Jobs Assíncronos — studyAI
Decisões operacionais para workers, retry, progress e failure handling.
Índice
Diagrama: ciclo de vida de jobs
A carregar diagrama…
Retry e backoff
A carregar diagrama…
Worker framework
Decisão: Celery + Redis
| Opção | Prós | Contras |
|---|---|---|
| Celery + Redis | Maduro, retry built-in, escalável, usado em produção | Mais setup |
| RQ | Mais simples que Celery | Menos features |
| Background tasks (FastAPI) | Zero dependências | Não persiste, não escala, perde jobs ao restart |
| Inngest / Trigger.dev | Managed, observability | Custo, vendor lock |
Escolha: Celery + Redis.
Rationale:
- Retry, dead-letter, rate limiting nativos
- Escala com workers
- Redis já usado para rate limit e cache
- Fase 0/MVP: pode começar com
BackgroundTasksdo FastAPI para ingestão simples; migrar para Celery na Fase 1
MVP fallback: Background tasks inline para ingestão (1 worker). Sem fila persistente. Adequado para validar fluxo antes de Celery.
Tipos de jobs
| Tipo | Trigger | Duração | Progress |
|---|---|---|---|
| ingest_file | Upload de ficheiro | 10s–2min | 0→parse, 50→chunk, 100→done |
| reindex_project | POST /index | 1–10min | Por ficheiro |
| generate_content | POST /generate | 2–10min | Por secção (0–80), questions (80–100) |
Retry e failure strategy
Retry
- Backoff: Exponencial: 1min, 5min, 15min, 1h
- Max retries: 5 (ingest), 3 (generate)
- Retry quando: Erro de rede, timeout LLM, rate limit 429
- Não retry: Erro de validação, ficheiro corrompido
Dead-letter / failure
- Após max retries: Job marcado
failed,errorpreenchido - Dead-letter queue: Opcional. Celery pode guardar em Redis ou log. Para MVP: só marcar failed em DB
- Notificação: Log estruturado. Futuro: alerta se taxa de falha > X%
- Recovery manual: Endpoint
POST /jobs/:id/retrypara admin re-disparar
Ingest específico
- files.retry_count: Incrementar a cada tentativa
- files.last_error: Guardar última mensagem
- files.status:
errorapós max retries - Cron opcional: Job que retenta files com status=error e retry_count < 5
Progress updates
Estratégia: Polling + SSE opcional
| Método | Uso | Quando |
|---|---|---|
| Polling | GET /jobs/:id | Default. Frontend faz poll a cada 2–5s |
| SSE | GET /jobs/:id/stream | Opcional. Conexão persistente, eventos em tempo real |
Decisão: Implementar polling primeiro (GET /jobs/:id). SSE como melhoria futura se UX exigir.
Formato do progress (jobs.progress, jobs.result):
{
"progress": 45,
"message": "A escrever secção 2 de 5...",
"current_section": 2,
"total_sections": 5,
"document_id": "uuid" // quando criado
}
Tabela jobs (operacional)
| Coluna | Tipo | Uso |
|---|---|---|
| id | UUID | PK |
| project_id | UUID | FK |
| type | VARCHAR | ingest_file, reindex_project, generate_content |
| status | VARCHAR | pending, running, completed, failed |
| progress | INTEGER | 0–100 |
| message | TEXT | Mensagem humana ("A escrever secção 2...") |
| payload | JSONB | Input do job (file_id, topic, etc.) |
| result | JSONB | Output (document_id, etc.) |
| error | TEXT | Mensagem de erro se failed |
| retry_count | INTEGER | Tentativas feitas |
| started_at | TIMESTAMPTZ | Quando começou a correr |
| completed_at | TIMESTAMPTZ | Quando terminou |
| created_at | TIMESTAMPTZ | |
| updated_at | TIMESTAMPTZ |
Celery: task_id (opcional) para correlacionar com Celery result backend.
Zona de prática
Sem perguntas. Clica em Editar para adicionar.