Vous n’avez pas besoin d’un autre système distribué si votre base de données peut faire le travail. Mais vous n’avez pas non plus besoin qu’elle fasse tout. La décision de Microsoft d’ouvrir le code d’un moteur d’exécution durable en base pour Postgres (pg_durable) met cette tension directement sur votre bureau. La promesse est séduisante : moins d’éléments à opérer, cohérence transactionnelle et observabilité native SQL. Le risque est tout aussi réel : domaines de défaillance élargis, tables chaudes sujettes à l’amplification des écritures, et du travail de DBA déguisé en simplicité.
Ce billet vous propose un cadre de décision pragmatique. Quand déplacer vos workflows dans Postgres ? Quand conserver un orchestrateur externe comme Temporal, Cadence, Conductor, Argo ou AWS Step Functions ? Et si vous choisissez l’approche en base, comment éviter qu’elle ne devienne le prochain récit d’incident ?
Le problème que pg_durable veut résoudre
Les workflows durables ont besoin de trois éléments :
- Un état qui survit aux redémarrages de processus et de machines.
- Des minuteurs et des relances avec backoff qui ne disparaissent pas pendant les déploiements.
- Une exécution exactly‑once ou des effets idempotents liée aux écritures métier.
La plupart des équipes bricolent cela avec une file d’attente, une table de jobs et quelques clés d’idempotence « best effort ». Ça marche… jusqu’au jour où ça casse. Les orchestrateurs externes corrigent le problème mais ajoutent un nouveau système distribué à faire tourner, à apprendre et à payer. L’exécution durable en base dit : laissez Postgres porter l’automate d’état et les minuteurs du workflow, et laissez vos workers tirer les étapes avec des sémantiques transactionnelles. Vous réduisez les sauts réseau et rattachez écritures métier et transitions du workflow dans un seul commit.
Principes de base : les quatre axes qui tranchent
1) Profil de charge
Profilage de ce qui passe réellement dans vos workflows :
- Débit : moyenne et p95 de workflows démarrés par seconde ; étapes exécutées par seconde.
- Durée des étapes : limitées CPU dans votre code (10–500 ms), attentes d’E/S vers des API tierces (100 ms–10 s), ou humain dans la boucle (minutes–jours).
- Fan‑out : un workflow → N étapes en parallèle ? Jusqu’où peut aller N ?
- Taille des charges utiles : entrées/sorties d’étapes persistées par transition (octets vs Ko vs Mo).
Pourquoi c’est important : Postgres excelle à traiter des milliers de petites transactions par seconde avec des tailles de lignes prévisibles. Il est moins à l’aise avec des partitions chaudes à fort turn‑over, des journaux append‑only sans borne dans le cluster principal, ou des charges utiles de plusieurs mégaoctets dans l’historique de workflow. Pour ordre de grandeur, des systèmes de file d’attente sur Postgres bien réglés (par ex. pg_boss, pgbmq, patterns SKIP LOCKED) soutiennent couramment 5–20k jobs/s sur une machine convenable avec SSD. Vous pouvez aller plus loin avec du partitionnement et un VACUUM agressif, mais vous devenez alors ingénieur de file d’attente. Les orchestrateurs externes dépassent ces seuils sans mettre sous pression votre base primaire.
2) Proximité des données et couplage transactionnel
Vos étapes de workflow modifient‑elles des lignes dans la même base Postgres qui détient la vérité métier ? Si oui, l’orchestration en base est convaincante. Vous pouvez faire l’écriture métier et la transition de workflow dans la même transaction. Cela supprime la complexité outbox/relay et élimine une classe de fenêtres de course. Si la plupart des étapes appellent des API externes ou d’autres magasins de données, la valeur du couplage diminue, et vous chargez Postgres avec de l’état qui n’est pas co‑localisé avec le vrai travail.
3) Domaines de défaillance et rayon d’explosion
Déplacer l’orchestration dans Postgres élargit le rayon d’explosion d’un incident base de données. Un pic de WAL dû à une tempête de relances mal configurée peut dégrader le même cluster sur lequel reposent les requêtes de votre produit. Si votre posture RTO/RPO traite déjà le primaire comme un joyau de la couronne, voulez‑vous vraiment des tempêtes de workflows à cet endroit ? Les orchestrateurs externes vous coûtent un plan de contrôle séparé qui peut tomber indépendamment — ce qui est une bonne chose quand votre base brûle.
4) Contraintes de plateforme et capacité de l’équipe
Vérifications de réalité :
- Limites des Postgres managés : certains fournisseurs restreignent les extensions C. RDS et Aurora Postgres en autorisent un sous‑ensemble. Vérifiez si pg_durable fonctionne là où vous faites tourner Postgres.
- Multi‑région : si vous avez besoin d’actif‑actif entre régions, des orchestrateurs avec leur propre modèle de réplication peuvent être plus faciles que d’écrire l’état de workflow dans plusieurs régions via Postgres.
- Capacité Ops : bien faire tourner Temporal ou Step Functions n’est pas gratuit. Devenir expert en autovacuum, HOT updates et tables de jobs partitionnées non plus.
Là où l’exécution durable en base brille
1) Workflows à faible latence, au plus près des lignes
Pensez aux contrôles anti‑fraude sur une ligne de checkout, au recalcul des droits quand un utilisateur change de plan, ou aux rafraîchissements de vues matérialisées. Le chemin d’exécution, c’est « toucher quelques lignes, émettre un événement, planifier une relance ». Vous voulez que chaque étape soit idempotente et se valide dans la même transaction que la modification de la ligne. L’en‑base gagne en simplicité, en cohérence et en latence (un saut réseau en moins).
2) Maîtrise serrée des coûts et petites équipes
Un système scalable horizontalement de moins à opérer signifie un coût cognitif et financier plus bas. Si votre pic est sous 5k exécutions d’étapes/s et que les charges utiles sont petites, Postgres est difficile à battre en TCO. Vous payez pour plus d’IOPS et de marge de stockage plutôt que pour un cluster d’orchestrateur ou des frais par transition d’état dans un service cloud.
3) Conformité et auditabilité via SQL
L’historique des étapes et des décisions vit dans des tables que vous pouvez joindre, snapshotter et exporter dans votre régime de conformité existant. Pas de lac d’audit séparé à rapprocher. Les revues SOC 2 et ISO 27001 deviennent plus simples quand vous pouvez prouver le contrôle par des requêtes SQL.
Là où les orchestrateurs externes gardent l’avantage
1) Fan‑out débridé, longue traîne, interventions humaines
Des centaines de branches en parallèle, des étapes qui attendent des heures des callbacks, et des validations humaines vous poussent vers l’orchestration dédiée. Les timer wheels et l’event sourcing de Temporal sont conçus pour cela. Chercher à faire rentrer ce profil dans Postgres sans affamer le reste de votre charge est un art que vous ne devriez pas avoir à apprendre.
2) Frontières d’équipe et isolation des services
Si vous êtes un scale‑up multi‑équipes, un orchestrateur externe devient une plateforme avec des API bien définies, des quotas et une équité multi‑locataires. Mettre l’état de tous les workflows dans un seul schéma de base peut créer des incidents de « voisins bruyants » et des débats politiques sur les réglages de VACUUM.
3) Actif‑actif multi‑région et prolifération des permissions cloud
Des SLA globaux et des workflows inter‑régions sont plus faciles à raisonner quand l’orchestrateur abstrait la réplication. Et quand vos étapes ont besoin d’identifiants cloud pour une douzaine de services, garder les secrets et l’IAM cadrés par l’orchestrateur est souvent préférable à repousser plus de responsabilités dans la frontière base de données.
Les coûts cachés des workflows en base
Amplification des écritures et budget VACUUM
Chaque transition d’étape, c’est au moins une écriture et souvent deux (claim + complete) plus une écriture de minuteur. À 2k étapes/s avec des lignes de 300 octets en moyenne, vous générez de l’ordre de 600 Ko/s de churn de table avant le surcoût WAL. Le multiplicateur WAL peut facilement être de x2 à x4 selon les index. Cela fait 1,2–2,4 Mo/s de WAL, soit 100–200 Go/jour. Planifiez IOPS et stockage en conséquence. Puis planifiez le VACUUM : si votre autovacuum prend du retard, les tuples morts enflent, les HOT updates se dégradent et les réplicas commencent à laguer.
Partitions chaudes et conception d’index
Les minuteurs créent un point chaud autour de « next_due_at ». Il vous faut des index partiels, du bucketing (par exemple à la minute) et du partitionnement pour les grands déploiements. C’est de l’ingénierie que vous auriez sinon payée à votre fournisseur d’orchestrateur avec une carte bleue.
Réplication logique et effets secondaires sur la CDC
Des tables de workflow à fort churn peuvent dominer votre flux de réplication logique et noyer vos consommateurs en aval. Utilisez des filtres de publication pour exclure les schémas de workflow de la CDC sauf si nécessaire. Si vous ne pouvez pas les exclure, envisagez un cluster Postgres séparé pour l’état d’orchestration.
Observabilité à construire
SQL rend le debug ad hoc délicieux. Mais vous avez quand même besoin de métriques de niveau service : démarrages/arrêts d’étapes, relances, taille de la DLQ, retard du « timer wheel », saturation des exécuteurs. Si l’extension ne les expose pas, vous les construirez. Ce travail reste à faire que vous soyez nearshore ou en interne.
Un cadre de décision concret
Attribuez un score de 0 à 2 à chaque affirmation. Faites la somme.
- 80 %+ des étapes de workflow lisent/écrivent des lignes dans le même cluster Postgres que votre produit cœur.
- Le p95 de durée d’étape est inférieur à 500 ms ; le 99e centile à moins de 5 s ; les charges utiles à moins de 10 Ko.
- Pics soutenus de transitions < 5k/s ; fan‑out généralement < 32 branches.
- Monorégion ou actif‑passif acceptable ; RTO de minutes, pas de secondes.
- Votre fournisseur Postgres autorise les extensions requises ; votre équipe sait régler autovacuum et le partitionnement.
- La conformité bénéficie d’un audit natif SQL de l’historique des workflows.
- 10–12 : L’exécution durable en base est probablement le bon défaut. Restez discipliné.
- 6–9 : Mitigé. Commencez en base pour les flux proches des lignes ; extrayez vers un orchestrateur externe les flux à longue traîne ou à fort fan‑out.
- 0–5 : Utilisez un orchestrateur externe. Votre charge ou votre organisation fera payer la base de données.
Si vous choisissez l’en‑base : des garde‑fous pour éviter les drames à 3 h du matin
1) Schéma séparé, voire cluster séparé
Gardez les tables de workflow dans leur propre schéma avec des filtres de publication désactivés par défaut. Si les réplicas de lecture de votre produit ou les consommateurs CDC commencent à laguer à cause du churn des workflows, déplacez l’état de workflow vers un deuxième cluster Postgres avant de changer de technologie. Deux clusters Postgres sont souvent encore plus simples que d’apprendre un tout nouvel orchestrateur.
2) Partitionnez par temps et par tenant
Partitionnez les tables de jobs et d’historique par bucket due_at et éventuellement par tenant. Supprimez les anciennes partitions au lieu de faire des DELETE. Gardez l’ensemble de travail chaud assez petit pour que VACUUM se termine bien en‑deçà de vos intervalles de backoff de relance.
3) Concevez pour l’idempotence et des journaux d’effets
Exigez un request_id pour chaque effet externe. Stockez une table de journal des effets indexée par (request_id, target). Faites vérifier tous les workers avant d’émettre un effet. C’est une hygiène agnostique à l’orchestrateur qui transforme de l’at‑least‑once en effets exactly‑once.
4) Mettez un budget sur les minuteurs
Ne laissez pas chaque équipe planifier des réveils arbitraires. Proposez des politiques de backoff fixes et des comptes de relance plafonnés par type de workflow. Exposez une métrique « dette de minuteurs » : total des minuteurs en retard. Alertez en cas de croissance. Cette métrique vous signalera plus vite la saturation des exécuteurs que les retours utilisateurs.
5) Rétro‑pression (backpressure) et équité
Utilisez SKIP LOCKED ou des verrous consultatifs avec des limites par file. Assurez‑vous que les files à forte valeur ne puissent pas être affamées par des lots de jobs bas‑priorité. Définissez des plafonds de concurrence par tenant. Faites‑les respecter en SQL, pas seulement dans le code des workers.
6) SLO sur WAL, IOPS et VACUUM
Fixez des SLO mesurables : par ex., p95 du délai de VACUUM sous 5 minutes pour les partitions chaudes ; génération de WAL sous 3 Mo/s soutenus en pic ; retard d’application des réplicas sous 5 secondes pour les schémas non‑workflow. Si vous ne pouvez pas mesurer cela, vous volez à l’aveugle.
7) Tests de défaillance
Tuez un worker pendant une étape. Redémarrez le primaire pendant une tempête de relances. Mettez autovacuum en pause. Mesurez le temps de récupération et le volume de tentatives d’effets en double. Si ces exercices font peur, vos défauts ne sont pas encore sûrs.
Si vous restez avec un orchestrateur externe : gardez les gains
- Colocalisez les écritures : Même avec Temporal ou Step Functions, gardez le pattern outbox au plus près de vos tables de domaine. Faites dépendre la complétion d’étape de l’écriture dans l’outbox.
- Utilisez l’orchestrateur pour la longue traîne uniquement : Envisagez un hybride : en base pour les workflows courts et proches des lignes ; externe pour les étapes humaines et les gros fan‑outs. Tracez la frontière par latence et rétention, pas par équipe.
- Évitez le choc de facture : Si vous êtes sur une tarification à la transition, déplacez les relances bavardes en backoff en processus avant de les exposer à l’orchestrateur.
Et les agents et workflows IA ?
Les systèmes agentiques sont les pires clients possibles pour un moteur de workflow : longues chaînes, branches spéculatives, relances sur des outils instables et prompts de plusieurs mégaoctets dans les charges utiles d’étapes. Résistez à la tentation de mettre cet état dans votre Postgres primaire. Si vous devez garder une fine couche de contrôle dans Postgres (par ex. pour la facturation ou l’audit), stockez uniquement des références et un minimum de métadonnées. Les flux de tokens, logs d’outils et scratchpads appartiennent à un stockage d’objets indexé par un orchestrateur dédié ou un processeur de flux, pas dans votre base OLTP.
Modélisation des coûts : arithmétique simple, implications majeures
Voici un calcul de coin de table pour comparer les options d’une startup de taille moyenne :
- En base : Pic 2k transitions/s, 1 Ko par état/log de transition, facteur WAL ~3x → ~500 Go/mois de churn de stockage, plus les réplicas. Supposez une instance Postgres musclée à 2–4 k$/mois et 2–3 réplicas. Ingénierie : 0,2–0,4 ETP d’un senior connaissant les internals de Postgres.
- Orchestrateur externe (Temporal self‑hosted) : cluster de 3–5 nœuds, 1–2 k$/mois d’infra, 0,3–0,6 ETP d’ingénierie plateforme. Meilleure isolation, plus de logiciel à opérer.
- Orchestrateur externe (managé/PaaS) : souvent tarification à la transition. À 0,25 $ par million de transitions d’état (indicatif ; vérifiez votre fournisseur), 5 Md de transitions/an = 1,25 M$. Beaucoup d’équipes sous‑estiment cette ligne jusqu’à la facture.
Aucun de ces chiffres n’est « le bon » pour vous. Mais ils cadrent la décision : payez‑vous en compute, en expertise ou en marge fournisseur ?
Migrations sans interruption de service
Des files maison au durable en base
- Introduisez l’extension et les nouvelles tables dans un schéma séparé.
- Écrivez en double les prises d’étapes (claims) vers l’ancienne et la nouvelle table de jobs pour un sous‑ensemble canari.
- Basculez les consommateurs workers par type de workflow derrière des flags.
- Renforcez VACUUM, la latence des minuteurs et les vérifications d’idempotence ; puis basculez la valeur par défaut.
- Rétro‑remplissez ou archivez l’ancien historique de jobs dans un stockage peu cher et supprimez les partitions chaudes.
Du en‑base à un orchestrateur externe (la trappe de secours)
- Ajoutez une couche d’adaptation qui traduit votre ligne d’état de workflow en entrée de workflow d’orchestrateur.
- Pour le nouveau travail, envoyez à l’externe mais gardez un pointeur Postgres pour l’audit.
- Pour le travail en cours, exportez les minuteurs comme minuteurs de l’orchestrateur à des frontières de transfert sûres (après la complétion d’une étape).
- Gardez les minuteurs en base désactivés mais récupérables pendant 1–2 cycles de release au cas où vous auriez besoin de revenir en arrière.
Une note sur l’exécution nearshore
Si vous êtes un CTO américain travaillant avec une équipe nearshore basée au Brésil, l’exécution durable en base peut être un multiplicateur de vélocité quand l’équipe possède déjà Postgres. La fenêtre de recouvrement (6–8 heures) suffit pour itérer rapidement sur le schéma et les sémantiques d’exécution, et vous évitez d’attendre un nouveau contrat de plateforme ou une intégration SSO. La contrepartie est que vous devez écrire tôt des SLO opérationnels et des runbooks — sinon l’état d’esprit « c’est juste du SQL » vous laissera avec un labeur invisible qui ressortira au pire moment.
En résumé
pg_durable met l’exécution durable en base à portée d’équipes qui n’auraient jamais déployé Temporal. C’est bon pour livrer. C’est dangereux pour les équipes qui confondent moins de conteneurs avec moins de complexité. Fondez votre décision sur la forme de votre charge et votre modèle de défaillance — pas sur la mode.
Points clés
- Les workflows en base excellent pour des étapes à faible latence, au plus près des lignes, avec un couplage transactionnel serré. Ils réduisent la glue outbox/relay et un élément mobile.
- Les orchestrateurs externes gagnent pour les fan‑outs massifs, les longues attentes, l’actif‑actif multi‑région et l’isolation multi‑équipes.
- Modélisez le coût : Postgres paie en WAL/IOPS et en temps de DBA ; les orchestrateurs paient en clusters ou en dollars par transition.
- Si vous optez pour l’en‑base, budgétez VACUUM, le partitionnement, l’équité des minuteurs et des SLO sur le WAL. Mettez minuteurs et relances sous budget.
- Gardez une sortie de secours propre : schéma séparé, canaris en double écriture et frontières de transfert claires vers un système externe si vous dépassez Postgres.
Auteur : Diogo Hudson Dias