Skip to content

Passer des données entre tâches Airflow — le standard

Guideline DAG-author de l'Epic CVN-N014-ED. Source de vérité de la règle : ADR-0100 — cette page est l'aide à la décision ; ADR-0100 porte l'invariant normatif. En cas de divergence, ADR-0100 prime.

En une phrase

Pour passer des données d'une tâche à une autre, utilise return / xcom_pull : le backend object-storage XCom gère l'offload des gros payloads (ADR-0100, S01). Ne bricole jamais un stockage intermédiaire par-tâche (chemin S3 à la main, /tmp local, np.savez maison) — c'est l'anti-pattern qui a causé l'incident fondateur s43 (prédictions perdues cross-pod).

Pourquoi cette page existe

L'incident fondateur (CVN-N001-EI-S05 / s43) : une tâche écrivait ses prédictions via np.savez dans un /tmp local au pod, une autre tâche tournait dans un autre pod, lisait un /tmp vide → cohorte vide → verdict INCONCLUSIVE_TOOLING. Cause racine : pas de standard de transport — chaque DAG improvisait son stockage, avec une cible configurable qui pouvait être fausse et que les tests unitaires (tmp_path) ne voyaient jamais. Cette classe de bug est invisible au test et à la review sans une règle écrite. Cette page est cette règle.

L'arbre de décision

Je veux passer des données de la tâche A à la tâche B.
├─ 1. JSON-sérialisable ET petit (run_ids, params, métriques, chemins, références) ?
│      → XCom natif : return depuis A, xcom_pull dans B. Rien d'autre à faire.
├─ 2. Volumineux (DataFrame, ndarray, modèle, parquet) mais sérialisable ?
│      → return / xcom_pull QUAND MÊME : le backend object-storage XCom (ADR-0100)
│        offload automatiquement au-delà du seuil. Tu n'écris AUCUN chemin à la main.
├─ 3. Très volumineux / binaire spécifique / besoin d'un layout S3 maîtrisé ?
│      → pass-by-référence S3 partagé via cvntrade_s3_manager : A écrit sous un
│        préfixe run-isolé + manifest schéma-versionné, A renvoie la RÉFÉRENCE
│        (clé/prefix) en XCom, B la pull et lit depuis le S3 PARTAGÉ.
│        Pattern de référence : s43_io (S02). Voir le runbook s43 transport.
└─ 4. Producteur ET consommateur dans le MÊME pod (capture single-pod bornée) ?
       → /tmp local TOLÉRÉ (exception S03), à condition que ce soit prouvablement
         le même pod (ex. chaîne s18_step1_3 : producteur+consommateur chaînés
         dans un seul KubernetesPodOperator). Dès que ça franchit un pod → cas 2 ou 3.

Allowed / disallowed — exemples concrets

Cas Verdict Pourquoi
return df dans A, ti.xcom_pull(task_ids="A") dans B allowed le backend gère l'offload (ADR-0100), aucun chemin bricolé
A écrit s3://bucket/runs/<run_id>/preds.npz via cvntrade_s3_manager + renvoie la clé en XCom ; B lit la clé allowed pass-by-référence S3 partagé, run-isolé, schéma fail-loud (pattern s43_io)
np.savez("/tmp/preds.npz") dans A et np.load("/tmp/preds.npz") dans B, A et B chaînés dans le même KubernetesPodOperator allowed capture single-pod bornée (exception S03) — /tmp partagé dans le pod
np.savez("/tmp/preds.npz") dans A (pod 1), np.load("/tmp/preds.npz") dans B (pod 2) disallowed /tmp est pod-local → B lit un fichier vide. C'est l'incident s43.
A écrit un chemin S3 codé en dur / par-tâche sans manifest ni run-isolation disallowed stockage bricolé par-tâche : cible configurable qui peut être fausse, invisible aux tests
A to_parquet("/data/handoff.parquet") pour B dans un autre pod disallowed même classe que s43 — passer par return/xcom_pull ou le pass-by-réf S3

Règle single-pod (S03), formulée pour le reviewer : un /tmp/fichier local n'est toléré que si le producteur et le consommateur tournent prouvablement dans le même pod. Dès qu'une frontière de pod est franchie (ou peut l'être), le /tmp est interdit → return/xcom_pull (cas 2) ou pass-by-réf S3 (cas 3).

Le gate de review (anti-régression)

Cette règle est appliquée à la review par CodeRabbit (.coderabbit.yaml, instructions sur dags/**, src/commun/finetune/**, scripts/**) + la checklist du comité pr_review. Le gate flague par suspicion tout np.savez/np.load/put_object/upload_fileobj/to_parquet/to_pickle/chemin /tmp/manifest S3 écrit par-tâche pour franchir une frontière de tâche/pod, et demande la mise en conformité (cas 2 ou 3), sauf capture single-pod bornée (cas 4).

⚠️ Limite connue : le gate est à la review, pas au runtime — il dépend de la vigilance humaine/LLM. Un step-script exécuté via une image custom hors des globs ci-dessus n'est pas couvert. C'est un garde-fou robuste, pas une garantie technique absolue ; un check CI statique reste une extension future possible.

Voir aussi