Aller au contenu

Workflow & événements

Vue d'ensemble

Le pipeline est entièrement piloté par événements Redis Streams. Aucun appel
HTTP synchrone n'invoque le pipeline depuis l'extérieur.

Stream Redis Consumer group Rôle
reliability:events reliability-workers File principale des analyses à exécuter.
reliability:events:retry File durable des retries planifiés (not_before_ts).
reliability:events:dlq Dead-letter pour les messages ayant dépassé MAX_RETRIES.

Noms surchargeables par les variables RELIABILITY_STREAM_KEY,
RELIABILITY_RETRY_STREAM_KEY, RELIABILITY_DLQ_STREAM_KEY et
RELIABILITY_CONSUMER_GROUP.

Événement d'analyse

Format de payload du message publié sur reliability:events :

{
  "event_type": "reliability.analysis.requested",
  "transcript_id": "uuid",
  "user_id": "uuid",
  "retry_count": 0,
  "timestamp_requested": "1716129600",
  "metadata": {}
}

L'événement est émis à la fin de la phase de post-processing de la
transcription (transcription, diarisation, enrichissement et persistance des
segments terminés).

Cycle de vie d'un message

sequenceDiagram autonumber participant Pub as Pipeline transcription participant Stream as reliability:events participant Worker as Reliability Worker participant Lock as Redis lock participant Prog as reliability_progress:{tid} participant Svc as Reliability Service participant Retry as reliability:events:retry participant DLQ as reliability:events:dlq Pub->>Stream: XADD event Worker->>Stream: XREADGROUP Worker->>Lock: SET reliability_lock:{tid} token NX EX 1800 alt lock acquis Worker->>Prog: HSET job_status=running stage=multi_type_analysis Worker->>Svc: run_full_pipeline(transcript_id) alt succès Svc-->>Worker: ok Worker->>Prog: HSET job_status=completed progress=100 Worker->>Stream: XACK Worker->>Lock: DEL (CAS Lua) else échec transitoire Svc-->>Worker: TimeoutError Worker->>Retry: XADD avec not_before_ts = now + 2^(n-1)s Worker->>Stream: XACK Worker->>Lock: DEL (CAS Lua) else échec non récupérable Svc-->>Worker: Exception Worker->>DLQ: XADD (trim maxlen ~5000) Worker->>Stream: XACK Worker->>Lock: DEL (CAS Lua) end else lock occupé Worker->>Stream: XACK (no-op, autre worker en charge) end note over Retry,Stream: scheduler async toutes 2 s
promeut les messages prêts

Lock distribué

Clé : reliability_lock:{transcript_id}.

Acquisition : SET key token NX EX RELIABILITY_LOCK_TTL_SECONDS (défaut
1800 s).

Libération : script Lua compare-and-delete — le worker ne libère le
lock que s'il en est toujours propriétaire :

if redis.call('GET', KEYS[1]) == ARGV[1] then
    return redis.call('DEL', KEYS[1])
else
    return 0
end

Ce schéma élimine la course classique « TTL expiré → un autre worker
acquiert → le premier worker DELete par erreur ». Le token est régénéré
(uuid4) à chaque tentative.

Métrique : worker_lock_acquired_total{outcome=acquired|busy}.

Retries durables

Les erreurs classifiées comme transitoires (timeouts LLM, indisponibilité
courte de dépendance) sont planifiées pour réexécution via le stream
reliability:events:retry.

Paramètre Variable Défaut
Délai de base RELIABILITY_RETRY_BASE_DELAY_S 10 s
Formule delay = base * 2^(retry_count-1) exponentiel
Nombre max RELIABILITY_MAX_RETRIES 3
Période du scheduler RELIABILITY_RETRY_POLL_INTERVAL_S 2 s
Batch par tick RELIABILITY_RETRY_BATCH_SIZE 50

Un scheduler asynchrone interne au worker promeut les entrées dont
not_before_ts <= now() vers le stream principal puis trim la queue.

Dead-letter

Une fois MAX_RETRIES atteint, le message est publié sur
reliability:events:dlq. La file est trimée approximativement à
RELIABILITY_DLQ_MAX_LEN (défaut 5000).

L'administration peut consulter et réinjecter manuellement les entrées via les
endpoints /api/reliability/dlq/list, /dlq/stats et /dlq/reprocess
documentés dans API REST.

Suivi de progression

Le worker maintient un hash Redis par transcription :

Clé : reliability_progress:{transcript_id} — TTL
RELIABILITY_PROGRESS_TTL_SECONDS (défaut 86 400 s).

Champ Valeurs
job_status running, completed, failed
stage étape courante (multi_type_analysis, …)
progress entier 0..100
started_at timestamp epoch

L'endpoint GET /api/reliability/transcripts/{tid}/progress expose ce hash
au frontend.

Arrêt gracieux

À la réception de SIGTERM :

  1. Le worker arrête de consommer de nouveaux messages.
  2. Il attend la fin du travail en cours jusqu'à
    RELIABILITY_SHUTDOWN_DRAIN_S (défaut 60 s).
  3. L'executor thread est terminé avec wait=True si le drain a été propre,
    cancel_futures=True sinon.
  4. Le scheduler de retries est annulé — les retries restent persistés sur
    Redis pour reprise au démarrage suivant.

Métrique : worker_drain_timeout_total.

Pool de connexions dédié

Le worker peut utiliser un pool SQLAlchemy isolé du pool de l'API HTTP,
contrôlé par :

  • RELIABILITY_USE_DEDICATED_POOL (défaut true),
  • RELIABILITY_DB_POOL_SIZE (défaut 5),
  • RELIABILITY_DB_MAX_OVERFLOW (défaut 2),
  • RELIABILITY_DB_POOL_RECYCLE (défaut 1800 s),
  • RELIABILITY_DB_POOL_TIMEOUT (défaut 30 s).

Ce cloisonnement empêche une charge worker de saturer le pool servant les
requêtes utilisateur.

State machine des suggestions

stateDiagram-v2 [*] --> pending: création (confidence < high) [*] --> auto_applied: création (confidence >= high) pending --> validated: action utilisateur "validated" pending --> ignored: action utilisateur "ignored" pending --> auto_applied: élévation auto validated --> pending: action "reverted" auto_applied --> pending: action "reverted" ignored --> [*]: terminal
  • validated et auto_applied ne sont pas terminaux : ils peuvent être
    reversés vers pending via une action reverted.
  • ignored est terminal — l'utilisateur a explicitement rejeté la suggestion.
  • Chaque transition crée une ligne immutable dans reliability_actions,
    préservant l'audit trail.