Le coût caché de l'orchestration avec état
SchemaBridge Team · 2026-02-02 · Distributed Systems, Immutable Infrastructure, Orchestration, Anti-Patterns, Reliability
Pourquoi vos outils internes « simples » sont peut-être votre plus grand risque de fiabilité, et comment les patterns d'infrastructure immuable appliqués à la logique applicative peuvent vous sauver.
Dans le monde des microservices, nous sommes obsédés par le découplage. Nous utilisons des files d'attente, nous adoptons des architectures événementielles et nous cassons les monolithes. Pourtant, au sein de nos moteurs d'orchestration, nous commettons souvent le péché capital des systèmes distribués : nous écrivons des boucles avec état.
Chez SchemaBridge, nous avons récemment remanié nos workflows système centraux — ceux qui pilotent nos propres déploiements progressifs. Ce faisant, nous nous sommes retrouvés face aux mêmes anti-patterns qui affligent de nombreuses organisations d'ingénierie. Ce n'est pas seulement une histoire de notes de version ; c'est un exercice d'application des principes de l'infrastructure immuable à la couche applicative.
L'anti-pattern de la « boucle »
Considérez le classique « poller » :
while not deployment.is_ready():
sleep(60)
check_status()
Ce code suppose un univers stable. Il suppose que le processus de polling vivra éternellement. En réalité, c'est un artefact de monolithe distribué. Un processus qui garde son état en mémoire pendant une longue attente synchrone est fragile : quand son hôte redémarre ou qu'un appel se bloque, il perd cet état ou subsiste comme un « zombie » que personne ne surveille, et ses modes de défaillance deviennent imprévisibles.
La solution : le chaînage récursif
Nous sommes passés d'une boucle while interne à un modèle d'exécution récursif, proche du Continuation Passing Style utilisé en programmation fonctionnelle.
- Pattern : Chaque itération lance un workflow enfant de monitoring qui attend avec une étape
DELAYdurable au lieu desleep(); une étapeSPAWNERlance ensuite l'itération suivante en tant que workflow enfant — le workflow de déploiement lance un autre run de lui-même. - Résultat : Le parent attend toujours que son enfant lui rende compte, mais il attend de façon durable : l'attente est une ligne dans PostgreSQL (un merge
WAIT_ALLqui attend le callback de l'enfant), et non un thread ou une boucle en mémoire. Si un worker meurt en plein déploiement, rien n'est perdu ; l'état de la base de données reste la source de vérité.
Une logique immuable : le versionnage par hachage canonique
Le versionnage est notoirement difficile. Lorsque vous mettez à jour une définition de workflow, qu'advient-il des exécutions déjà en cours ?
- L'anti-pattern : les « mises à jour sur place ». Vous déployez du nouveau code, et les exécutions existantes se mettent soudain à se comporter différemment.
- Le standard du secteur : l'infrastructure immuable. De même que nous ne nous connectons pas en SSH aux serveurs pour les patcher (nous les remplaçons), nous ne devrions pas patcher des définitions de workflow en cours d'exécution.
Les versions de workflow dans SchemaBridge sont immuables : un déploiement crée une nouvelle version, et un run est lié à la version sur laquelle il a démarré. Ce que nous avons ajouté, c'est le hachage canonique du DSL : chaque définition de workflow importée est sérialisée sous une forme JSON canonique puis hachée, et le hash est stocké avec le workflow (dslHash).
Ce hash répond à une question qui était étonnamment difficile — la logique a-t-elle réellement changé ? Lorsqu'un import de configuration ou notre propre bootstrap des workflows système rencontre une définition dont le hash correspond à ce qui est déjà en production, il planifie un no-op au lieu de redéployer, et un workflow en production qui a dérivé de sa définition source est détectable en comparant les hashs.
Verrouiller le « god mode »
Enfin, nous nous sommes attaqués à la sécurité des outils internes. Il est courant de donner un accès root aux scripts internes de « nettoyage ». Cela viole le principe du moindre privilège.
Nous avons constaté que notre propre outil interne de « Gradual Rollout » retrouvait les workflows sur lesquels il opérait via des ID logiques codés en dur, ce qui permettait facilement de le pointer vers le mauvais — et de l'écraser. Nous avons corrigé cela de deux manières :
- Résoudre par rôle, pas par nom : le déploiement trouve désormais son point d'entrée cible grâce à des tags explicites, dont l'unicité est validée, au lieu de recherches par ID logiques codés en dur.
- Un credential au moindre privilège : les étapes Gateway de nos workflows de déploiement s'authentifient avec leur propre clé d'API nommée, qui ne dispose délibérément pas de
config:manage— la permission de réécrire la configuration du tenant système. L'automatisation qui s'exécute depuis l'endroit le moins fiable ne peut plus modifier les règles sous lesquelles elle s'exécute.
Conclusion
Si votre orchestration repose sur sleep(), vous vous battez contre le cloud.
En adoptant la récursion, l'immuabilité et le moindre privilège, nous passons d'une orchestration « fondée sur l'espoir » à une orchestration dont vous pouvez inspecter l'état et borner le rayon d'impact.
Pour aller plus loin :