- Les workflows durables checkpointent leur état d’exécution dans une base de données afin de pouvoir reprendre après un crash à partir de la dernière étape terminée
- L’orchestration externe à la Temporal, Airflow ou AWS Step Functions ajoute un orchestrateur central et un stockage, ce qui accroît la complexité
- Une architecture basée sur Postgres permet aux serveurs applicatifs de sonder une table de workflows et de stocker directement les sorties des étapes pour rendre la reprise possible
- Plusieurs serveurs évitent les exécutions en double grâce aux verrous et aux contraintes d’intégrité, et traitent les problèmes de scalabilité, de disponibilité et d’observabilité avec des solutions Postgres
- Un seul Postgres peut monter verticalement jusqu’à des dizaines de milliers de workflows par seconde, tout en tirant parti de la réplication, du multi-AZ et de l’analyse SQL
Modèle de base des workflows durables
- Un workflow durable consiste à enregistrer périodiquement sous forme de checkpoints l’état d’avancement d’un programme dans une base de données pendant son exécution, afin de pouvoir reprendre après un crash ou un échec à partir de la dernière étape terminée
- On peut voir cela comme le système de sauvegarde et de chargement d’un jeu vidéo : le programme “sauvegarde” régulièrement sa progression, puis “recharge” le dernier checkpoint après un crash
- Les implémentations courantes reposent sur des systèmes d’orchestration externe comme Temporal, Airflow ou AWS Step Functions
- Dans une orchestration externe, le programme durable est écrit sous forme de workflow composé de plusieurs étapes, et un orchestrateur central en coordonne l’exécution
Comment fonctionne l’orchestration externe
- Lorsqu’un client soumet un workflow, l’orchestrateur crée un enregistrement dans le stockage de données, puis le distribue à un worker pour exécution
- Chaque fois qu’un worker termine une étape, il renvoie le résultat à l’orchestrateur, qui checkpointe cette sortie dans le stockage avant de distribuer l’étape suivante
- Si un worker plante ou échoue, l’orchestrateur redistribue ce workflow à un autre worker, qui redémarre depuis la dernière étape checkpointée
- Cette architecture accroît la complexité en ajoutant un serveur orchestrateur distinct à l’idée de base consistant à stocker l’état du workflow dans une base de données
Une architecture utilisant Postgres comme orchestrateur
- Si le cœur des workflows durables consiste à checkpointer l’état du programme dans une base de données, il est plus simple et plus efficace d’utiliser la base de données elle-même comme orchestrateur, sans serveur orchestrateur distinct
- Postgres est un bon choix pour construire des workflows durables grâce à sa popularité, sa scalabilité et la richesse de son écosystème
- Dans un système de workflows durables basé sur Postgres, les serveurs applicatifs exécutent les workflows en communiquant directement avec Postgres, sans passer par un orchestrateur central
- Les clients soumettent l’exécution en créant une entrée dans la table des workflows de Postgres, et les serveurs applicatifs sondent cette table pour dépiler et exécuter les workflows
- Pendant l’exécution, les serveurs checkpointent dans Postgres la sortie de chaque étape, et si le serveur en cours d’exécution plante ou échoue, un autre serveur reprend le workflow depuis le checkpoint
Pourquoi l’orchestrateur central devient inutile
- Les serveurs applicatifs peuvent se coordonner via Postgres, il n’est donc pas nécessaire qu’un orchestrateur central distribue les workflows aux workers
- Les serveurs peuvent dépiler les workflows de manière coopérative depuis les tables Postgres, et des mécanismes comme les clauses de verrouillage permettent de garantir que chaque workflow n’est dépilé que par un seul worker
- Les checkpoints des sorties d’étape sont aussi écrits directement dans Postgres par les workers, et non par un orchestrateur
- Si plusieurs workers essaient d’exécuter simultanément le même workflow, les contraintes d’intégrité de Postgres peuvent détecter le travail en double au moment du checkpoint et les forcer à se retirer
- Remplacer l’orchestrateur central par Postgres ou une autre base de données permet de traiter les questions de scalabilité, de disponibilité, d’observabilité et de sécurité avec des solutions natives Postgres bien connues
Scalabilité et disponibilité
- La scalabilité et la disponibilité d’un système de workflows durables basé sur une base de données sont fondamentalement déterminées par la base de données sous-jacente
- On peut monter horizontalement en ajoutant davantage de serveurs workers ; le débit maximal dépend donc de la vitesse à laquelle la base de données peut traiter les workflows
- Comme les workers sont interchangeables et peuvent restaurer l’état les uns des autres, le système reste disponible tant que la base de données sous-jacente l’est
- Avec Postgres, l’avantage est que la scalabilité et la disponibilité sont des problèmes étudiés depuis longtemps, pour lesquels il existe des solutions robustes
- Un seul serveur Postgres peut monter verticalement pour traiter des dizaines de milliers de workflows par seconde
- Une extension supplémentaire peut être obtenue via un Postgres distribué comme CockroachDB ou via un Postgres shardé
- Postgres prend en charge la réplication en flux avec bascule automatique, et les services managés offrent nativement des déploiements multi-AZ et des SLA de haute disponibilité
- Les décennies d’ingénierie et de recherche consacrées à l’exploitation de Postgres à grande échelle peuvent être réutilisées directement pour l’exécution durable
Observabilité
- Dans une exécution durable basée sur Postgres, les workflows et les étapes sont checkpointés dans des tables Postgres, ce qui permet de surveiller les workflows en temps réel et de visualiser leur exécution en parcourant ces checkpoints
- Postgres est particulièrement fort sur ce point, car presque toutes les requêtes d’observabilité liées aux workflows peuvent s’exprimer en SQL
- Des opérations complexes de filtrage et d’analyse peuvent être exprimées de manière déclarative en SQL, par exemple une requête qui retrouve tous les workflows ayant généré des erreurs au cours du dernier mois
- Cela est possible grâce au modèle relationnel de Postgres et à des décennies de recherche sur l’optimisation des requêtes
- De nombreux systèmes dotés de modèles de données plus simples, comme les stockages clé-valeur utilisés par des orchestrateurs externes populaires, n’offrent pas le même niveau de support analytique basé sur SQL
- En stockant les données de workflows et d’étapes dans des tables Postgres et en ajoutant des index secondaires pour accélérer les requêtes analytiques, on obtient une observabilité efficace pour l’exécution durable
Fiabilité et sécurité
- Dans l’exécution durable utilisant un orchestrateur externe, l’orchestrateur et son stockage de données constituent tous deux un point de défaillance unique
- Comme l’orchestrateur et le stockage coordonnent directement l’exécution des workflows, si l’un des deux tombe en panne, l’ensemble de l’application devient indisponible
- Ils traitent et stockent aussi les checkpoints des workflows et des étapes, et peuvent donc accéder à des données applicatives sensibles, ce qui impose durcissement, contrôle d’accès et audit comme pour toute infrastructure sensible
- Dans une exécution durable basée sur Postgres, le seul point de défaillance est Postgres lui-même, et toutes les données des workflows sont stockées directement dans Postgres sans transiter par un autre système
- Si l’application dépend déjà de Postgres, l’adoption de l’exécution durable n’ajoute pas de nouveau point de défaillance et ne crée pas non plus de nouvelle surface d’attaque à protéger
- La base de données étant déjà une infrastructure critique, il est plus logique de réutiliser la base existante que d’ajouter une nouvelle infrastructure critique pour l’orchestration
1 commentaires
Commentaires Hacker News
absurdd’Armin Ronacher est une implémentation de workflows durables pour Postgreshttps://lucumr.pocoo.org/2025/11/3/absurd-workflows/
https://github.com/earendil-works/absurd
https://earendil-works.github.io/absurd/
Je ne l’ai pas utilisé moi-même, mais cela vaut la peine de le comparer aux autres options
absurdet son implémentation dérivée en Rust,durable, me semblent être de bons choix pour garder le côté client très simpleC’est léger, donc un agent de code peut facilement garder toute la structure en tête, et si besoin il suffit d’interroger l’état avec des requêtes
Nous utilisons dbos.dev, restate.dev et cf workflows, et voici ce qui est écrit dans notre Agents.md
Restate.dev est utilisé pour l’intégration de paiement de northflank. C’est plus rapide que cf workflows, indépendant de Cloudflare et de ses pannes, et auto-hébergeable, donc sans verrouillage fournisseur
Cloudflare workflows est utilisé pour les tâches moins critiques, comme la génération de rapports CSV/PDF. Parce que c’est très peu coûteux
DBOS.dev est utilisé pour les workflows qui nécessitent une messagerie atomique liée à des transactions Postgres et une fiabilité/durabilité à 100 %. Par exemple, le remplissage de materialized rows ou l’envoi d’e-mails/push importants aux marchands
DBOS et Restate se ressemblent en apparence, mais Restate nécessite un « orchestrator » central, avec ses avantages et inconvénients, et cela facilite la construction avec les workers serverless de cf/vercel
Il y a aussi VirtualObject, qui est une bonne alternative open source, sans verrouillage fournisseur, au DurableObject mono-thread de Cloudflare
Il y a deux points où DBOS brille particulièrement. 1) Avec
dbos.enqueue_workflow, on peut faire de la messagerie atomique dans la même transaction DB que la logique métier. C’est souvent le point le plus fragile dans ce type de solution, donc le fait de le traiter de manière atomique et durable dans la même transaction que l’exécution de la logique métier réduit fortement la complexité2) DBOS stocke l’état des workflows dans la base de données, donc il devrait être facile de créer des tableaux de bord d’observabilité avec metabase/looker. Ce serait bien que Restate expose aussi son instance rocksdb pour pouvoir la connecter à metabase
Je serais curieux d’avoir le ressenti de ceux qui ont utilisé DBOS et Temporal.
J’ai utilisé Temporal par le passé et ça fonctionnait plutôt bien, mais les limites sur la taille des payloads de requête ou des événements m’ont parfois compliqué la vie au moment de concevoir une solution.
Il y a l’avantage d’imposer de bonnes pratiques d’ingénierie, mais je n’ai pas forcément envie de devoir toujours écrire une logique spéciale consistant à envoyer un fichier CSV sur S3 parce qu’il dépasse 2 Mo, à transmettre le lien, puis à le retélécharger dans le workflow.
Je serais curieux de savoir comment est l’expérience avec DBOS, et comment ça se compare à Temporal en termes de complexité opérationnelle ou d’équivalence fonctionnelle.
Je l’utilise aussi chez moi pour gérer des tâches de domotique qui ne sont pas très sensibles au temps. La latence des workflows n’est pas catastrophique, mais je ne l’utiliserais pas pour des déclencheurs qui doivent réagir immédiatement, comme les événements de détection de mouvement à la maison ; en revanche, pour des timeouts du genre éteindre quelque chose après une période d’inactivité, ça va très bien.
J’aime beaucoup l’approche qui consiste à placer une fine API REST devant Temporal à l’intérieur d’un VPC ou d’un cluster Kubernetes. Ainsi, les déclencheurs basés sur des événements n’ont pas à se soucier de l’authentification Temporal ni de l’état des workflows, et cela aide à garder les événements aussi dépourvus de logique que possible.
Par exemple, un trigger de base de données agit directement ou place un événement dans une file, puis un handler appelle une fine API REST avec les détails d’événement nécessaires. L’API REST peut alors décider s’il faut démarrer un workflow, envoyer un signal à un workflow existant, ou ignorer l’événement. Le pattern varie selon les cas, mais de mon côté j’utilise souvent
SignalWithStart, ou bien je jette simplement l’événement s’il ne vaut pas la peine de démarrer et qu’il n’existe pas déjà de workflow.De plus, la fonctionnalité de workflows parent/enfant est très utile lorsqu’il faut orchestrer des actions indépendantes entre elles dans le cycle de vie d’un même objet, et j’apprécie aussi le fait de pouvoir annuler quand des facteurs externes modifient la trajectoire de progression de cet objet.
Pour le dire de façon longue et vague : c’est très puissant, assez facile à manier, et ça aide énormément à sortir la logique de cycle de vie de l’API. Si on la laisse dans l’API, la dette technique s’accumule facilement et la maintenance devient précaire. Je suis d’accord avec l’idée que ça force à suivre de bonnes pratiques, plutôt que de balancer la logique dans un endroit qui paraît pratique sur le moment mais devient ensuite un piège caché.
En revanche, j’ai essayé l’offre Cloud et j’ai été sidéré par le prix. J’ai brûlé les 1 000 dollars de crédits gratuits avant même de passer en production. Je n’avais pas non plus envie d’exploiter moi-même un Temporal local.
Personnellement, je pense que le mieux est de reprendre seulement les idées de l’architecture et de l’implémenter soi-même avec Postgres.
Je n’en suis pas 100 % satisfait. Ça donne davantage l’impression d’un ajout que d’un élément intrinsèque, et c’est encore une release précoce. Mais, dans les faits, on peut considérer que le problème est désormais réglé.
Après plus d’un an en production, je trouve que Temporal est mal conçu, lent, et absurdement lourd côté infrastructure.
Pour des charges non triviales — par exemple plus de 200 événements par workflow et seulement quelques centaines qui tournent en parallèle toute la journée — on peut finir par dépenser des millions de dollars en coûts d’infrastructure, et malgré ça le résultat reste médiocre.
Quand on lance nos propres benchmarks, les chiffres sont lamentables.
L’équipe commerciale est également vraiment affreuse et a l’air désespérée.
Du point de vue développeur, les SDK sont plutôt bons.
Ne vous laissez pas enfermer dans nexus, et si l’équipe commerciale vous appelle, faites absolument entrer le service juridique dans la réunion.
Il nous a fallu du temps pour comprendre comment migrer depuis Celery, mais dans notre cas cela en valait la peine.
Conductor OSS fait aussi ça plutôt bien https://docs.conductor-oss.org/devguide/ai/index.html
https://github.com/agentspan-ai/agentspan est essentiellement une couche SDK d’agent pour Conductor, qui permet de rendre durables des agents langgraph, OpenAI, vercel et ADK sans modification du code, tout en ajoutant de l’orchestration.
Au lieu de séparer le stockage des données, la machine à états, les contraintes d’états valides et la logique qui effectue les transitions entre états valides, j’aimerais qu’on puisse les intégrer dans une sorte de noyau de l’état de l’application.
Honnêtement, Postgres a déjà beaucoup de ces capacités, mais je ne vois pas encore de vision claire, au niveau applicatif ou produit, qui fournisse l’ensemble prouvable des états dans lesquels une application peut transiter, et qui l’expose automatiquement aux clients d’une manière utile. Par exemple : cet utilisateur peut aimer ce post, mais ne peut pas le modifier.
À mes yeux, cela ressemble à une forme de réseaux de Petri colorés, mais je ne vois toujours pas de paradigme simple d’état applicatif, avec des frontières nettes de réussite comme les bases de données savent en avoir.
Cela dit, je ne suis pas certain qu’il s’agisse d’une intégration complète.
Comme DBOS ne prend pas en charge Rust, j’ai implémenté une version Rust très minimale d’une idée similaire sur https://github.com/tensorzero/durable.
C’était plutôt stable et scalable, mais il faut évidemment être très prudent avec l’implémentation SQL. J’espère que ça intéressera les lecteurs d’ici.
https://flawless.dev/
Je comprends parfaitement le concept et je suis d’accord. C’est une excellente façon d’intégrer ce type de durabilité dans un système de workflow
Cela dit, avec mon cerveau de gamer, j’ai envie d’appeler ça du « save scumming à grande échelle ». Beaucoup de gens savent déjà que cette approche fonctionne, mais ne l’ont peut-être pas reliée à des concepts abstraits d’informatique
Une autre stratégie pour améliorer la robustesse consiste à construire le workflow à partir d’opérations idempotentes. Cela peut être utile quand l’état du workflow devient trop volumineux pour être sauvegardé facilement. À la place, si on relance le travail depuis le début, tout devient un no-op jusqu’au point où la progression reprend
Je suis toujours surpris par tout ce qu’on peut faire avec peu d’outils, du moment que Postgres fait partie de la boîte à outils
J’ai récemment développé une file distribuée qui fonctionne vraiment bien, avec de bons benchmarks, sans conditions de course ni conflits. J’ai utilisé
SKIP LOCKEDpour que les workers puissent se disputer le travail en toute sécuritéOn peut aussi utiliser un mutex de portée session, c’est-à-dire
pg advisory lock, pour éviter les conflits entre workers répartis sur plusieurs nœudsSELECT FOR UPDATEne passe pas bien à l’échelleModification : après revérification, il semble que la recommandation soit désormais l’inverse
Rails propose plusieurs backends de jobs basés sur la base de données, mais par convention un job ne doit faire qu’une seule chose et, si possible, se terminer très vite
Du coup, construire un workflow devient un peu artificiel. On finit par mettre le deuxième job en file à la dernière ligne du premier, puis le troisième à la dernière ligne du deuxième
Le backend de jobs ne les présente pas comme un workflow lié, mais comme des jobs indépendants, et si on essaie de comprendre le workflow à un niveau élevé, il faut lire plusieurs classes de jobs
Rails a récemment introduit le concept de continuable, qui permet de poser des checkpoints étape par étape à l’intérieur d’un job et de reprendre ensuite, mais la convention qui pousse à garder les jobs à responsabilité unique reste forte, donc ça paraît encore maladroit pour de vrais workflows
Je me demande si d’autres ont rencontré ce problème et trouvé une solution
C’est un excellent pattern. Il vaut mieux faire autant de choses que possible dans la base de données
Spanner externe fournit des change streams. Le Spanner interne est différent, principalement parce que certains cas ont des besoins de montée en charge extrêmes, mais aussi pour un mélange de « ça marche déjà bien » et de « les change streams arbitraires font peur »
Le Spanner interne permet à n’importe quelle transaction d’écrire une entrée de file. Ici, la file est en gros une table avec une conscience particulière du temps. On peut planifier la livraison, les entrées sont poussées de la file vers un handler, et le handler peut aussi effectuer des écritures en base dans la transaction de dequeue. Et on conserve toute la même scalabilité