3 puntos por GN⁺ 4 시간 전 | 1 comentarios | Compartir por WhatsApp
  • Extensión de funciones durables que maneja reintentos, programación, fan-out en paralelo y ramificación condicional dentro de PostgreSQL usando solo un pequeño DSL de SQL
  • Funciona únicamente con Postgres y workers en segundo plano, sin contenedores ni servicios externos
  • Todos los pasos registran checkpoints de estado en PostgreSQL, por lo que se reanuda desde el punto de interrupción incluso ante fallas, reinicios o desconexiones
  • El motor de orquestación se encarga de la gestión de colas, seguimiento de estado, recuperación ante fallas, coordinación de pasos y reintentos, así que solo hay que escribir SQL
  • Reemplaza tareas que requerirían más de 300 líneas de código repetitivo en una implementación manual con una sola llamada al DSL, y está disponible de inmediato como open source en PostgreSQL 17

Descripción general y valor principal

  • Función durable resistente a fallas integrada en Postgres que orquesta reintentos, programación, fan-out en paralelo y ramificación condicional con un pequeño DSL de SQL
  • Funciona solo con Postgres + workers en segundo plano, sin infraestructura adicional ni necesidad de contenedores o servicios externos
  • Actúa como un motor de orquestación que se encarga de la gestión de colas, seguimiento de estado, recuperación ante fallas, coordinación de pasos y reintentos; el usuario solo escribe SQL

Cómo sería implementarlo sin pg_durable

  • Para ejecutar 3 agregaciones en paralelo, actualizar un dashboard e incluir reintentos y recuperación ante fallas, se necesitarían más de 300 líneas de código repetitivo
    • Elementos que habría que construir manualmente: configuración de colas, gestión y polling de workers, procesamiento de mensajes y seguimiento de estado, manejo de errores y reintentos, y coordinación manual de pasos
    • El código de ejemplo incluye múltiples tablas de estado como job_queue, job_results, job_state, workflow_steps, step_variables, scheduled_jobs, además de un worker de polling, avance del workflow, recuperación ante fallas, coordinador de ejecución paralela, paso de variables, programación y funciones de limpieza
    • El cálculo de next_run para la programación también requiere una librería externa para parsear cron

Cómo se implementa con pg_durable

  • La misma agregación paralela + actualización de dashboard se expresa con una sola llamada a df.start(), usando el operador & para fan-out y ~> para hacer join
    • Ejemplo: 3 consultas se ramifican en paralelo y luego convergen en el paso refresh dashboard para generar el resultado
    • En el ejemplo de ejecución en vivo, tras 3 pasos ejecutados en paralelo y luego el join, se completa de forma durable hasta dashboard ready en solo 1.9 segundos
  • pg_durable se encarga de la gestión de colas, seguimiento de estado, recuperación ante fallas, coordinación de pasos y reintentos

Características principales

  • Durable por defecto

    • Cada paso registra checkpoints de estado en PostgreSQL, por lo que el workflow sobrevive a fallas, reinicios y desconexiones
    • Se reanuda exactamente desde el punto donde se interrumpió
  • Reintentos automáticos

    • Incluye lógica de reintentos integrada para trabajos inestables; si un paso falla, solo se reintenta ese paso y el resto del workflow sigue avanzando
    • No hace falta código manual para manejo de errores
  • Observabilidad total en SQL

    • Todo el estado del workflow se guarda en tablas de Postgres, por lo que se puede consultar el historial de ejecución, revisar la salida de cada paso y depurar fallas con SQL estándar
    • No se necesitan dashboards externos
  • Ejecución en paralelo

    • Con el operador & o df.join() se hace fan-out de tareas independientes y se ejecutan simultáneamente agregaciones, llamadas a API o pasos ETL con coordinación automática

