Skip to content

CVN-N014-ED-S01 — Fondation : flip global du backend object-storage XCom · hub Story

Hub documentaire de la Story S01 (load-bearing) de l'Epic CVN-N014-ED — poser le standard de transport inter-tâches (object-storage XCom backend, piloté par seuil) et le flipper globalement, sans régresser le parc. État live = OpenProject (wp#242, GH #1105, ADR-76).

En une phrase

Activer globalement XComObjectStorageBackend — un flip near-inert aujourd'hui (parc JSON-only sous seuil, vérifié in-pod), outillé et prouvé avant d'être déployé, le numpy (s43) étant déféré à S02.

Les documents (dans l'ordre de lecture)

# Document Quoi Pour qui
1 Plan dossier le quoi & pourquoi — Partie I : problématisation (sans jargon), user stories, hypothèses (EN), état de l'art (EN), Definition of Done ; Partie II : decision record A-recon-fondé + design. plan_review PASSED. décideur, quant, relecteur
2 Architecture le comment c'est construit — mécanique serialize_value (json.dumps avant seuil), flip config-only Helm, 3 outils read-only in-pod, rollback, conformité ADR. ingénieur, archi
3 ADR-0100 — standard de transport le standard — 5 invariants : JSON-ou-pass-by-réf, pas de store bespoke, flip proof-gated, rollback revert+re-trigger, seuil ≠ sérialisation. archi, décideur
4 Runbook flip / kill-switch le comment on s'en sert — pre-flight, détection, revert + re-trigger, rehearsal obligatoire avant cut-over. opérateur
5 Stratégie de tests le comment on le valide — pyramide ADR-83, table de cas (audit/verdict/read-only), validation système (dogfood in-pod). dev, QA
6 MLOps readiness ADR-70 — monitoring/SLO XCom, rollback, DRI, risque résiduel. DRI
7 Cut-over decision dossier le paquet de cut-over — calibrage seuil, pre-flight, diff Helm, lifecycle, rehearsal kill-switch, vérif post-flip + le RCA du 1er cut-over raté (voir ci-dessous). opérateur, archi
8 PR review — committee pr_review PASSED (session 3a73b7e1, OP Meeting #251) dossier archivé sur la Meeting. relecteur

Outils livrés (branche feat/CVN-N014-ED-S01-xcom-objectstorage-foundation) :

  • scripts/xcom_serialization_audit.py — audit statique (AST) du parc XCom. Run réel : 129 sites, 0 numpy_suspect, 116 unknown (@task dicts).
  • scripts/xcom_roundtrip_probe.py — round-trip dynamique in-pod. Run réel : contrôle-flow GREEN, numpy EXPECTED-FAIL (finding).
  • scripts/xcom_flip_preflight.py — pre-flight durci §G.3 (provider + connexion S3 + config dotted-section prouvée in-pod ; S3 write opt-in).
  • 19 tests unitaires (tests/unit/test_xcom_*.py).

État

In progress (OpenProject) — implémentation des outils + docs + ADR sur la branche. Le flip prod (values-prod.yaml) est volontairement staged, séparé, sur go opérateur + rehearsal kill-switch (gate runbook).

Findings vérifiés in-pod (scheduler réel) : - enable_xcom_pickling=False effectif → parc XCom JSON-only énumérable. - XComObjectStorageBackend ne sérialise pas numpy (json.dumps(XComEncoder) avant le seuil) → l'Epic §2 corrigé ; route X non drop-in (S02 : X′/X″/Y préféré). - sous seuil = octets identiques à BaseXCom → flip near-inert ; au-dessus = DB garde le chemin → kill-switch = revert + re-trigger.

Incident & RCA — 1er cut-over raté (encodage env-var section pointée) · 2026-06-06

Le cut-over prod a eu lieu (PR #1118 mergée, deploy SUCCESS) puis s'est révélé cassé mais latent (DAGs schedule=None, 0 run en vol → aucune perte). Fix-forward (PR fix/CVN-N014-ED-S01-xcom-env-encoding). Détail complet : cut-over decision dossier §RCA.

  • Symptôme : backend actif → AirflowConfigException: section/key [common.io/xcom_objectstorage_path] not found. serialize_value lit le seuil avant le test de taille → tout write XCom aurait crashé.
  • Root cause : section pointée encodée avec un point littéral (AIRFLOW__COMMON.IO__…). Airflow fait section.replace('.', '_') → il ne cherche que AIRFLOW__COMMON_IO__… (underscore). Var présente dans l'env mais invisible à conf.get.
  • Fix (vérifié in-pod avant re-deploy) : encoder COMMON_IO (underscore).
  • Pourquoi les gates ont raté : (1) pre-flight testait la présence de l'env var, pas sa résolution conf.get ; (2) le signal (config get-value en échec post-deploy) a été rationalisé en « quirk CLI » au lieu d'être traité comme le footgun ; (3) le rehearsal bypassait le mapping (import direct + offload manuel). → Gate durci : post-deploy on appelle conf.get('common.io', …), et un échec = STOP/revert, jamais un quirk.
  • Blast radius : zéro (aucun run exécuté entre le deploy cassé et le fix).

Gates ADR-81 : - In specification ✅ (plan dossier) · Specified ✅ (plan_review PASSED 8d7efca0 / Meeting #249, 0 blocker) · In progress ✅ (impl en cours, single-WIP override opérateur 2026-06-05). - → Developed : reste PR ouverte + checks verts + CodeRabbit + pr_review PASSED + docs live.