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 UPDATE para evitar bloquear inserts de chave estrangeira.
    • Particionar a tabela de jobs, sequenciamento por tenant e processamento em lote via leases para melhor throughput.
  • NOTIFY do 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.