Ciclo di vita dei batch job
Sync vs async, stati dei job, risultati parziali e webhook
Gli endpoint batch (/batch/distill, /batch/extract) sono asincroni. Un job si muove attraverso una piccola macchina a stati; capire gli stati ti aiuta a decidere tra polling e webhook, e come gestire i fallimenti parziali.
Sync vs async
Sync (/distill, /extract) | Async (/batch/distill, /batch/extract) | |
|---|---|---|
| URL per richiesta | 1 | fino a 100 |
| Risposta | Risultato completo nel body HTTP | Job ID; risultati recuperati separatamente |
| Latenza | Una richiesta, un'attesa | Invio → polling o webhook → fetch |
| Adatto a | UX in tempo reale, tooling per agent, lookup ad-hoc | Job pianificati, ingestione RAG, monitoraggio |
| Modalità di fallimento | Un URL sbagliato fa fallire la chiamata | Un URL sbagliato fa fallire la sua riga, il job continua |
| Costo di concorrenza | Uno slot per chiamata | Uno slot per l'intero batch |
In caso di dubbio: URL singolo → sync; molti URL o nessuna fretta → async.
Stati del job
| Status | Significato |
|---|---|
PENDING | Job accettato, in coda |
PROCESSING | Almeno un URL è in elaborazione |
COMPLETED | Tutti gli URL hanno raggiunto uno stato terminale (successo o fallimento) |
FAILED | Errore fatale a livello di job (raro — di solito fallisce un URL, non l'intero job) |
CANCELLED | Cancellazione iniziata dall'utente tramite DELETE |
Un fallimento di URL non fa fallire il job. Ogni elemento in results[] porta il proprio status: PENDING, PROCESSING, SUCCEEDED o FAILED. Il job passa a COMPLETED quando ogni riga raggiunge uno stato terminale.
Polling vs webhook
| Dimensione del job | Consigliato | Perché |
|---|---|---|
| < 10 URL | Polling ogni 5–10 s | L'overhead del webhook non vale il cablaggio |
| 10–100 URL | Webhook | Il polling brucia ~60 round-trip su un job di 5 minuti |
| > 100 URL (più batch) | Webhook | Ogni batch si attiva una volta al completamento |
Vedi Webhooks per la forma del payload, la verifica della firma (HMAC-SHA256) e il comportamento dei retry.
Risultati parziali
GET /batch/distill/{id} funziona mentre il job è ancora in PROCESSING — ottieni tutto ciò che è terminato finora. Utile per dashboard che fanno streaming delle righe man mano che si completano.
import httpx, time
API = "https://openapi.thunderbit.com/openapi/v1"
H = {"Authorization": "Bearer YOUR_API_KEY"}
job = httpx.post(f"{API}/batch/distill", headers=H,
json={"urls": urls}).json()
batch_id = job["data"]["id"]
while True:
body = httpx.get(f"{API}/batch/distill/{batch_id}", headers=H).json()["data"]
fresh = [r for r in body["results"] if r["status"] == "SUCCEEDED"]
yield_to_dashboard(fresh)
if body["status"] in ("COMPLETED", "FAILED", "CANCELLED"):
break
time.sleep(5)Cancellazione
DELETE /batch/distill/{id} (o /batch/extract/{id}) funziona solo su job PENDING o PROCESSING. Una volta che un job raggiunge uno stato terminale, vi rimane. Gli URL già elaborati in un job cancellato restano fatturabili; gli URL in volo che non avevano finito no.