BookinglyTech News
Infraestructura

Durable execution sin orquestador: Postgres como cola de trabajo con SKIP LOCKED

El equipo de Kestrel Workflows cuenta cómo gestiona ejecuciones durables apoyándose solo en una tabla de Postgres, sin Temporal ni Step Functions en la ruta crítica.

3 min de lecturaInfoQ0 vistas

Levantar Temporal o AWS Step Functions es la respuesta habitual cuando alguien pide ejecución durable. El equipo de Kestrel Workflows, que automatiza respuesta a incidentes, aprovisionamiento en la nube, CI/CD y peticiones de desarrolladores, decidió que no: puso el orquestador dentro de Postgres, la base de datos que ya operaba, y sacó de la ruta crítica un sistema con estado que habría que desplegar, asegurar, monitorizar y actualizar.

La idea de la ejecución durable es vieja y sencilla. El programa va guardando su avance en un sistema externo; si el proceso muere, otro arranca desde el último paso completado. Es el autoguardado de un videojuego, pero para código de backend. Lo normal es delegar ese punto de control en un orquestador central que persiste el estado, despacha cada paso a un worker y reasigna lo que quede colgando cuando un worker muere. La pega es que ese orquestador se convierte en dependencia obligatoria de cada flujo, con su propio modelo de datos, sus controles de acceso y sus payloads viajando fuera: topología de infraestructura, código fuente, logs.

Una tabla como cola de trabajo

En Kestrel no hay proceso orquestador. Cada servidor de aplicación lleva embebida la librería de flujos y habla directamente con Postgres. Cada disparo, venga de PagerDuty o de un webhook de GitHub, inserta una fila en una tabla de ejecuciones con estado, entrada en JSONB, un identificador de worker propietario y una fecha de caducidad del lease. Los servidores hacen polling sobre esa tabla para reclamar trabajo.

Lo que hace segura la coordinación es una cláusula: SELECT ... FOR UPDATE SKIP LOCKED. Bloquea las filas que un worker coge y ordena al resto que las salte en lugar de esperar, de forma que dos servidores pueden consultar la misma tabla a la vez sin entregar el mismo flujo a dos ejecutores. La reclamación va dentro de una transacción corta que además cambia el estado a en ejecución y escribe el lease, con una ventana de 30 segundos en el ejemplo del artículo. El patrón no es exclusivo de Postgres: MySQL 8.0 o superior, MariaDB 10.6 y Oracle soportan SKIP LOCKED, y Db2 y SQL Server tienen mecanismos equivalentes para saltar filas bloqueadas.

Recuperación y lo que se gana

Para los caídos está el patrón de lease y barredor. Los workers van renovando el latido de las filas que poseen y una consulta periódica vuelve a encolar cualquier ejecución cuyo lease haya expirado. La idempotencia se apoya en la propia base de datos: si los checkpoints de cada paso van atados a una clave primaria, un paso que se reejecuta tras un fallo lee su resultado anterior en lugar de repetir el efecto. Y como el estado vive en la base principal, la observabilidad es una consulta SQL normal en vez de una API ajena.

El atractivo es evidente: menos sistemas con estado, un único modelo de datos relacional para analizar lo que pasa, la seguridad reducida a una dependencia que ya tienes y un punto de fallo menos en medio de todo. El coste también es claro. Ahora Postgres es el orquestador, así que su salud, sus ventanas de mantenimiento y la latencia de sus transacciones son las de tus flujos. Quien no tenga la base bien dimensionada, o no quiera asumir leases caducados y barredores como parte del diseño, seguirá pagando el orquestador externo.