Aller au contenu

PD-21 — Spécification (révision clarifiée)

Statut : Révision suite audit d’ambiguïtés et de testabilité

Principe directeur : La spécification fait loi. Aucune hypothèse implicite. Toute règle est testable ou explicitement hors périmètre.


1. Objectif

Définir de manière non ambiguë, testable et auditable le comportement du système de traitement asynchrone de jobs utilisé par ProbatioVault (files de jobs), afin de garantir :

  • la non‑interférence avec le flux principal (API synchrone),
  • une sémantique d’exécution déterministe et vérifiable,
  • une traçabilité consultable des états et événements,
  • une sécurité explicite du backend de queue,
  • une diagnosabilité post‑incident compatible avec les exigences probatoires.

2. Périmètre / Hors périmètre

2.1 Périmètre

Sont couverts par la présente spécification :

  • le cycle de vie d’un job (soumission → exécution → état terminal),
  • les garanties d’exécution (au plus une fois, échec explicite),
  • la persistance des états et événements de jobs,
  • la consultation des états et historiques,
  • les contraintes de non‑blocage du flux principal,
  • les exigences de sécurité du backend de queue (Redis),
  • les critères d’acceptation testables et scénarios associés.

2.2 Hors périmètre (explicitement exclus)

Les éléments suivants sont hors périmètre et ne doivent pas être inférés :

  • le choix d’une implémentation concrète (BullMQ, Celery, autre),
  • le dimensionnement matériel (CPU, RAM, nombre de workers),
  • les stratégies internes d’optimisation (batching, priorité interne),
  • la politique exacte de monitoring graphique (Grafana, autre).

3. Définitions

  • Job : unité de travail asynchrone identifiée par un identifiant unique immuable (job_id).
  • État terminal : état final non transitoire d’un job (SUCCEEDED, FAILED, CANCELLED).
  • Flux principal : traitement synchrone exposé par l’API backend.
  • Backend de queue : composant persistant stockant jobs, états et événements.
  • État consultable : état accessible via une interface définie et stable.
  • Exécution au plus une fois : un job ne peut produire ses effets au maximum qu’une seule fois.

4. Invariants (non négociables)

I‑1 — Non‑blocage du flux principal

La soumission d’un job ne doit jamais bloquer le flux principal.

Règle mesurable : - la réponse HTTP de soumission doit être retournée indépendamment de l’exécution du job.


I‑2 — Exécution au plus une fois ou échec explicite

Un job doit satisfaire exactement une des conditions suivantes :

  • être exécuté une seule fois avec succès,
  • échouer de manière explicite et traçable.

Interdictions : - exécution multiple silencieuse, - redémarrage implicite sans trace, - succès partiel non détectable.


I‑3 — Politique d’absence de retry implicite

Aucune tentative automatique de ré‑exécution n’est autorisée sans règle explicite.

  • Par défaut : 0 retry.
  • Toute autre politique doit être explicitement définie (cf. §9).

I‑4 — Traçabilité obligatoire et persistante

Chaque job doit produire une trace persistante comprenant :

  • son identifiant (job_id),
  • son état courant,
  • les timestamps d’entrée et de sortie de chaque état,
  • la cause d’échec le cas échéant.

I‑5 — Persistance post‑terminal

Les données de jobs ne doivent pas disparaître automatiquement à l’atteinte d’un état terminal.

  • La politique de rétention doit être définie explicitement.

I‑6 — Backend de queue sécurisé

Le backend de queue doit être considéré comme sensible et soumis à :

  • authentification,
  • chiffrement en transit,
  • isolation logique.

5. Flux nominaux

FN‑1 — Soumission d’un job

  1. Le client appelle l’API de soumission.
  2. Le backend génère un job_id unique.
  3. Le job est persisté dans le backend de queue avec l’état PENDING.
  4. L’API retourne une réponse de succès sans attendre l’exécution.

FN‑2 — Exécution nominale

  1. Un worker récupère le job PENDING.
  2. L’état passe à RUNNING.
  3. Le traitement s’exécute.
  4. En cas de succès, l’état passe à SUCCEEDED.

5bis. Diagrammes

D-1 — Machine d’etats d’un job

Les etats terminaux (SUCCEEDED, FAILED, CANCELLED) sont irreversibles (cf. I-2, I-5). Chaque transition produit une trace persistante avec timestamps (cf. I-4).

