3 pontos por GN⁺ 4 시간 전 | 1 comentários | Compartilhar no WhatsApp
  • Extensão de funções duráveis que lida com retries, agendamento, fan-out paralelo e desvios condicionais dentro do PostgreSQL usando apenas uma pequena DSL em SQL
  • Funciona apenas com Postgres e background workers, sem contêineres nem serviços externos
  • Todas as etapas registram checkpoints de estado no PostgreSQL, permitindo retomar do ponto de interrupção mesmo após falha, reinício ou perda de conexão
  • Sem precisar implementar manualmente gerenciamento de filas, rastreamento de estado, recuperação de falhas, coordenação de etapas e retries: basta escrever SQL e o motor de orquestração faz o resto
  • Substitui tarefas que exigiriam mais de 300 linhas de boilerplate por uma única chamada DSL, com uso imediato em código aberto no PostgreSQL 17

Visão geral e valor principal

  • Função durável embutida no Postgres, à prova de falhas (crash-proof), que orquestra retries, agendamento, fan-out paralelo e desvios condicionais com uma pequena DSL em SQL
  • Funciona apenas com Postgres + background workers, sem infraestrutura adicional, contêineres separados ou serviços externos
  • Atua como um motor de orquestração responsável por gerenciamento de filas, rastreamento de estado, recuperação de falhas, coordenação de etapas e retries; o usuário só escreve SQL

Como seria implementar sem pg_durable

  • Para executar 3 agregações em paralelo, atualizar um dashboard e ainda incluir retries e recuperação de falhas, seriam necessárias mais de 300 linhas de boilerplate
    • Itens que precisariam ser construídos manualmente: configuração e estrutura da fila, gerenciamento de workers e polling, processamento de mensagens e rastreamento de estado, tratamento de erros e retries, coordenação manual de etapas
    • O código de exemplo inclui várias tabelas de estado como job_queue, job_results, job_state, workflow_steps, step_variables, scheduled_jobs, além de worker de polling, progresso do workflow, recuperação de falhas, coordenador de execução paralela, passagem de variáveis, agendamento e funções de limpeza
    • O cálculo de next_run no agendamento exige adicionalmente uma biblioteca externa de parser de cron

Como fica com pg_durable

  • A mesma agregação paralela + atualização de dashboard pode ser expressa com uma única chamada df.start(), usando o operador & para fan-out e ~> para join
    • Exemplo: 3 consultas se ramificam em paralelo e depois convergem na etapa refresh dashboard para gerar o resultado
    • No exemplo de execução ao vivo, após 3 etapas em paralelo e um join, o dashboard fica pronto de forma durável em apenas 1,9 segundo
  • Gerenciamento de filas, rastreamento de estado, recuperação de falhas, coordenação de etapas e retries são todos tratados pelo pg_durable

Principais recursos

  • Durável por padrão

    • Todas as etapas registram o estado como checkpoint no PostgreSQL, então o workflow sobrevive a falhas, reinicializações e perdas de conexão
    • Retoma exatamente do ponto em que parou
  • Retries automáticos

    • Lógica de retry embutida para tarefas instáveis; se uma etapa falhar, apenas ela é tentada novamente, enquanto o restante do workflow continua
    • Não há necessidade de código manual de tratamento de erros
  • Observabilidade completa em SQL

    • Todo o estado do workflow é armazenado em tabelas do Postgres, permitindo consultar histórico de execução, verificar saídas de etapas e depurar falhas com SQL padrão
    • Sem necessidade de dashboard externo
  • Execução paralela

    • Com o operador & ou df.join(), tarefas independentes podem fazer fan-out, executando agregações, chamadas de API e etapas de ETL simultaneamente com coordenação automática

