- Oban.py é uma versão portada para Python, baseada em PostgreSQL, do framework de processamento de jobs Oban, do Elixir, permitindo inserir e processar jobs apenas com o banco de dados
- Os jobs são criados e revertidos dentro de transações do banco de dados, com suporte a vários recursos como gerenciamento de filas, armazenamento de resultados e agendamento via cron
- A versão open source tem limitações como execução asyncio em thread única e inserção/confirmação individual, mas a versão Pro oferece processamento paralelo, workflows e concorrência inteligente
- O funcionamento interno é composto por cinco etapas:
Insert → Notify → Fetch → Execute → Ack, usando FOR UPDATE SKIP LOCKED do PostgreSQL para evitar conflitos de concorrência
- Eleição de líder, recuperação de jobs órfãos e tentativas com backoff também são feitas com base no banco de dados, permitindo processamento distribuído estável sem broker externo
Visão geral do Oban.py
- Oban.py é um framework de processamento de jobs baseado em banco de dados portado do Oban, do Elixir, para Python
- Ele insere e processa jobs dentro de transações do banco de dados, e em caso de falha toda a transação é revertida
- Inclui vários recursos de controle, como limites de fila, armazenamento de jobs concluídos, retenção de resultados e agendamento via cron
- Há duas versões disponíveis
- Open source (OSS): execução asyncio em thread única, inserção/confirmação individual, recuperação simples
- Versão Pro: paralelismo com base em pool de processos, suporte a workflows, relay, jobs únicos e concorrência inteligente
- A OSS é adequada para projetos pessoais ou avaliação, enquanto a versão Pro é recomendada para ambientes de grande escala
Fluxo de processamento de jobs
- Após a inserção, o job é salvo na tabela
oban_jobs com state='available', e o NOTIFY do PostgreSQL envia notificações para cada nó
- O Stager de cada nó detecta a fila correspondente e desperta o Producer, que então busca e executa o job
- Na seleção de jobs, usa-se
FOR UPDATE SKIP LOCKED do SQL para permitir processamento paralelo sem execução duplicada
- Linhas já bloqueadas são ignoradas, permitindo que outro producer pegue imediatamente outros jobs
- Os jobs são despachados como async task e, ao concluir, passam por acknowledgement via callback
- A versão Pro usa um dispatcher com pool de processos em vez de asyncio, permitindo execução paralela em múltiplos núcleos
Processos em segundo plano
- Eleição de líder (Leader Election)
- O líder é definido com
INSERT ... ON CONFLICT do PostgreSQL e leases com TTL
- Sem protocolo de consenso separado, um único líder fica responsável por limpeza e recuperação de jobs
- Lifeline (recuperação de jobs órfãos)
- Se um job em execução durar mais do que um certo tempo (
rescue_after, padrão de 5 minutos), ele é restaurado para o estado available
- A versão Pro verifica se o producer ainda está vivo, mas a OSS julga isso apenas com base no tempo
- Pruner (limpeza de jobs)
- Remove jobs concluídos, cancelados ou descartados que já passaram de
max_age (padrão de 1 dia)
- Limita o escopo de exclusão com
LIMIT para evitar sobrecarga no banco de dados
Tentativas e backoff
- Quando um job gera uma exceção, o Executor decide se ele deve ser tentado novamente
- Se estiver abaixo do número máximo de tentativas (
max_attempts), ele é reenfileirado; se ultrapassar, é descartado
- O backoff padrão é um crescimento exponencial com jitter
- Isso reduz picos de carga (Thundering Herd) ao evitar novas tentativas simultâneas em falhas em massa
- Ex.: cerca de 17 segundos na 1ª tentativa, cerca de 47 segundos na 5ª e cerca de 17 minutos na 10ª
- A classe worker pode implementar lógica personalizada de backoff com o método
backoff()
Principais características e avaliação
- O PostgreSQL desempenha papel central
- Com
FOR UPDATE SKIP LOCKED, LISTEN/NOTIFY e ON CONFLICT, ele cuida de controle de concorrência, sinalização e eleição de líder
- Sem Redis ou broker externo, a camada de coordenação é formada com um único banco de dados
- Não é paralelo, mas suporta concorrência
- Por ser baseado em asyncio, é adequado para tarefas I/O-bound; para tarefas CPU-bound, recomenda-se a versão Pro
- Clareza na estrutura do código
- Com nomenclatura consistente e responsabilidades bem separadas, resulta em uma base de código fácil de ler
- Separação clara entre OSS e Pro
- A OSS é voltada a experimentação e pequeno porte; a Pro, a ambientes de grande escala e alto desempenho
- Conclusão: um port limpo e bem estruturado para Python que implementa uma fila de jobs completa apenas com PostgreSQL, adequado para usuários de Elixir ou desenvolvedores que querem um sistema de jobs sem infraestrutura externa
1 comentários
Comentários no Hacker News
Eu sou a pessoa que criou o Sidekiq, e quero parabenizar Shannon e Parker pelo lançamento
Já passei por esse mesmo dilema antes — focar no Ruby ou expandir o Sidekiq para outras linguagens. Percebi que não dá para ser especialista em todas as linguagens, então em vez disso criei o Faktory. A estrutura é de um servidor central que gerencia o ciclo de vida da fila, enquanto os clientes de cada linguagem permanecem simples. Por exemplo, existe um cliente como o faktory-rs. A desvantagem é não estar focado em uma comunidade de linguagem específica, então fica difícil oferecer exemplos adaptados àquela linguagem.
Pode ser que uma abordagem focada em uma comunidade gere resultados melhores. O tempo dirá
O ponto central do Oban é que você consegue inserir e processar jobs usando apenas o banco de dados. Dá para enfileirar um job de envio de e-mail dentro da transação de criação do usuário, e se ela falhar tudo é revertido junto.
Muita gente diz que não se deve usar banco relacional como fila de jobs, mas ignora a importância das transações. O texto Job Drain, do Brandur Leach, também explica bem esse conceito
Mas agora ninguém mais se lembra daquele incômodo. O “padrão de outbox transacional” é indispensável, e eu prefiro uma abordagem que receba as mesmas garantias ACID que os meus dados.
Mesmo sem conhecer o internals do banco, investir uma semana para aprender nível de isolamento e ordem de commit pode poupar um ano de depuração de sistemas distribuídos
Em uma época com tantos processos longos de IA, esse tipo de durabilidade é essencial. Em outros ecossistemas isso costuma ser cobrado à parte, mas no Oban já vem incluído
A equipe do Oban é conhecida por sua engenharia sofisticada no ecossistema Elixir. Mas foi confuso ver o pool de processos trancado na versão Pro.
Por exemplo, o plano de US$ 135/mês inclui execução multiprocessos, workflows, limites globais, jobs únicos, operações em lote, sources criptografadas, suporte dedicado etc.
O meu projeto Chancy é totalmente gratuito, e permite combinar livremente asyncio, processos, threads e subinterpreters.
Acho que seria melhor levar esses recursos para o OSS e deixar o pago mais focado em suporte enterprise. No ecossistema Python há muito mais concorrentes
O modelo de vender apenas suporte não funcionou muito bem, mas no Python pode ser diferente.
No ecossistema Python realmente tem de tudo
Se o Chancy ganhar suporte a Django Tasks, ou surgir um pacote
django-chancy, acho que a adoção seria rápidaO Oban OSS só oferece execução asyncio single-threaded, então jobs CPU-bound bloqueiam o event loop.
Por isso achei que não valia a pena tentar. A interface do Celery não é grande coisa, mas é familiar, e permite escalar vertical e horizontalmente sem limite.
Ainda assim, depois que descobri que é possível subir vários nós de worker, mudei um pouco de opinião
A divisão entre recursos OSS/Pro é aceitável, mas é uma pena ver algo como “a versão Pro rastreia a vida do producer com heartbeats mais inteligentes”.
Colocar recursos relacionados à confiabilidade atrás de pagamento dificulta a adoção em projetos OSS
A versão básica deveria ser a melhor possível, e os extras deveriam ser pagos. Parece que essa fronteira está em um lugar meio estranho
A frase citada é um pouco imprecisa — o rastreamento de vida do producer é o mesmo; a diferença está apenas na forma de recuperar jobs órfãos
Seria ótimo se workflows de BI/ML/DS em Python migrassem para Elixir.
Acho que o Elixir, com sua natureza funcional, tolerância a falhas e concorrência, é uma base muito mais natural para esse tipo de trabalho
Este vídeo e o guia Elixir Genius são boas referências
Aqui na empresa também usamos Celery, e não é tão bom assim. Temporal é pesado demais, enquanto o Oban parece leve e interessante.
Gostaria de ver uma comparação de alguém que já usou os dois
O Temporal é adequado para organizações que precisam das garantias de workflow e conseguem arcar com a complexidade, como bancos.
O Oban é uma fila baseada em banco de dados, cuja confiabilidade precisa ser reforçada por quem usa.
Eu acho bom ter os dois no sistema ao mesmo tempo
Estamos usando uma combinação de ProcessWorker simples com workers em ECS
Fico curioso se o Celery ficou menos estável recentemente, ou só mais difícil de lidar
Projeto interessante. Mas chama atenção o fato de alguns recursos centrais estarem disponíveis apenas na versão Pro.
Como projetos anteriores que implementaram workflows duráveis baseados em Postgres como OSS, há o DBOS e o Absurd.
É bom ver essa abordagem centrada no banco de dados ganhando espaço
Um modelo totalmente open source sustentado apenas pela venda de suporte é um modelo dos sonhos. Espero que um dia seja possível
Fico curioso se o Postgres tem desempenho suficiente para processar centenas de milhões de jobs. No passado, vi um grande ganho de performance ao migrar para Redis + Sidekiq
A versão OSS diz que jobs longos podem ser recuperados incorretamente mesmo quando o producer ainda está vivo.
Então ela serve apenas para jobs curtos?
O timing de recuperação só muda quando não foi possível esperar um encerramento normal