River: Uma fila de jobs rápida e robusta para Go e Postgres
Uma nova biblioteca Go chamada River propõe usar PostgreSQL como uma fila transacional de jobs, vinculando a criação dos jobs às mesmas transações de banco de dados que alteram os dados da aplicação. Defensores argumentam que essa abordagem de “uma única dependência” simplifica a arquitetura, melhora a correção por meio de enfileiramento atômico e escala bem o suficiente para a maioria das cargas, citando sistemas semelhantes como Oban como prova. Críticos respondem que bancos relacionais são filas ruins em grande escala e preferem serviços dedicados ou sistemas de tarefas via HTTP, levantando preocupações sobre desempenho, jobs de longa duração e os trade-offs em relação a ferramentas como Redis, Kafka, Temporal ou filas de tarefas em nuvem.
Design: Fila de Jobs Transacional com Postgres
- Ideia central: jobs são enfileirados dentro da mesma transação de banco de dados que os dados de negócio (“transactional outbox” style).
- Isso garante que a criação do job seja atômica em relação às mudanças de domínio: ou ambos são confirmados, ou nenhum dos dois.
- Os jobs são executados por processos separados; a transação serve apenas para enfileirar, não para execução.
- Há suporte para jobs agendados (adiados) via a opção
ScheduledAt, embora a documentação ainda esteja sendo desenvolvida.
RDBMS como Fila: Prós e Contras
- Defensores:
- Fortes garantias de correção, modelo mental simples e menos partes móveis se o Postgres já estiver em uso.
- Throughput adequado para a maioria dos sistemas; exemplos de outros ecossistemas mostram dezenas de milhares de jobs/s.
- Operação mais fácil do que adicionar Redis/RabbitMQ/Kafka para muitos apps pequenos/médios.
- Céticos:
- “Bancos de dados são filas ruins” continua sendo uma crença comum, citando escalabilidade, bloat e preocupações com jobs de longa duração.
- Alguns argumentam que “nunca” usariam um RDBMS como fila de jobs com base em experiências anteriores.
Detalhes de Implementação e Padrões
- Padrão típico:
SELECT … FOR UPDATE SKIP LOCKED(ou variações) para reservar jobs com segurança entre workers. - As sugestões incluem:
- Usar
FOR NO KEY UPDATEpara evitar bloquear inserts de chave estrangeira. - Particionar a tabela de jobs, sequenciamento por tenant e processamento em lote via leases para melhor throughput.
- Usar
NOTIFYdo Postgres pode ser usado para acordar workers, mas há preocupação com overhead e incompatibilidade com poolers como PgBouncer.- Alguns recursos estão em desenvolvimento ou foram solicitados: dashboard de UI, notificações de conclusão de jobs, suporte mais rico a workflows.
Comparações com Outros Sistemas
- Filas de jobs em Postgres relacionadas em outras linguagens (por exemplo, bibliotecas conhecidas de Elixir e JS) são citadas como prior art e evidência de que o modelo funciona.
- Alternativas discutidas:
- Filas baseadas em HTTP (GCP Tasks, sistemas no estilo SQS) elogiadas pela simplicidade, mas criticadas por problemas de correção transacional e limites de timeout.
- Abordagens baseadas em Kafka ou NATS, engines de workflow no estilo Temporal e filas puramente SQL (por exemplo, PGMQ) mencionadas como pontos diferentes de trade-off.
Direção do Projeto, Licenciamento e Atribuição
- Alguns se perguntam sobre metas comerciais versus puramente open source e se filas Go semelhantes deveriam “se unir”.
- A licença LGPLv3 para uma biblioteca Go é questionada devido ao linking estático; os mantenedores reconhecem a necessidade de esclarecer ou ajustar isso.
- Há debate sobre o quanto o design foi inspirado por sistemas de jobs baseados em Postgres já existentes e pedidos por uma atribuição mais clara.