Patrones que se pueden construir

  • Pipelines ETL

    • Encadena cleanup → transform → load con garantía de secuencia; cada paso espera al anterior y, si falla, el pipeline se detiene limpiamente (~> sequence, |=> variables)
  • Agregación en paralelo

    • Ejecuta al mismo tiempo el conteo de usuarios, la suma de ingresos y la verificación de inventario, haciendo fan-out con varias consultas y esperando a que todo termine (&, df.join())
  • Procesamiento de pedidos

    • Captura un ID de pedido y lo pasa por validación, procesamiento y finalización, con flujo automático de variables entre pasos (|=> capture, $var substitution, df.sleep())
  • Jobs programados

    • Hace polling de APIs, archivado de registros y sincronización de datos con un cron schedule; el loop corre permanentemente y sobrevive a reinicios (@> loop, df.wait_for_schedule())
  • Ramificación condicional

    • Revisa trabajos en espera, número de filas o flags para decidir entre procesar o saltar, con la lógica condicional en SQL y no en la aplicación (df.if(), ?> conditional)
  • Validación de múltiples pasos

    • Obtiene datos → valida esquema → revisa reglas de negocio → aprueba/rechaza; cada paso queda checkpointed para no perder el progreso aunque haya fallas
  • Mantenimiento de base de datos

    • Detecta bloqueos para autovacuum, bloat de tablas y riesgo de wraparound, los expone para revisión y luego los corrige de forma durable incluso tras reinicios (?> conditional, df.wait_for_signal(), @> loop)
  • Azure Functions y HTTP

    • Con df.http() llama directamente desde SQL a Azure Functions o endpoints HTTPS permitidos, y procesa en línea chunking de documentos, enriquecimiento de filas o clasificación de registros
  • Aprobación con humano en el ciclo

    • Aprueba automáticamente tareas rutinarias y pausa tareas de alto riesgo (facturas grandes, operaciones destructivas) hasta recibir una señal de aprobación humana (df.wait_for_signal(), df.if())

Soporte de escritura con IA

  • Si se describe el workflow en inglés natural, Copilot genera el SQL correcto de durable functions, sin necesidad de aprender la sintaxis
  • El repositorio incluye la skill reutilizable pg-durable-sql, que enseña a GitHub Copilot y otros agentes a generar correctamente SQL con operadores, sustitución de variables, loops, joins paralelos y más

Disponible como open source

  • Se ofrece como open source completo sin lista de espera ni lock-in; se puede clonar el repositorio, compilarlo y ejecutarlo de inmediato en tu propio PostgreSQL
  • Permite aplicar orquestación durable en laptops, servidores o la nube

Opción administrada con Azure HorizonDB

  • Azure HorizonDB es el nuevo servicio cloud de PostgreSQL de Microsoft, con pg_durable integrado, lo que permite conservar las durable functions escritas y al mismo tiempo sumar escalabilidad empresarial, seguridad e IA
    • Hasta 3× más rendimiento, autoescalado de almacenamiento hasta 128 TB y escalado horizontal de cómputo hasta 3,072 vCores
    • Detección de amenazas en tiempo real con Microsoft Defender y gestión de identidad con Microsoft Entra ID
    • Búsqueda vectorial con Filtered DiskANN, ranking semántico y curación de modelos de IA dentro de la base de datos
    • Mirroring casi en tiempo real con Microsoft Fabric, integración con VS Code y conexión con GitHub Copilot
  • Pipeline de IA integrado

    • HorizonDB superpone un pipeline de IA administrado de extremo a extremo sobre la ejecución durable de pg_durable, con checkpoints, reintentos y tolerancia a fallas en cada paso
    • Flujo de etapas: Ingest (carga de documentos y datos) → Chunk (división de contenido) → Embed (vectorización) → Index (almacenamiento en DiskANN) → Serve (búsqueda y ranking)

