>_ DevTrendspt

Idioma

Início

Linguagens

Seções

Frontend Backend Mobile DevOps AI / ML GameDev Blockchain Embarcados Segurança
Rust

Como Fazer o PostgreSQL Concluir Tarefas em Segundo Plano Mesmo Após Reinicialização

Imagine este cenário: você está escrevendo um fluxo de trabalho complexo de processamento de dados. Você precisa buscar registros, enviá-los para uma API externa, atualizar o status no banco de dados e, em seguida, iniciar a geração de relatórios. Se o banco de dados "travar" ou o servidor reinicializar no meio do processo, um procedimento PL/pgSQL padrão simplesmente abortará. O resultado são dados "pendentes" e uma dor de cabeça: como você descobre em qual estágio tudo parou e como reinicia tudo com segurança?

Normalmente, resolvemos isso com gambiarras. Configuramos pg_cron, criamos tabelas com filas de jobs, escrevemos workers em Python ou Go que consultam constantemente o banco de dados. No pior dos casos, arrastamos monstros pesados como Temporal ou Airflow para o projeto. Mas engenheiros da Microsoft decidiram que toda essa complexidade era desnecessária e lançaram a extensão pg_durable.

Por que os desenvolvedores precisam disso

A ideia principal por trás do projeto é dar ao PostgreSQL a capacidade de executar funções de longa duração que são resilientes a falhas. Na terminologia dos autores, isso é chamado de "Execução Durável".

pg_durable logo

Se o seu fluxo de trabalho é definido através do pg_durable, o banco de dados salvará seu estado após cada etapa. Se o servidor reinicializar bem no meio da execução de uma consulta pesada ou chamada de API, a extensão retomará a tarefa a partir do último checkpoint bem-sucedido. Você não precisa mais colar junto cron jobs, workers e tabelas de status.

Como funciona nos bastidores

O projeto é escrito em Rust usando pgrx. Arquiteturalmente, não é apenas um wrapper, mas um ambiente de execução completo dentro do banco de dados. Ele consiste em várias camadas:

  1. SQL DSL: um conjunto de operadores para descrever o grafo de tarefas.
  2. Background Worker: um processo em segundo plano dentro do Postgres que gerencia a execução.
  3. Duroxide: um motor de orquestração (também um desenvolvimento da Microsoft) que gerencia replay determinístico e checkpoints.

Curiosamente, os autores escolheram uma abordagem "SQL-nativa". Você descreve a lógica diretamente no console ou na migração, usando operadores especiais como ~> ou |=>.

Workflow graph example

Recursos principais

Aqui estão três coisas que realmente simplificam sua vida.

Tolerância a falhas sem serviços externos

Você não precisa do Redis para filas ou instâncias separadas do Temporal. Tudo vive diretamente nas tabelas df.* e duroxide.*. Dados e lógica de controle estão no mesmo ambiente transacional. Isso elimina o problema clássico de sistemas distribuídos onde uma tarefa existe na fila, mas as alterações no banco de dados ainda não foram confirmadas.

Execução paralela e mesclagem

Usando operadores, você pode facilmente "bifurcar" a execução de tarefas em vários fluxos paralelos e então esperar que eles sejam concluídos. O README tem um exemplo claro: conte usuários, pedidos e receita simultaneamente e, em seguida, consolide tudo em uma única etapa de relatório.

Integração com sistemas externos

A extensão tem uma função df.http(). Isso significa que você pode chamar microsserviços externos ou APIs de redes neurais diretamente de um processo de longa duração. Se a API retornar um erro 500, o pg_durable pode esperar e tentar novamente sem bloquear toda a operação do banco de dados.

Exemplo de código

Aqui está a aparência da criação de uma tarefa simples diretamente em SQL:

-- Запускаем процесс: берем 100 необработанных документов и обновляем их статус
SELECT df.start(
    'SELECT id FROM documents WHERE processed = false LIMIT 100' |=> 'batch'
    ~> 'UPDATE documents SET processed = true WHERE id = ANY($batch)'
);

Este código criará uma instância de Workflow que tem garantia de execução. Se o UPDATE falhar, o sistema saberá exatamente qual batch de dados ele estava tentando processar.

Onde isso é útil

Vejo vários cenários onde o pg_durable economizará muito do seu tempo.

Primeiro, pipelines de IA. Se você precisa processar milhares de linhas através de embeddings e salvá-las em pgvector, esta é a ferramenta ideal. O fracionamento de texto, chamadas de API da OpenAI e upserts no banco de dados são empacotados em um pipeline confiável.

Segundo, processamento de dados em grande escala (ETL). Em vez de escrever procedimentos PL/pgSQL monstruosos que caem quando o WAL acaba, você pode dividir o trabalho em pequenas etapas com checkpoints.

Terceiro, automação de administração. Por exemplo, verificar inchaço de tabelas, enviar uma notificação e esperar aprovação — tudo isso pode ser descrito como uma Função Durável.

Nuances e limitações

O projeto está em status de Preview. Isso significa que ainda é cedo demais para colocá-lo em produção, mas é perfeito para ferramentas internas.

Limitações importantes: você precisa do PostgreSQL 17 ou 18. Se estiver em versões mais antigas, precisará atualizar. Outro ponto é a segurança. Por padrão, todas as funções em shared_preload_libraries exigem direitos de superusuário para configuração, embora os desenvolvedores tenham fornecido um sistema de permissões via df.grant_usage() para funções regulares.

O sistema é otimizado para SQL. Se você precisa de lógica de negócios complexa com loops complicados em Python ou Node.js, é melhor usar o motor Duroxide diretamente do código da sua aplicação. O pg_durable é especificamente sobre manter os cálculos o mais próximo possível dos dados.

A Microsoft está investindo ativamente no Postgres (lembre-se da aquisição do Citus), e o pg_durable é mais um passo para transformar o banco de dados em uma plataforma de aplicação completa. Se você está cansado das suas tarefas em segundo plano "desaparecendo" nos momentos mais inconvenientes, ou se está farto de configurar orquestradores externos para pipelines simples, definitivamente vale a pena conferir este repositório. Para começar, um simples container Docker ou Codespaces será suficiente, que já estão configurados na aba Development no repositório.

Projetos relacionados