Padrões que podem ser criados

  • Pipelines de ETL

    • Encadeia cleanup → transform → load com garantia de sequência; cada etapa espera a anterior e, em caso de falha, o pipeline é interrompido de forma limpa (~> sequence, |=> variables)
  • Agregação paralela

    • Conta usuários + soma receita + verifica inventário ao mesmo tempo, fazendo fan-out com várias consultas e aguardando a conclusão total (&, df.join())
  • Processamento de pedidos

    • Captura o ID do pedido e o repassa pelas etapas de validação, processamento e conclusão, com fluxo automático de variáveis entre etapas (|=> capture, $var substitution, df.sleep())
  • Jobs agendados

    • Faz polling de APIs, arquivamento de registros e sincronização de dados com cron; o loop roda permanentemente e sobrevive a reinicializações (@> loop, df.wait_for_schedule())
  • Desvios condicionais

    • Verifica tarefas pendentes, número de linhas ou flags para decidir entre processar ou pular, com a lógica de desvio no SQL, não na aplicação (df.if(), ?> conditional)
  • Validação em múltiplas etapas

    • Busca dados → valida esquema → verifica regras de negócio → aprova/rejeita, com checkpoints em cada etapa para não perder progresso em caso de falha
  • Manutenção de banco de dados

    • Detecta fatores que bloqueiam autovacuum, bloat de tabelas e risco de wraparound, expondo para revisão e depois corrigindo de forma durável mesmo após reinicializações (?> conditional, df.wait_for_signal(), @> loop)
  • Azure Functions & HTTP

    • Com df.http(), chama Azure Functions ou endpoints HTTPS permitidos diretamente do SQL para processar inline chunking de documentos, enriquecimento de linhas e classificação de registros
  • Aprovação com humano no loop

    • Aprova automaticamente tarefas rotineiras e pausa tarefas de alto risco até receber um sinal de aprovação humana (grandes faturas, operações destrutivas) (df.wait_for_signal(), df.if())

Suporte de autoria com IA

  • Ao descrever o workflow em inglês simples, o Copilot gera o SQL correto de função durável, sem necessidade de aprender a sintaxe
  • O repositório inclui a skill reutilizável de agente pg-durable-sql, que ensina ao GitHub Copilot e outros agentes como gerar corretamente SQL com operadores, substituição de variáveis, loops e joins paralelos

Disponível como código aberto

  • Disponível como código aberto completo, sem lista de espera nem lock-in; basta clonar o repositório, compilar e executar imediatamente no seu próprio PostgreSQL
  • É possível aplicar orquestração durável em notebooks, servidores ou nuvem

Opção gerenciada no Azure HorizonDB

  • O Azure HorizonDB é o novo serviço de PostgreSQL em nuvem da Microsoft, com pg_durable embutido, preservando as funções duráveis que você escreveu enquanto adiciona escala empresarial, segurança e IA
    • Até 3× mais desempenho, autoescalonamento de armazenamento até 128 TB e scale-out de computação de até 3.072 vCores
    • Microsoft Defender para detecção de ameaças em tempo real e Microsoft Entra ID para gerenciamento de identidade
    • Busca vetorial com Filtered DiskANN, ranking semântico e curadoria de modelos de IA dentro do banco de dados
    • Espelhamento quase em tempo real com Microsoft Fabric, integração com VS Code e conexão com GitHub Copilot
  • Pipeline de IA embutido

    • O HorizonDB adiciona um pipeline de IA gerenciado de ponta a ponta sobre a execução durável do pg_durable, com cada etapa tendo checkpoint, retry e segurança contra falhas
    • Fluxo das etapas: Ingest (carregamento de documentos/dados) → Chunk (divisão de conteúdo) → Embed (vetorização) → Index (armazenamento no DiskANN) → Serve (busca/ranking)

