Si tienes un backend pequeño o mediano, seguro ya conoces este problema: una parte de tu sistema necesita hacer trabajo confiable en segundo plano, reintentar si falla, no duplicar acciones y sobrevivir a reinicios. En ese punto, muchas arquitecturas se llenan de piezas: una cola para encolar jobs, un worker pool para procesarlos, un orquestador para coordinar pasos, otra tabla para auditoría y, a veces, un sistema de estado aparte para saber en qué va cada ejecución.
La idea de usar Postgres como base para workflows durables parte de una pregunta bastante práctica: ¿y si no necesitas sumar más infraestructura para resolver eso? Si tu equipo ya depende de Postgres para casi todo, quizá puedas guardar el estado del workflow, coordinar transiciones y asegurar idempotencia sin introducir una cola externa o un orquestador pesado desde el día uno.
Qué significa realmente un workflow durable
Un workflow durable no es solo “un job que corre en background”. Hablamos de una secuencia de pasos que debe poder pausar, reanudarse, reintentarse y seguir siendo consistente aunque el proceso se caiga, el servidor se reinicie o un paso tarde más de lo normal. En otras palabras, el sistema no debería depender de que una instancia viva todo el tiempo para recordar lo que iba haciendo.
Esto importa porque muchos flujos de negocio no son triviales. Piensa en un cobro que debe validar saldo, registrar la transacción, enviar un correo y luego actualizar inventario. Si falla el envío del correo, no quieres repetir el cobro. Si se cae el worker después de registrar la transacción, no quieres perder el progreso. Y si el usuario vuelve a intentar la operación, tampoco quieres duplicar resultados.
Lo que suele romperse primero
Los fallos típicos no son espectaculares. Son los que pasan en producción un martes cualquiera:
- El worker termina a la mitad de un paso por un deploy.
- El mensaje se procesa dos veces porque hubo un retry sin idempotencia.
- La cola dice que el job fue entregado, pero el proceso nunca guardó su avance.
- Un orquestador externo añade latencia y complejidad para un flujo que solo tenía 4 pasos.
Cuando eso pasa, el problema no es solo técnico. También es operativo. Tienes más dashboards, más alertas, más estados intermedios y más lugares donde buscar qué salió mal.
Por qué Postgres encaja mejor de lo que parece
Postgres no es solo una base relacional para guardar usuarios y pedidos. También te da transacciones, locks, constraints, índices, JSONB, triggers y una semántica de consistencia que sirve muchísimo para coordinar trabajo durable. Si tu flujo necesita que un cambio de estado y una acción asociada queden atados, la transacción de Postgres es una herramienta bastante sólida.
La propuesta no es usar la base como si fuera una cola genérica para todo. La propuesta es más específica: si tu workflow está estrechamente ligado a datos que ya viven en Postgres, puedes usar la misma base para persistir estado, coordinar ejecución y guardar intentos. Eso reduce la cantidad de sistemas que tienes que operar.
La documentación oficial de Postgres sobre transacciones y bloqueo te da la base conceptual para este enfoque. Puedes revisar la guía oficial de transaction isolation y la referencia de SELECT … FOR UPDATE para entender cómo se protege el acceso concurrente.
La ventaja real: menos piezas, menos fallos
En equipos pequeños y medianos, el costo de operar una cola más un orquestador más una base de datos no siempre se justifica. No es solo el costo en infraestructura. También cuentas el tiempo de aprendizaje, el tiempo de debugging y el tiempo que pierdes cuando una pieza tiene un comportamiento raro bajo carga.
Con Postgres como centro del workflow, puedes tener:
- una sola fuente de verdad para el estado,
- transacciones para cambios atómicos,
- reintentos controlados desde la aplicación,
- y observabilidad directa con consultas SQL.
Si tu equipo ya sabe SQL, eso baja bastante la fricción. No necesitas que todos dominen una DSL nueva o entiendan cómo funciona un scheduler distribuido solo para mover cuatro estados.
Cómo se ve una implementación práctica
La forma más simple de pensar esto es como una tabla de workflows y otra de pasos o eventos. Cada workflow tiene un estado, un payload, un contador de reintentos y marcas de tiempo. Cada paso puede registrar si empezó, si terminó, si falló y con qué error.
Un patrón común es este:
- crear el workflow dentro de una transacción,
- dejarlo en estado
pending, - tomarlo para ejecución con un lock o con una actualización condicional,
- ejecutar el paso fuera de la transacción si hace trabajo externo,
- y luego persistir el resultado o el siguiente estado.
Eso permite que el sistema sobreviva a reinicios sin perder contexto. También te deja consultar con SQL qué quedó pendiente, qué falló y qué lleva más tiempo del esperado.
Ejemplo de esquema mínimo
No necesitas una estructura enorme para empezar. Algo así suele bastar para un caso inicial:
CREATE TABLE workflows (
id BIGSERIAL PRIMARY KEY,
type TEXT NOT NULL,
state TEXT NOT NULL DEFAULT 'pending',
payload JSONB NOT NULL,
attempts INT NOT NULL DEFAULT 0,
locked_at TIMESTAMPTZ,
last_error TEXT,
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
CREATE INDEX workflows_state_created_at_idx
ON workflows (state, created_at);
Con algo así ya puedes listar pendientes, reclamar trabajo sin duplicarlo y auditar errores. Si más adelante necesitas granularidad por paso, agregas una tabla de steps o events.
Un flujo típico de ejecución
- Insertas el workflow con su payload.
- Un worker toma un registro
pendingy lo marcarunningen una transacción. - Ejecuta el paso externo, por ejemplo llamar a una API de pagos.
- Guarda el resultado o el error en la misma base.
- Si falla, incrementa
attemptsy decide si reintenta o pasa afailed.
Ese patrón funciona bien cuando el estado del negocio y el estado técnico viven cerca. No elimina los problemas de consistencia por arte de magia, pero sí hace más fácil razonar sobre ellos.
Qué ganas frente a una cola u orquestador dedicado
Una cola dedicada sigue siendo útil en muchos casos. Lo mismo pasa con un orquestador de workflows. El punto no es que Postgres los reemplace siempre, sino que en varios equipos el stack se sobredimensiona demasiado pronto. Si tu flujo no necesita millones de eventos por minuto ni una topología compleja de fan-out/fan-in, quizá te conviene empezar más simple.
La diferencia más visible está en la operación diaria. Una cola más un orquestador te obliga a revisar más componentes cuando algo falla. Con Postgres, muchas veces puedes depurar con una consulta y ver el estado real del proceso sin saltar entre sistemas.
Comparación rápida
| Opción | Ventaja principal | Costo operativo | Cuándo encaja mejor |
|---|---|---|---|
| Postgres como workflow store | Menos piezas y transacciones claras | Bajo | Equipos pequeños o medianos, flujos acoplados a datos |
| Cola dedicada | Buen desacople y throughput alto | Medio | Jobs simples, alto volumen, procesamiento asíncrono general |
| Orquestador de workflows | Visibilidad y control de pasos complejos | Alto | Flujos largos, múltiples ramas, dependencias entre servicios |
Si trabajas en una startup en LatAm, esta decisión suele ser todavía más sensible. Menos infraestructura significa menos costo mensual, menos dependencia de especialistas y menos tiempo perdido en operación. En contextos donde el equipo de backend es de 2 a 6 personas, eso pesa bastante.
Cuándo sí se te queda corto
Postgres no es la respuesta ideal para todo. Se te puede quedar corto si:
- tienes un volumen muy alto de eventos por segundo,
- necesitas fan-out masivo entre muchos consumidores,
- requieres workflows con muchas ramas y compensaciones complejas,
- o quieres separar por completo la ejecución del almacenamiento.
En esos casos, una cola o un motor de workflows dedicado puede valer la pena. La clave es no asumir que necesitas esa complejidad desde el inicio.
Patrones que hacen que funcione bien
Si vas a usar Postgres para workflows durables, hay algunos patrones que te ahorran dolores de cabeza. El más importante es la idempotencia. Si un paso se ejecuta dos veces por un retry, el sistema no debería duplicar efectos externos. Eso significa usar claves únicas, estados bien definidos y verificaciones antes de ejecutar acciones irreversibles.
También conviene separar claramente los pasos que tocan sistemas externos de los pasos internos. Lo que cambia solo dentro de la base puede ir en una transacción. Lo que llama a una API externa casi siempre debe ejecutarse fuera de la transacción, pero con un registro previo y posterior que permita reanudar si algo falla.
Buenas prácticas concretas
- Usa estados explícitos como
pending,running,succeeded,failed. - Guarda un
attemptscount para controlar reintentos. - Registra
locked_atoclaimed_atpara detectar trabajos atascados. - Usa
UNIQUEconstraints para evitar duplicados. - Separa el payload del estado para no mezclar datos de negocio con metadata operativa.
- Define una política de retry con backoff, por ejemplo 3 intentos con esperas de 5, 30 y 120 segundos.
Si necesitas un modelo más robusto, puedes apoyarte en eventos. En vez de sobrescribir un único registro, guardas una secuencia de cambios: creado, reclamado, falló, reintentado, completado. Eso te da trazabilidad sin salir de Postgres.
Cuándo esta estrategia te simplifica de verdad
La ganancia aparece cuando el workflow está cerca del dominio principal de tu producto. Por ejemplo, onboarding de usuarios, cobros, provisioning de cuentas, sincronización con CRM, generación de reportes o procesamiento de archivos. Son casos donde el estado del proceso importa tanto como el resultado final.
También funciona bien cuando tu equipo quiere moverse rápido sin sumar demasiadas piezas. Si ya tienes Postgres, ya sabes operarlo y ya lo monitoreas, meterlo al centro del workflow reduce el salto mental. No necesitas aprender otra consola para ver si un job quedó a medias.
Señales de que te conviene probarlo
- Tu sistema ya depende de Postgres como base principal.
- Los workflows son de baja o media complejidad.
- El equipo es pequeño y no quiere operar más servicios.
- Los errores más frecuentes son duplicados, retries y estados perdidos.
- El negocio necesita trazabilidad simple, no una orquestación de alto nivel.
Si cumples varias de esas condiciones, vale la pena hacer un prototipo. No hace falta migrar todo. Puedes empezar con un flujo concreto y medir cuántas piezas eliminas y cuánto tiempo ahorras en debugging.
Tabla resumen
| Pregunta corta | Respuesta corta |
|---|---|
| ¿Postgres reemplaza siempre a una cola? | No, pero puede cubrir muchos workflows internos sin sumar otra pieza. |
| ¿Qué problema resuelve mejor? | Persistir estado y coordinar reintentos de forma durable. |
| ¿Qué necesitas para empezar? | Una tabla de workflows, estados claros e idempotencia. |
| ¿Cuándo no conviene? | Cuando hay alto volumen, muchas ramas o desacople fuerte entre servicios. |
| ¿Qué gana un equipo pequeño? | Menos infraestructura, menos operación y debugging más simple. |
| ¿Qué dato debes guardar sí o sí? | Estado actual, intentos y timestamps de bloqueo o actualización. |
En la práctica, usar Postgres para workflows durables no significa negar que existan herramientas especializadas. Significa elegir la herramienta más simple que cubra tu caso real. Si tu backend todavía puede vivir con una base bien diseñada, quizá no necesitas una cola, un scheduler y un orquestador para resolver algo que Postgres ya sabe hacer bastante bien.
Preguntas frecuentes
¿Postgres sirve para cualquier workflow durable?
¿No es mala idea usar la base de datos como cola?
¿Qué gana un equipo pequeño o mediano con este enfoque?
¿Cómo evito procesar un workflow dos veces?
¿Necesito tablas separadas para pasos y eventos?
¿Esto aplica bien para equipos en LatAm?
¿Cuándo debería pasarme a una cola u orquestador dedicado?
Azirgo
¿Listo para construir tu Producto Digital?
Sitios web, apps móviles, software a medida y soluciones blockchain. Cuéntanos qué tienes en mente y armamos un plan claro contigo.
- Cotización clara en 48 horas
- Equipo en Ecuador, atención en español
- Desde un MVP hasta un producto en producción