Skip to content

Architecture — CVN-N014-ED-S01 : object-storage XCom backend flip (foundation)

Réfs : plan dossier · ADR-0100 · runbook · test strategy. Le comment c'est construit — la mécanique du backend, le flip config-only, et les 3 outils read-only in-pod. Le quoi & pourquoi est dans le plan.

1. Vue d'ensemble

S01 ne livre pas le flip prod ; il livre la fondation prouvable : la mécanique du backend documentée + 3 outils read-only qui prouvent in-pod que le flip est sûr + le standard (ADR) + le runbook de rollback. Le flip lui-même = 4 variables d'env Helm, staged séparément.

                        ┌───────────────────────────────────────────────┐
   tâche A `return v`   │  XComObjectStorageBackend.serialize_value(v)    │
        ──────────────► │   s = json.dumps(v, cls=XComEncoder)   ← TJRS   │
                        │   if len(s) < threshold:  return s     (inline) │──► metadata-DB (JSON)
                        │   else: write s → S3 ; return BaseXCom(path)    │──► S3 + DB garde l'URI
                        └───────────────────────────────────────────────┘
   tâche B `xcom_pull`  ◄── deserialize_value : DB → (JSON direct | lit l'objet S3 via le path)

Le fait architectural porteur : json.dumps(v, XComEncoder) tourne avant le test de seuil. Donc (a) la sérialisabilité ne dépend pas de la taille (numpy échoue à toute taille) ; (b) sous seuil, les octets stockés sont identiques à BaseXCom (flip near-inert) ; (c) au-dessus, la DB ne contient que la string de chemin (conséquence rollback, §4).

2. Surface de configuration (le flip — staged)

Backend cible (Airflow 2.10.4 / common-io 1.4.2) : airflow.providers.common.io.xcom.backend.XComObjectStorageBackend. 4 env vars dans infra/helm/airflow/values-prod.yaml (Helm = SSoT, ConfigMap cvntrade-env-config, #378) :

Variable Rôle
AIRFLOW__CORE__XCOM_BACKEND bascule le backend (instance-global)
AIRFLOW__COMMON_IO__XCOM_OBJECTSTORAGE_PATH s3://<conn>@<bucket>/xcom
AIRFLOW__COMMON_IO__XCOM_OBJECTSTORAGE_THRESHOLD octets ; > p99.9 contrôle-flow
AIRFLOW__COMMON_IO__XCOM_OBJECTSTORAGE_COMPRESSION gz optionnel

Footgun à section pointée : le . de common.io peut faire silencieusement dropper l'env var (seuil → défaut -1 → flip inerte ; path → offload casse). D'où la vérif config get-value in-pod (§3.3), pas la lecture du fichier.

3. Les trois outils (read-only, in-pod)

Tous suivent le pattern kubectl exec de scripts/airflow_xcom_pull.py : la DB de métadonnées et XComEncoder ne vivent qu'en cluster (VPC privé + image Airflow), donc les outils embarquent une sonde Python exécutée dans le pod scheduler. Read-only par construction (ADR-25), fail-fast.

3.1 scripts/xcom_serialization_audit.py — audit statique (local)

AST-scan d'un arbre de DAGs (les deux dépôts via --path), sans importer/exécuter le code. Classe chaque producteur XCom (@task return, xcom_push, .output, xcom_pull) : json_literal / numpy_suspect / unknown. Sortie : carte du blast-radius. Run réel dags/ : 129 sites, 0 numpy_suspect, 116 unknown.

3.2 scripts/xcom_roundtrip_probe.py — round-trip dynamique (in-pod)

Exécute la vraie sérialisation du backend (json.dumps(v, XComEncoder)json.loads(.., XComDecoder)) sur un corpus représentatif, dans le pod. Encode le finding : contrôle-flow GREEN, numpy (dont la forme littérale s43) EXPECTED-FAIL. Une régression contrôle-flow bloque ; un EXPECTED-FAIL numpy ne bloque pas (c'est un finding S02, pas une régression S01).

3.3 scripts/xcom_flip_preflight.py — pre-flight durci (in-pod)

Prouve, avant de se fier au flip, dans le contexte worker/scheduler réel : (1) provider common-io présent ; (2) connexion S3 résoluble (et write/read/delete d'une sentinelle sous xcom/_preflight/, opt-in --functional-s3) ; (3) la config dotted-section a réellement pris (airflow config get-value common.io xcom_objectstorage_{path,threshold}-1/vide). « env var exportée » ≠ « Airflow l'a lue ». Read-only par défaut.

4. Rollback — pourquoi revert + re-trigger

deserialize_value lit d'abord la DB (BaseXCom.deserialize_value) puis, si la valeur est un chemin object-store reconnu, lit l'objet. Donc : - Sous seuil : la DB contient du JSON pur → lisible par BaseXComrevert seul OK. - Au-dessus : la DB ne contient que la string de chemin → BaseXCom la rend telle quelle à l'aval (qui attend la donnée) → casse. Fix = revert + re-trigger des runs in-flight (XCom per-run → self-heal au re-run). Détail opérateur : runbook.

5. Conformité ADR

  • ADR-0100 (Inv 1–5) : ce design EST l'implémentation du standard.
  • ADR-25 (no silent fallback) : S3 injoignable → la tâche échoue (pas de fallback) ; les sondes fail-fast.
  • ADR-59 / #378 (Helm = SSoT config) : le flip vit dans values-prod.yaml, jamais en kubectl apply manuel.
  • ADR-26 : le runbook part de Grafana.

6. Frontière S01 / S02

S01 = plomberie + preuve + standard sur le parc JSON existant (near-inert). S02 = stratégie de sérialisation numpy (X′ tolist / X″ serializer enregistré / Y pass-by-référence, défaut), dont le premier usage réel de l'offload (s43 route X) est le validateur in-vivo. S01 ne dépend d'aucun trafic numpy.