1 comentários

 
GN⁺ 4 시간 전
Comentários do Hacker News
  • 2026 parece que será o ano das filas em Postgres: há movimentos como DBOS[0] e pgQue[1], e é legal ver a comunidade criando esse tipo de opção
    Ainda assim, como ex-engenheiro de aplicações, prefiro que a lógica da fila fique no código e no Git. Talvez eu mude de ideia com as ferramentas certas
    [0]: https://www.dbos.dev/
    [1]: https://github.com/NikolayS/pgque

    • Pode ser que a forma de trabalhar seja mais difícil ou apenas diferente, mas parece haver pouca documentação, conteúdo pesquisável, experiência e ferramentas
      Fico me perguntando como fazem versionamento, depuração, testes e releases. Gosto da ideia de manter tudo em um só lugar por causa da localidade dos dados e da simplicidade da stack, mas dá a sensação de perder muito conhecimento útil sobre como fazer isso “do jeito certo”
    • Concordo com “quero manter a lógica da fila no código”. As ações que precisam ser executadas sobre os dados mudam muito mais frequentemente do que os próprios dados, então não faz sentido ter de fazer uma migração toda vez que se muda o comportamento
      Foi por isso também que eu realmente detestava o fato de, no Supabase, qualquer coisa minimamente complexa exigir a criação de funções no Postgres. Dito isso, em uma startup anterior nós mesmos construímos uma fila de tarefas simples sobre Postgres, e algo como pgQue provavelmente teria sido uma versão bem mais refinada disso
    • Sou fã desde a época do Postgres 7 e já tentei, de forma experimental, colocar o máximo possível dentro do PostgreSQL, mas pelo menos em experiência do desenvolvedor e observabilidade ainda falta bastante coisa
      Como até extensões multi-master não são algo para usar de imediato nem totalmente seguro, fico receoso de colocar tarefas complexas com muita escrita que acelerem a necessidade de expandir o banco de dados
    • Em projetos com triggers de banco de dados, também mantínhamos o código SQL no Git
      Em alguns casos, isso era empurrado para dentro das migrations do Django para que, na configuração local, os triggers fossem instalados no banco de dados local
  • Isso tem cheiro de stored procedures. São difíceis de testar unitariamente e de versionar, e a lógica de negócio fica escondida dentro do banco de dados, virando um “cérebro oculto”
    Também é difícil isolar workloads barulhentos, não há observabilidade, e toda a pressão de escalabilidade vai parar no Postgres. Falta entrada e saída, especialmente para coisas como chamadas de API. Para tarefas que rodam só dentro do banco local, tudo bem, mas parece ter uso limitado

    • Stored procedures também são excelentes quando usadas corretamente. O versionamento pode ser feito com IDs monotonicamente crescentes no final do nome; quando for preciso uma mudança incompatível, sobe-se o ID, e as versões antigas ficam lá até deixarem de ser usadas
      Claro, é preciso ter um procedimento adequado de upgrade do banco de dados. Se os membros da equipe executam migrations SQL arbitrárias como root, aí vocês vão sofrer
      Teste unitário também é tão possível quanto qualquer outro teste SQL; só é preciso subir o banco de dados. Se você não consegue testar stored procedures, isso significa que não tem como testar o próprio SQL, e esse é o problema real
      A alternativa às stored procedures não é não colocar lógica de negócio nenhuma no banco de dados, e sim muitas vezes acabar com SQL espalhado pela base de código, difícil de testar, ruim de versionar e encapsular, e desnecessariamente lento
      Sobre observabilidade, há certa razão nisso: investigar problemas de SQL costuma exigir mais esforço manual do que na maioria das linguagens de programação. Mas, se stored procedures estão causando problemas de entrada/saída e de escalabilidade, então estão sendo usadas de forma errada; quando usadas corretamente, muitas vezes reduzem bastante a entrada/saída e melhoram a escalabilidade
  • Se entendi direito, Absurd, criado pelos desenvolvedores do harness de LLM da Pi, parece ir na direção de reduzir ao máximo o acesso puro ao banco de dados. Ainda estou começando a olhar esse tema
    https://github.com/earendil-works/absurd

    • Só uma pequena correção: absurd parece ser o projeto original da earendil, iniciado antes de Mario Zechner se juntar à empresa, e eu nem o vi nos commits
      Claro, não conheço todos os detalhes, então estou genuinamente curioso
  • Em “quando não usar”, está escrito “quando o workflow estiver majoritariamente fora do Postgres e se estender por vários sistemas heterogêneos”; se for esse o caso, então não entendo como este projeto pode ser comparado a algo como Temporal
    Fico me perguntando se estou entendendo errado a limitação implícita nessa recomendação

    • Concordo. Olhando o exemplo https://github.com/microsoft/pg_durable/blob/main/examples/i..., não fica claro qual é o valor do projeto
      Pode até ser uma conquista tecnicamente interessante, mas ler SQL assim é bem estranho
      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'
      );
  • Na empresa estamos presos ao Azure e seguimos esperando o Azure PostgreSQL alcançar os recursos modernos
    Por exemplo, não dá para usar isto: https://www.paradedb.com/blog/hybrid-search-in-postgresql-th...
    Também não há suporte para vetores ultralargos de alta dimensionalidade. É bom ver o pg_durable sendo lançado como open source, mas seria bom começarem adotando ao menos os recursos básicos que no AWS já vêm como algo natural

    • O ParadeDB é AGPL, então em geral é difícil que grandes provedores de nuvem o ofereçam diretamente. Ainda assim, no Azure HorizonDB dá para usar https://github.com/timescale/pg_textsearch e há uma boa chance de isso chegar logo ao Flex
      Para ser transparente, sou mantenedor do pg_textsearch e agora estou na Azure. Não entendi exatamente o comentário sobre suporte a vetores; queria saber se você está procurando algo além de pgvector + diskann oferecidos pela Azure
    • Para busca vetorial, talvez o Azure Cosmos DB faça mais sentido
    • Você provavelmente já considerou isso, mas fico curioso sobre por que não simplesmente criar uma VM bare metal e instalar o Postgres mais recente
    • Sou PM da equipe do Azure PG responsável pelos recursos de IA do Postgres. Os recursos solicitados de fato já estão disponíveis, e houve bastante avanço nos últimos 3 a 6 meses
      No caso de busca híbrida (BM25 + vetor), o pg_search do ParadeDB também não é um recurso nativo da AWS; você precisa hospedá-lo por conta própria no EC2. No Azure PostgreSQL, tornamos nativo o pg_textsearch, que fornece o mesmo modelo de ranking BM25, e o principal contribuidor hoje está na equipe do Azure Postgres
      Documentação: https://learn.microsoft.com/en-us/azure/horizondb/ai/full-te...
      Em vetores de alta dimensionalidade, na verdade estamos à frente. O pgvector com HNSW tem limite de 2.000 dimensões, mas o Azure oferece pgvector para armazenamento e busca vetorial e, para workloads grandes e de alta dimensionalidade, fornece o pg_diskann, o índice vetorial baseado em grafos da Microsoft. Ele suporta até 16.000 dimensões e também oferece filtragem avançada dentro do índice, avaliando cláusulas WHERE durante a travessia do grafo, sem perder recall em condições seletivas
      pgvector: https://learn.microsoft.com/en-us/azure/horizondb/ai/vector-...
      Suporte do DiskANN para alta dimensionalidade: https://learn.microsoft.com/en-us/azure/horizondb/ai/vector-...
      Esses recursos estão disponíveis hoje no Azure PostgreSQL, especialmente no Azure HorizonDB Preview. Se houver um workload específico, dá para analisar com mais detalhes
  • Isso parece uma solução equivocada para um problema antigo que agendadores de DAG como o Apache Airflow já resolvem há muito tempo
    Acho estranho querer armazenar o fluxo de controle no banco de dados em vez de no código. Não estou tentando diminuir o projeto, só ainda não entendi bem

    • A Microsoft tem o framework Durable Task[1] para esse tipo de uso, que pode ser executado como um serviço independente self-hosted, como o Temporal, e também de forma serverless no Azure Functions. Se bem me lembro, ele é até mais antigo que Airflow e Temporal
      Este projeto parece voltado para casos de uso mais específicos de banco de dados. A vantagem provavelmente é poder acompanhar o estado exato das tarefas no próprio banco, sem precisar confrontar os logs do workflow com o codebase e seguir linha por linha. A carga e a latência também devem ser menores, e ainda há o efeito operacional de ter um componente a menos para subir
      [1] https://learn.microsoft.com/en-us/azure/durable-task/common/...
    • Ferramentas externas como o Airflow não têm visibilidade sobre a carga do banco de dados. Se o desenvolvedor jogar 200 workers simultâneos no banco, isso pode afetar outros workloads
      Em contraste, embora não pareça funcionar assim no momento, esta abordagem poderia se autoajustar com feedback de desempenho quase em tempo real, sem o custo de latência de ida e volta
  • Mesmo lendo a documentação e os exemplos, algumas coisas ainda não ficam claras. Fiquei curioso sobre como df.wait_for_schedule() funciona
    Se for chamado pela aplicação, ele é idempotente? Se eu executar duas vezes com os mesmos parâmetros, dispara o tick duas vezes? É algo para chamar manualmente uma vez no console de consultas, ou para rodar como parte de um script de migração? Também queria entender se timed_out no exemplo[0] é uma constante fixa retornada em caso de timeout. E também não fica claro de imediato como funciona o tratamento de erros ou exceções
    [0] https://github.com/microsoft/pg_durable/blob/main/examples/i...

    • Quando df.start() é chamado, ele cria uma função durável e ao mesmo tempo inicia sua execução. Essa chamada retorna um ID de instância que representa essa execução e que pode ser usado depois para referenciá-la
      Dentro dessa função durável, df.wait_for_signal() é chamado, e essa chamada só é executada exatamente uma vez dentro daquela instância da função, então duplicação não é possível. Se a própria chamada de df.start() der timeout e for executada de novo, pode haver duplicação, mas nesse caso uma instância de função diferente será criada
      Se ocorrer um erro não tratado durante a execução do SQL, a instância da função falha, e o estado registra exatamente o erro que aconteceu
  • Você poderia explicar por que usar isso em vez de uma ferramenta de orquestração fora do banco de dados? Mesmo lendo o README e os exemplos, ainda não entendi bem

    • Se você usa snapshot PITR do banco de dados, todas as tarefas duráveis até aquele ponto específico no tempo são restauradas junto
      Como não é preciso sincronizar backups com outros componentes que pertencem ao mesmo armazenamento de dados, isso é bom para pipelines de ETL ou trabalhos em forma de máquina de estados. Se o ETL for majoritariamente SQL, também ajuda o fato de o trabalho real ser executado no mesmo servidor
    • Do ponto de vista de contribuidores, os clientes de Postgres da Microsoft se dividem de forma bem equilibrada em dois grupos. Os que querem fazer o máximo possível dentro do banco de dados e os que preferem manter a aplicação e o processamento fora dele
    • Se, na arquitetura, o banco de dados for o único componente com estado, isso às vezes é conveniente
      Quando todo o estado está em um único banco de dados, também há mais chance de obter backups consistentes
    • Dá para integrar bem com workflows da aplicação. Por exemplo, é possível mostrar o progresso a partir de um link permanente no app frontend, criar workflows que continuam executando mesmo após reinicializações da aplicação, e fazer isso sem adicionar mais um componente de infraestrutura
      Em https://transport.data.gouv.fr, eles usam Postgres para esse tipo de coisa, e isso ajuda em um app Elixir que faz bastante processamento. Ainda não conheço bem o pg_durable, mas já usei ou implementei soluções parecidas, então faz sentido para mim
  • O banco de dados já não é uma das infraestruturas mais difíceis de escalar? Não entendo por que alguém iria querer colocar tarefas de longa duração nele também

    • Executar tarefas de longa duração no Postgres não é nenhuma novidade. Um exemplo é o pg_cron
      No fim das contas, esse tipo de workload será executado contra o banco de dados, independentemente de ser acionado ou não por um componente externo. Em pipelines de dados ou IA, também tem se tornado mais comum enviar consultas HTTP a partir do banco de dados para evitar ida e volta e pontos extras de falha causados por componentes adicionais. Ainda assim, trazer o processamento para perto dos dados ou levar os dados até o processamento é uma grande decisão de arquitetura e bastante debatida
  • Parece mais um caso de https://en.wikipedia.org/wiki/Inner-platform_effect que talvez nem fosse necessário se linguagens de programação ou máquinas virtuais populares já dessem suporte a determinismo, execução passo a passo mensurável e controlável, pausa do estado em runtime, serialização e desserialização, e retomada