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¶
- ADR-0100 — Inter-task data transport (standard normatif)
- Runbook — s43 transport (pattern pass-by-référence S3)
- Runbook — XCom backend flip (kill-switch + rollback)
- Pattern de référence dans le code :
src/commun/finetune/diagnostic/hamilton/s43_io.py(S02) - Epic CVN-N014-ED — Inter-task data transport