- Los flujos de trabajo durables hacen checkpoint del estado de ejecución en la base de datos, para poder recuperarse desde el último paso completado después de un fallo
- La orquestación externa al estilo de Temporal, Airflow o AWS Step Functions agrega un orquestador central y un almacén de datos, aumentando la complejidad
- Una arquitectura basada en Postgres permite que los servidores de aplicaciones consulten la tabla de workflows y guarden directamente la salida de cada paso para hacer posible la recuperación
- Varios servidores evitan ejecuciones duplicadas mediante bloqueos y restricciones de integridad, y abordan los problemas de escalabilidad, disponibilidad y observabilidad con soluciones de Postgres
- Un solo servidor Postgres puede escalar verticalmente hasta decenas de miles de workflows por segundo, y aprovechar replicación, multi-AZ y análisis con SQL tal como están disponibles
El modelo básico de los flujos de trabajo durables
- Un flujo de trabajo durable guarda periódicamente checkpoints del progreso de la ejecución de un programa en una base de datos, para que pueda recuperarse desde el último paso completado después de un choque o una falla
- Puede verse como el modelo de guardar y cargar de un videojuego: el progreso del programa se “guarda” periódicamente y, tras un fallo, se “carga” desde el último checkpoint
- Una implementación común son los sistemas de orquestación externa como Temporal, Airflow y AWS Step Functions
- En la orquestación externa, el programa durable se escribe como un workflow compuesto por varias etapas, y un orquestador central coordina la ejecución
Cómo funciona la orquestación externa
- Cuando el cliente envía un workflow, el orquestador crea un registro en el almacén de datos y lo despacha a un worker para su ejecución
- Cada vez que el worker completa una etapa, envía el resultado al orquestador, que hace checkpoint de esa salida en el almacén de datos antes de despachar la siguiente etapa
- Si un worker falla o se cae, el orquestador vuelve a despachar ese workflow a otro worker, que lo reanuda desde la última etapa guardada en checkpoint
- Esta arquitectura aumenta la complejidad al agregar un servidor orquestador separado a la idea central de guardar el estado del workflow en una base de datos
Arquitectura que usa Postgres como orquestador
- Si la esencia de un flujo de trabajo durable es hacer checkpoint del estado del programa en la base de datos, entonces usar la propia base de datos como orquestador, sin un servidor orquestador separado, resulta más simple y eficiente
- Postgres es una buena opción para construir flujos de trabajo durables por su popularidad, escalabilidad y rico ecosistema
- En un sistema de flujos de trabajo durables basado en Postgres, los servidores de aplicaciones se comunican directamente con Postgres para ejecutar workflows, sin pasar por un orquestador central
- El cliente envía la ejecución creando una entrada en la tabla de workflows de Postgres, y los servidores de aplicaciones consultan esa tabla para desencolar y ejecutar workflows
- Mientras ejecutan el workflow, los servidores guardan en checkpoint en Postgres la salida de cada paso, y si el servidor en ejecución falla o se cae, otro servidor recupera el workflow desde el checkpoint
Por qué deja de ser necesario un orquestador central
- Los servidores de aplicaciones pueden coordinarse mediante Postgres, por lo que no hace falta que un orquestador central despache workflows a los workers
- Los servidores pueden desencolar workflows de manera cooperativa desde tablas de Postgres, y mecanismos como las cláusulas de bloqueo permiten garantizar que cada workflow sea desencolado por exactamente un worker
- Los checkpoints de la salida de cada etapa también los escribe directamente el worker en Postgres, no el orquestador
- Si varios workers intentan ejecutar el mismo workflow al mismo tiempo, las restricciones de integridad de Postgres pueden detectar trabajo duplicado en el momento del checkpoint y hacer que uno retroceda
- Reemplazar el orquestador central con Postgres u otra base de datos permite abordar problemas como escalabilidad, disponibilidad, observabilidad y seguridad con soluciones nativas de Postgres ya bien conocidas
Escalabilidad y disponibilidad
- La escalabilidad y disponibilidad de un sistema de flujos de trabajo durables basado en base de datos dependen fundamentalmente de la base de datos subyacente
- Como se puede escalar horizontalmente agregando más servidores worker, la capacidad máxima de procesamiento depende de qué tan rápido la base de datos pueda manejar workflows
- Los workers son intercambiables y pueden recuperar el estado unos de otros, por lo que el sistema sigue disponible mientras la base de datos subyacente lo esté
- Al usar Postgres, una ventaja es que la escalabilidad y la disponibilidad son problemas estudiados durante mucho tiempo y cuentan con soluciones robustas
- Un solo servidor Postgres puede escalar verticalmente para manejar decenas de miles de workflows por segundo
- Se puede lograr escalado adicional usando Postgres distribuido como CockroachDB o Postgres con sharding
- Postgres admite replicación por streaming con failover automático, y los servicios administrados ofrecen despliegues multi-AZ y SLA de alta disponibilidad de forma predeterminada
- Décadas de ingeniería e investigación acumuladas para operar Postgres a gran escala pueden aprovecharse directamente también para operar flujos de trabajo durables
Observabilidad
- En una ejecución durable basada en Postgres, los workflows y sus etapas se guardan en checkpoint en tablas de Postgres, por lo que se pueden escanear esos checkpoints para monitorear workflows en tiempo real y visualizar la ejecución
- Postgres destaca porque casi cualquier consulta de observabilidad de workflows puede expresarse en SQL
- Tareas complejas de filtrado y análisis, como una consulta para encontrar todos los workflows que fallaron durante el último mes, pueden expresarse de forma declarativa en SQL
- Esto es posible gracias a que aprovecha el modelo relacional de Postgres y décadas de investigación en optimización de consultas
- Muchos sistemas con modelos de datos más simples, como los almacenes clave-valor usados por orquestadores externos populares, no ofrecen el mismo nivel de soporte para análisis basados en SQL
- Si se guardan los datos de workflows y etapas en tablas de Postgres y se agregan índices secundarios para consultas analíticas rápidas, se puede obtener una observabilidad eficiente en la ejecución durable
Confiabilidad y seguridad
- En la ejecución durable que usa un orquestador externo, tanto el orquestador como su almacén de datos se convierten en puntos únicos de falla
- Como el orquestador y el almacén de datos coordinan directamente la ejecución del workflow, si cualquiera de los dos deja de estar disponible, toda la aplicación queda inutilizable
- Además, como procesan y almacenan checkpoints de workflows y etapas, pueden tener acceso a datos sensibles de la aplicación, por lo que requieren endurecimiento, control de acceso y auditoría como cualquier infraestructura sensible
- En una ejecución durable basada en Postgres, el único punto de falla es el propio Postgres, y todos los datos del workflow se almacenan directamente allí sin pasar por otros sistemas
- Si la aplicación ya depende de Postgres, adoptar ejecución durable no agrega nuevos puntos de falla ni crea una nueva superficie de ataque que haya que proteger
- Como la base de datos ya es infraestructura crítica, resulta más razonable reutilizar la base de datos existente que agregar nueva infraestructura crítica para la orquestación
1 comentarios
Comentarios en Hacker News
absurdde Armin Ronacher es una implementación de workflows durables para Postgreshttps://lucumr.pocoo.org/2025/11/3/absurd-workflows/
https://github.com/earendil-works/absurd
https://earendil-works.github.io/absurd/
No lo he usado directamente, pero parece una opción que vale la pena comparar con otras
absurdy su implementación derivada en Rust,durable, me parecen buenas opciones para mantener el lado del cliente muy simpleEs lo bastante ligero como para que un agente de código pueda tener toda la estructura en la cabeza con facilidad y, si hace falta, consultar el estado con queries
Estamos usando dbos.dev, restate.dev y cf workflows, y en nuestro Agents.md tenemos esto escrito
Restate.dev lo usamos para la integración de pagos de northflank. Es más rápido que cf workflows, independiente de Cloudflare y de sus caídas, y se puede self-hostear, así que no hay lock-in de proveedor
Cloudflare workflows lo usamos para tareas menos importantes, como generar reportes CSV/PDF. Sobre todo porque es muy barato
DBOS.dev lo usamos para workflows que necesitan mensajería atómica ligada a transacciones de Postgres y requieren 100% de confiabilidad/durabilidad. Por ejemplo, poblar materialized rows o enviar correos/push importantes para los comercios
DBOS y Restate se ven parecidos por fuera, pero Restate necesita un “orchestrator” central, lo cual tiene pros y contras, y facilita construirlo junto con workers serverless de cf/vercel
Además tiene VirtualObject, así que también es una buena alternativa open source sin lock-in de proveedor frente a DurableObject de un solo hilo de Cloudflare
Hay dos puntos donde DBOS realmente destaca. 1) Con
dbos.enqueue_workflowpuedes hacer mensajería atómica dentro de la misma transacción de DB que la lógica de negocio. Esa parte suele ser el caso más frágil en casi cualquier solución, así que manejarlo de forma atómica y durable en la misma transacción donde ejecutas la lógica de negocio reduce mucho la complejidad2) Como DBOS guarda el estado del workflow en la DB, parece que sería fácil crear dashboards de observabilidad con metabase/looker. También estaría bien que Restate expusiera una instancia de rocksdb para poder conectarla a metabase
Me da curiosidad la experiencia práctica de quienes han usado DBOS y Temporal.
Probé Temporal hace tiempo y funcionaba bastante bien, pero en algún momento fue incómodo al diseñar soluciones por los límites en el tamaño del payload de las solicitudes o de los eventos.
Tiene la ventaja de forzar buenas prácticas de ingeniería, pero no siempre quisiera usar una lógica especial en la que, si un archivo CSV pesa más de 2 MB, lo subes a S3, pasas el enlace y luego el workflow lo vuelve a descargar.
Me interesa saber cómo ha sido la experiencia con DBOS y cómo se compara con Temporal en complejidad operativa o paridad de funciones.
También lo corro en casa para tareas de automatización del hogar que no son muy sensibles al tiempo. La latencia del workflow no es terrible, pero no lo usaría para triggers que requieren reacción inmediata, como eventos de detección de movimiento en casa; para timeouts del tipo apagar algo después de un periodo de inactividad, sí está bien.
Me gusta bastante poner una REST API delgada delante de Temporal dentro de una VPC o un clúster de Kubernetes. Así, los triggers basados en eventos no tienen que preocuparse por la autenticación de Temporal ni por consultar el estado del workflow, y además ayuda a mantener los eventos con la menor lógica posible.
Por ejemplo, un trigger de BD actúa directamente o mete un evento en una cola, y el handler llama a una REST API delgada con los detalles del evento que hagan falta. La REST API puede decidir si esto debe iniciar un workflow, enviar un signal a uno existente o ignorarlo. El patrón cambia según el caso, pero en mi caso uso mucho
SignalWithStart, o simplemente lo descarto si no vale la pena iniciarlo y tampoco existe un workflow previo.Además, cuando hay que orquestar acciones mutuamente independientes dentro del ciclo de vida de un solo objeto, la función de workflows padre/hijo es muy útil, y también me gusta que se puedan cancelar cuando factores externos cambian la trayectoria del objeto.
Para decirlo de forma larga y vaga, es muy potente, fácil de manejar y ayuda mucho a sacar la lógica de ciclo de vida fuera de la API. Si la dejas dentro de la API, es fácil acumular deuda técnica y que el mantenimiento se vuelva frágil. Coincido en que obliga a seguir prácticas recomendadas, en lugar de aventar la lógica a un lugar que parece fácil y luego descubrir trampas ocultas más adelante.
Pero probé el producto Cloud y el precio me dejó en shock. Me gasté los 1,000 dólares de créditos gratis antes siquiera de ponerlo en producción. Tampoco quería operar Temporal local por mi cuenta.
Personalmente, creo que lo mejor es tomar solo las ideas de la arquitectura e implementarlas uno mismo sobre Postgres.
No me encanta al 100%. Se siente más como un agregado que como una parte esencial, y todavía es un lanzamiento temprano. Aun así, en la práctica ya puede considerarse resuelto.
Después de más de un año corriéndolo en producción, mi opinión es que Temporal está mal diseñado, es lento y absurdamente pesado desde el punto de vista de infraestructura.
Con trabajos que no son triviales, por ejemplo más de 200 eventos por workflow y apenas unos cientos ejecutándose en paralelo durante todo el día, podrías terminar gastando millones de dólares en infraestructura y aun así seguiría siendo malo.
Si corres tus propios benchmarks, los números son pésimos.
El equipo de ventas también es realmente terrible y se ve desesperado.
Desde la perspectiva del desarrollador, el SDK sí está bastante bien.
No te quedes atrapado en nexus, y si el equipo de ventas te llama, más te vale meter al equipo legal en la llamada.
Nos tomó tiempo entender cómo migrar desde Celery, pero en nuestro caso valió la pena.
Conductor OSS también hace esto bastante bien https://docs.conductor-oss.org/devguide/ai/index.html
https://github.com/agentspan-ai/agentspan es básicamente una capa de SDK de agentes para Conductor, con la que puedes volver durables y agregar orquestación a agentes de langgraph, OpenAI, vercel y ADK sin cambiar el código.
Me gustaría que, en vez de separar el almacén de datos, la máquina de estados, las restricciones de estados válidos y la lógica que hace transiciones entre esos estados, se pudiera integrar todo eso en una especie de núcleo del estado de la aplicación.
Siendo sincero, Postgres ya tiene bastante de esa capacidad, pero todavía no veo una visión clara a nivel de aplicación o producto que ofrezca un conjunto demostrable de estados a los que la app puede transicionar, y que además eso se exponga automáticamente al cliente de una forma útil. Por ejemplo, algo como: este usuario puede dar like a esta publicación, pero no puede editarla.
A mis ojos se parece a una forma de redes de Petri coloreadas, pero todavía no veo un paradigma sencillo para el estado de la app del mismo modo en que las bases de datos tienen fronteras claras de éxito.
Aun así, no estoy seguro de que sea una integración completa.
Como DBOS no soporta Rust, implementé una versión muy mínima y parecida en Rust en https://github.com/tensorzero/durable.
Ha sido bastante estable y escalable, aunque por supuesto hay que tener muchísimo cuidado con la implementación SQL. Ojalá les resulte interesante a los lectores de aquí.
https://flawless.dev/
Entiendo completamente el concepto y estoy de acuerdo. Es una gran manera de incorporar este tipo de durabilidad en un sistema de workflows
Pero, con mi cerebro de gamer, quiero llamar a esto save scumming a gran escala. Mucha gente ya sabe que este enfoque funciona, aunque quizá no lo haya conectado con conceptos abstractos de ciencias de la computación
Otra estrategia para aumentar la robustez es componer el workflow con operaciones idempotentes. Eso puede ser útil cuando el estado del workflow es demasiado grande como para respaldarlo fácilmente. En su lugar, si vuelves a ejecutar el trabajo desde el principio, todo hasta el punto en que vuelva a haber progreso se convierte en un no-op
Me sigue sorprendiendo lo mucho que se puede hacer con pocas herramientas con solo tener Postgres en la caja de herramientas
Hace poco desarrollé una cola distribuida y funciona realmente bien, con buenos benchmarks y sin condiciones de carrera ni conflictos. Usé
SKIP LOCKEDpara que los workers puedan competir de forma seguraSi quieres que workers repartidos entre varios nodos eviten chocar entre sí, también puedes usar un mutex con alcance de sesión, es decir,
pg advisory lockSELECT FOR UPDATEtomados no escala bienEdit: revisándolo otra vez, parece que ahora la recomendación va en sentido contrario
En Rails hay varios backends de trabajos basados en base de datos, pero por convención un trabajo siempre debe hacer una sola cosa y, si es posible, terminar muy rápido
Por eso construir workflows se vuelve algo forzado. Terminas poniendo en cola el segundo trabajo en la última línea del primero, y el tercero en la última línea del segundo
El backend de trabajos no los muestra como un workflow conectado, sino que los trata como trabajos independientes, y si intentas entender el workflow a alto nivel tienes que leer varias clases de trabajos
Rails introdujo recientemente el concepto de continuable, que permite hacer checkpoints paso a paso y reanudar dentro de un trabajo, pero todavía existe una convención fuerte de mantener los trabajos con una sola responsabilidad, así que sigue sintiéndose incómodo para usarlo como un workflow real
Me da curiosidad si otras personas también se han topado con esto y si encontraron alguna solución
Este es un patrón excelente. Conviene hacer dentro de la base de datos todo lo que sea posible
El Spanner externo ofrece change streams. El Spanner interno es distinto, principalmente porque en algunos casos tiene requisitos de escalado extremos, y también por una mezcla de "ya funciona bien" y "los change streams arbitrarios dan miedo"
El Spanner interno permite que cualquier transacción escriba entradas de cola. Aquí la cola es, más o menos, una tabla especial con noción del tiempo. Puedes programar la entrega, las entradas se empujan desde la cola hacia un handler, y el handler también puede hacer escrituras en la BD dentro de la transacción de dequeue. Y se mantiene toda la misma escalabilidad