stateDiagram-v2
    [*] --> PENDING : Soumission (FN-1)
    PENDING --> RUNNING : Worker acquiert le job (FN-2)
    RUNNING --> SUCCEEDED : Traitement OK (FN-2)
    RUNNING --> FAILED : Erreur execution (CE-1)
    PENDING --> CANCELLED : Annulation explicite
    RUNNING --> CANCELLED : Annulation explicite

    SUCCEEDED --> [*]
    FAILED --> [*]
    CANCELLED --> [*]

    note right of PENDING
        I-1 : la soumission ne bloque
        jamais le flux principal
    end note

    note right of RUNNING
        I-2 : un seul worker execute
        le job (at-most-once)
    end note

    note left of FAILED
        I-4 : cause d’echec
        structuree obligatoire
    end note

D-2 — Sequence de soumission et execution nominale

Interactions entre le client API, le backend, le backend de queue (Redis) et le worker. Illustre la separation synchrone/asynchrone (cf. I-1) et la securite du transit (cf. I-6).

sequenceDiagram
    participant C as Client API
    participant B as Backend
    participant Q as Backend de queue (Redis)
    participant W as Worker

    Note over Q: I-6 : auth + TLS + isolation

    C->>B: POST /jobs (soumission)
    B->>B: Generer job_id unique
    B->>Q: Persister job (etat PENDING)
    Q-->>B: ACK
    B-->>C: 202 Accepted {job_id}
    Note over C,B: I-1 : reponse immediate, pas d’attente worker

    rect rgb(240, 248, 255)
        Note over Q,W: Traitement asynchrone
        W->>Q: Acquerir job PENDING
        Q-->>W: Job (acquisition exclusive)
        Note over W: I-2 : at-most-once
        W->>Q: Transition PENDING → RUNNING (+ timestamp)
        W->>W: Execution du traitement
        alt Succes
            W->>Q: Transition RUNNING → SUCCEEDED (+ timestamp)
        else Echec
            W->>Q: Transition RUNNING → FAILED (+ cause + timestamp)
            Note over W,Q: I-4 : cause structuree obligatoire
        end
    end

    C->>B: GET /jobs/{job_id}
    B->>Q: Lire etat + historique
    Q-->>B: Etat + timestamps
    B-->>C: 200 {state, history}
    Note over C,B: I-5 : donnees persistees post-terminal

6. Cas d’erreur

CE‑1 — Échec d’exécution

  • Le job passe à l’état FAILED.
  • Une cause d’échec structurée est enregistrée.

CE‑2 — Backend indisponible

  • La soumission échoue explicitement.
  • Aucun job fantôme ne doit être créé.

CE‑3 — Queue inexistante

  • La soumission doit échouer explicitement.
  • Le comportement silencieux est interdit.

7. Critères d’acceptation (testables)

CA‑1 — Non‑blocage

Mesure : la réponse de soumission est retournée même si aucun worker n’est actif.


CA‑2 — Unicité d’exécution

Mesure : un même job_id ne peut produire qu’un seul effet observé.


CA‑3 — Traçabilité consultable

Mesure : l’état et l’historique d’un job sont accessibles via une interface définie.


CA‑4 — Persistance post‑terminal

Mesure : un job terminal reste consultable après exécution.


CA‑5 — Sécurité du backend

Mesure : toute connexion non authentifiée ou non chiffrée est refusée.


8. Scénarios de test (Given / When / Then)

ST‑1 — Soumission sans worker

Given aucun worker actif
When un job est soumis
Then l’API répond avec succès et le job est en état PENDING.


ST‑2 — Exécution unique

Given un job PENDING
When deux workers tentent de l’exécuter
Then un seul passe à RUNNING, l’autre échoue explicitement.


ST‑3 — Consultation post‑échec

Given un job en état FAILED
When l’état est consulté
Then la cause d’échec est lisible.


9. Hypothèses explicites

  • Le backend de queue dispose d’un mécanisme de persistance durable.
  • Les horodatages sont fournis par une source fiable.
  • Les identifiants de job sont globalement uniques.

10. Points à clarifier

Les points suivants doivent être décidés explicitement avant implémentation :

  1. Politique exacte de rétention post‑terminal (durée, purge).
  2. Existence ou non de retries explicites et leurs règles.
  3. Interface exacte de consultation des états (API, DB, autre).
  4. Niveau d’isolation du Redis (dédié vs mutualisé).
  5. Limites de charge et mécanismes anti‑DoS côté workers.

Fin de spécification — toute implémentation non conforme est invalide.