1 comentarios

 
GN⁺ 4 시간 전
Comentarios en Hacker News
  • 2026 parece que será el año de las colas en Postgres: están tendencias como DBOS[0] y pgQue[1], y está bueno que la comunidad esté creando este tipo de opciones
    Aun así, desde la perspectiva de alguien que antes fue ingeniero de aplicaciones, prefiero que la lógica de la cola esté en el código y en Git. Con la herramienta adecuada quizá cambie de opinión
    [0]: https://www.dbos.dev/
    [1]: https://github.com/NikolayS/pgque

    • Puede que la forma de trabajar sea más difícil o simplemente distinta, pero parece que faltan documentación, artículos fáciles de encontrar, experiencia y herramientas
      Me pregunto cómo se manejan el control de versiones, el debugging, las pruebas y los releases. Tener todo en un solo lugar por localidad de datos y por simplicidad del stack suena bien, pero da la sensación de que se pierde mucho conocimiento útil sobre cómo hacerlo “bien”
    • Coincido con “quiero mantener la lógica de la cola en el código”. Las acciones que hay que realizar sobre los datos cambian mucho más seguido que los datos mismos, y no me hace sentido tener que hacer una migración cada vez que cambias ese comportamiento
      Por eso también me molestó mucho que en Supabase, apenas querías hacer algo un poco complejo, ya tenías que crear una función de Postgres. Aun así, en una startup anterior hicimos nuestra propia cola de trabajos simple sobre Postgres, y si hubiera existido algo como pgQue, probablemente habría quedado mucho más pulido
    • Soy fan desde la época de Postgres 7 y he intentado experimentalmente meter la mayor cantidad posible dentro de PostgreSQL, pero al menos en experiencia de desarrollador y observabilidad todavía se queda corto
      Como incluso las extensiones multi-master no son algo que se pueda usar de inmediato con total seguridad, me da cautela meter trabajos complejos con muchas escrituras que adelanten la necesidad de escalar la base de datos
    • En proyectos con triggers de BD, también guardábamos el código SQL en Git
      En algunos casos hasta lo empujábamos dentro de migraciones de Django para que, al configurar en local, los triggers quedaran instalados en la base de datos local
  • Esto huele a procedimientos almacenados. Son difíciles de probar unitariamente y de versionar, y la lógica de negocio queda escondida dentro de la base de datos como una “mente oculta”
    También es difícil aislar cargas de trabajo ruidosas, no hay observabilidad y toda la presión de escalado recae sobre Postgres. Sobre todo, falta entrada/salida para cosas como llamadas a APIs. Para trabajos que solo corren dentro de la base de datos local está bien, pero el caso de uso parece limitado

    • Los procedimientos almacenados también pueden ser excelentes si se usan bien. El control de versiones se puede manejar con IDs monótonamente crecientes al final del nombre; cuando necesitas un cambio incompatible, subes el ID y dejas la versión anterior hasta que ya no se use
      Claro, para eso hace falta un procedimiento de upgrade de base de datos bien hecho. Si un compañero ejecuta migraciones SQL arbitrarias como root, la vas a pasar mal
      Las pruebas unitarias también se pueden hacer igual que cualquier otra prueba de SQL; solo hace falta levantar la base de datos. Si no puedes probar procedimientos almacenados, entonces tampoco tienes forma de probar SQL en general, y ese sí es el verdadero problema
      La alternativa a los procedimientos almacenados no es no poner nada de lógica de negocio en la base de datos, sino que muchas veces terminas con SQL disperso por todo el codebase, difícil de probar, con mal versionado y encapsulación, y además innecesariamente lento
      Lo de la observabilidad es cierto en parte: inspeccionar problemas de SQL suele requerir más trabajo manual que en la mayoría de los lenguajes de programación. Pero si los procedimientos almacenados te están causando problemas de entrada/salida y de escalado, entonces se están usando mal; bien usados, muchas veces reducen muchísimo la entrada/salida y mejoran la escalabilidad
  • Si lo entendí bien, Absurd, hecho por los desarrolladores del harness Pi LLM, parece ir en la dirección de reducir al máximo el acceso puro a base de datos. Apenas estoy empezando a ver este tema
    https://github.com/earendil-works/absurd

    • Corrección menor: absurd parece ser el proyecto original de earendil desde antes de que Mario Zechner se uniera a earendil, y tampoco lo vi en los commits
      Igual no conozco todos los detalles, así que tengo curiosidad genuina
  • En “cuándo no deberías usarlo” dice “cuando el workflow está mayormente fuera de Postgres y se extiende por varios sistemas heterogéneos”, pero entonces no entiendo cómo este proyecto podría compararse con algo como Temporal
    Me pregunto si estoy entendiendo mal la limitación que implica esa recomendación

    • De acuerdo. Mirando el ejemplo en https://github.com/microsoft/pg_durable/blob/main/examples/i..., no me queda muy claro cuál es el valor del proyecto
      Técnicamente puede ser un logro interesante, pero leer SQL así resulta bastante extraño
      SELECT df.start(
      @> (
      ($$SELECT ... FROM demo.invoices WHERE status = 'pending'$$ |=> 'inv')
      ~> df.if_rows('inv',
      $$UPDATE ... SET status = 'processing'$$
      ~> (df.http(...) |=> 'resp')
      ~> df.if($$SELECT $r.ok$$,
      -- classify, branch, wait for signal ...
      ),
      df.sleep(5)
      )
      ),
      'invoice-approval-pipeline'
      );
  • En la empresa estamos atados a Azure y seguimos esperando a que Azure PostgreSQL se ponga al día con las funciones modernas.
    Por ejemplo, no se puede usar esto: https://www.paradedb.com/blog/hybrid-search-in-postgresql-th...
    Tampoco soporta vectores de ultra gran ancho y alta dimensionalidad. Está bien que publiquen pg_durable como open source, pero ojalá primero incorporaran las funciones básicas que en AWS se dan por sentadas.

    • ParadeDB es AGPL, así que normalmente es difícil que los grandes proveedores de nube lo ofrezcan de forma general. Aun así, en Azure HorizonDB se puede usar https://github.com/timescale/pg_textsearch y es muy probable que pronto también llegue a Flex.
      Para transparentarlo, soy mantenedor de pg_textsearch y ahora estoy en Azure. No entendí con precisión lo que comentas sobre soporte vectorial; me pregunto si buscas algo más allá de pgvector + diskann, que ya se ofrece en Azure.
    • Me da la impresión de que para búsqueda vectorial Azure Cosmos DB sería más adecuado.
    • Seguro ya lo evaluaste, pero me da curiosidad por qué no simplemente crear una VM bare metal e instalar la versión más reciente de Postgres.
    • Soy PM del equipo de Azure PG a cargo de las funciones de IA de Postgres. Las capacidades que pediste sí existen, y en los últimos 3 a 6 meses ha habido mucho avance.
      En búsqueda híbrida (BM25 + vectores), el pg_search de ParadeDB tampoco es una función nativa de AWS; hay que alojarlo directamente en EC2. En Azure PostgreSQL hicimos nativo pg_textsearch, que ofrece el mismo modelo de ranking BM25, y uno de sus principales contribuidores ahora está en el equipo de Azure Postgres.
      Documentación: https://learn.microsoft.com/en-us/azure/horizondb/ai/full-te...
      De hecho, en vectores de alta dimensionalidad vamos más adelantados. pgvector con HNSW tiene un límite de 2,000 dimensiones, pero Azure soporta pgvector para almacenamiento y búsqueda vectorial, y para cargas de trabajo grandes y de alta dimensionalidad ofrece pg_diskann, el índice vectorial basado en grafos de Microsoft. Soporta hasta 16,000 dimensiones y también ofrece filtrado avanzado dentro del índice, evaluando condiciones WHERE durante el recorrido del grafo para no perder recall en condiciones selectivas.
      pgvector: https://learn.microsoft.com/en-us/azure/horizondb/ai/vector-...
      Soporte de alta dimensionalidad en DiskANN: https://learn.microsoft.com/en-us/azure/horizondb/ai/vector-...
      Estas funciones ya están disponibles en Azure PostgreSQL, especialmente en Azure HorizonDB Preview. Si tienes una carga de trabajo específica, se puede revisar con más detalle.
  • Esto se siente como una mala solución para un problema viejo que los programadores de DAG como Apache Airflow llevan mucho tiempo resolviendo.
    Se me hace raro querer guardar el flujo de control en la base de datos y no en código. No busco demeritar el proyecto, solo que todavía no lo termino de entender.

    • En Microsoft existe el framework Durable Task[1] para este tipo de casos; también puede ejecutarse como un servicio independiente autohospedado, como Temporal, y en Azure Functions puede correr en modo serverless. Si no recuerdo mal, incluso apareció antes que Airflow y Temporal.
      Este proyecto parece apuntar a casos de uso más específicos de base de datos. La ventaja probablemente sea que puedes seguir el estado exacto del trabajo directamente dentro de la base de datos, sin tener que ir cotejando logs del workflow con el codebase línea por línea. También parecería tener menor carga y menor latencia, además de reducir en uno los componentes que hay que operar.
      [1] https://learn.microsoft.com/en-us/azure/durable-task/common/...
    • Las herramientas externas como Airflow no pueden conocer la carga de la base de datos. Si un desarrollador lanza 200 workers concurrentes contra la base de datos, puede afectar otras cargas de trabajo.
      En cambio, aunque no parece funcionar así por ahora, este enfoque podría ajustarse por sí mismo con retroalimentación de rendimiento casi en tiempo real, sin el costo de latencia de ida y vuelta.
  • Incluso leyendo la documentación y los ejemplos, hay varias cosas que no me quedan claras. Me pregunto cómo funciona df.wait_for_schedule()
    Si se llama desde la aplicación, si es idempotente, si al ejecutarlo dos veces con los mismos parámetros se dispara el tick dos veces, si es algo que se llama manualmente una sola vez desde la consola de consultas, o si se ejecuta como parte de un script de migración, no me queda claro
    También me pregunto si timed_out en el ejemplo[0] es una constante fija que se devuelve cuando se agota el tiempo. Tampoco se ve de inmediato cómo se manejan los errores o las excepciones
    [0] https://github.com/microsoft/pg_durable/blob/main/examples/i...

    • Cuando llamas a df.start(), se crea una función durable y al mismo tiempo se inicia su ejecución. Esa llamada devuelve un ID de instancia que representa esa ejecución y que luego puede usarse para referirse a ella
      Dentro de esa función durable se llama a df.wait_for_signal(), y esa llamada se ejecuta exactamente una vez dentro de esa instancia de función, así que no puede duplicarse. La llamada a df.start() en sí podría duplicarse si se agota el tiempo y se vuelve a ejecutar, pero en ese caso se crearía una instancia de función distinta
      Si durante la ejecución de SQL ocurre un error no controlado, la instancia de función falla y en el estado queda registrado exactamente el error que ocurrió
  • ¿Podrías explicar por qué habría que usar esto en lugar de una herramienta de orquestación fuera de la base de datos? Incluso leyendo el README y los ejemplos todavía no lo termino de entender

    • Si usas el PITR por snapshot de la base de datos, también se restauran junto con ella todas las tareas durables hasta ese punto específico en el tiempo
      Como no hace falta sincronizar los respaldos con otros componentes que pertenezcan al mismo almacenamiento de datos, viene bien para pipelines de ETL o trabajos tipo máquina de estados. Si el ETL es mayormente SQL, también ayuda que el trabajo real se ejecute en el mismo servidor
    • Desde la perspectiva de quienes contribuyen, los clientes de Postgres de Microsoft se dividen de forma bastante pareja en dos grupos: quienes quieren hacer dentro de la base de datos la mayor cantidad de cosas posible, y quienes prefieren mantener la aplicación y el cómputo fuera de la base de datos
    • Puede resultar conveniente cuando, en la arquitectura, la base de datos es el único componente con estado
      Si todo el estado está en una sola base de datos, también es más probable obtener respaldos consistentes
    • Puede integrarse bien con los flujos de trabajo de la aplicación. Por ejemplo, se puede mostrar el progreso desde un enlace permanente en una app frontend, crear flujos de trabajo que continúen después de reiniciar la app y hacerlo sin agregar otro componente de infraestructura
      En https://transport.data.gouv.fr usan Postgres para ese tipo de cosas, y les ha servido en una app de Elixir que hace bastante procesamiento. Todavía no conozco bien pg_durable, pero me identifico porque ya he usado o implementado soluciones parecidas
  • ¿La base de datos no es ya una de las piezas de infraestructura más difíciles de escalar? No entiendo por qué querrías además cargarle trabajos de larga duración

    • Ejecutar trabajos de larga duración en Postgres no es para nada algo nuevo. Un ejemplo es pg_cron
      Al final, estas cargas de trabajo serán tareas que se ejecutan contra la base de datos, ya sea que las dispare o no un componente externo. En pipelines de datos o de IA también se ha vuelto más común enviar consultas HTTP desde la base de datos para evitar viajes de ida y vuelta y puntos de falla adicionales por componentes extra. Aun así, decidir si llevar el cómputo hacia los datos o los datos hacia el cómputo es una elección de diseño importante y muy debatida
  • Se siente como otra https://en.wikipedia.org/wiki/Inner-platform_effect que no haría falta si los lenguajes de programación o máquinas virtuales populares ya soportaran determinismo, ejecución paso a paso medible y controlable, pausa del estado en tiempo de ejecución, serialización y deserialización, y reanudación