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 BaseXCom → revert 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 enkubectl applymanuel. - 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.