Executando verificação de segurança...
21

Sidequest.js: uma alternativa simples e robusta para jobs distribuídos no Node.js

Olá pessoal, tudo beleza?

Há alguns anos, criei uma lib para trabalhar com agendamento de tarefas no Node.js. O node-cron até que é uma lib relativamente utilizada pela comunidade, com mais de 5 milhões de downloads por semana. A ideia na época era criar algo simples, sem dependências externas, para evitar o uso direto de setInterval.

Porém, com o passar dos anos, percebi que a lib começou a ser usada de formas que não eram o objetivo inicial. Basicamente, começaram a adicioná-la em web apps, como APIs com Express, e a executar jobs recorrentes no mesmo processo do Express.

Com isso, identifiquei dois grandes problemas:

  1. Tasks recorrentes com blocking I/O fazem a API com Express parar de responder, se tudo estiver rodando no mesmo processo.
  2. Se o app for deployado em vários nós, existirão N tasks recorrentes sendo executadas simultaneamente. Ou seja, os jobs são executados em duplicidade.

Pensando nisso, e inspirado pelo Oban (Elixir) e Sidekiq (Rails), criei o Sidequest.js.

A ideia é ter algo simples de usar, que possa aproveitar o que o app já tem, como PostgreSQL, MySQL ou MongoDB, e que resolva esses problemas de jobs distribuídos. Até existe uma solução similar, o BullMQ, porém ela roda apenas com Redis.

A utilização é basicamente:
1 - Criar um Job:

import { Job } from "sidequest";

export class EmailJob extends Job {
  async run(to, subject, body) {
    console.log(`Sending email to ${to}: ${subject}`);
    // Your email sending logic here
    return { sent: true, timestamp: new Date() };
  }
}

2 - Iniciar o Sidequest.js junto com a aplicação:

await Sidequest.start({
  backend: {
    driver: "@sidequest/postgres-backend",
    config: "postgresql://postgres:postgres@localhost:5432/my_app_dev",
  }
});

Neste exemplo, está usando o PostgreSQL como backend.
3 - Fazer o enqueue do job:

await Sidequest.build(EmailJob).enqueue("[email protected]", "Welcome!", "Thanks for signing up!");

Isso pode ser feito de qualquer lugar, como em um handler de rota no Express.

Pontos importantes: o Sidequest roda em um fork process e todos os jobs são executados em worker threads. Ou seja, ele roda totalmente isolado do processo principal.
Além disso, implementa algumas features interessantes, como jobs únicos, retry com backoff exponencial, snooze, entre outros.

Ah, e também há um dashboard que pode ser usado para acompanhar os jobs:
https://sidequestjs.com/screenshot.png

O Sidequest.js acabou de ser liberado. Seria muito massa se vocês puderem experimentar, testar e me contar o que acharam. Qualquer feedback, sugestão ou bug encontrado é super bem-vindo. Espero que seja útil no dia a dia de quem trabalha com Node.js e precisa de uma solução simples e robusta para tarefas em background.

Carregando publicação patrocinada...
2
1
1

Opa! Tudo certo?

Então, acho que não consegui entender muito bem o que você quis dizer com “limitações em comparação com o BullMQ”.
A pergunta, do jeito que veio, fica um pouco difícil de responder porque parte da ideia de que o Sidequest é um substituto direto do BullMQ, e esse não é o objetivo dele.

A proposta do Sidequest.js é não precisar adicionar outro serviço (como Redis) para lidar com filas/jobs, usando o próprio banco que a aplicação já tem.

• BullMQ foca em latência e throughput muito altos, além de features específicas do Redis.

• Sidequest prioriza simplicidade operacional e consistência transacional. Ele usa seu próprio banco, sem infra extra, oferece auditabilidade via SQL, e os jobs são duráveis (persistem no banco e podem ser mantidos indefinidamente, se quiser).

No fim das contas:
As limitações do Sidequest são basicamente as limitações do seu banco.
As limitações do BullMQ são as limitações do Redis.

1
1

boa noite, sr

muito interessante. eu li a documentação e achei muito massa, pois é uma alternativa essencial inclusive para situações em que a RAM é limitada, assim como processamento está limitado, como num cenário crítico de uma VPS de 1 núcleo, 1GB de RAM. ou até mesmo em uma tv box armbian portando H3 quad core @ 1.2GHz, 2GB RAM e 8GB de armazenamento NAND, fraquinha. eu não teria usado nestjs com bullmq e redis para isso, eu teria usado essa lib. comentei sobre isso em uma postagem sobre sqlite vs postgres.
em cenários críticos, minimalistas, faz todo sentido; combinado com sqlite, fica ideal, mesmo.

1

Só pra salientar que SQLite não é muito bom com concorrência. Se for em um caso como esse que você comentou, onde vai ter apenas um nó consumindo dados, maravilha, vai funcionar muito bem mesmo :D

SQLite é o nosso DB de teste btw. Essa facilidade é uma maravilha mesmo.

1

Legal esse caso de uso, não tinha pensado por esse lado! Mas de certa forma ele se conecta com um dos princípios que motivaram o projeto. Quando começamos um side project ou algo pessoal, geralmente temos só um DB e uma instância rodando o app. A ideia era criar algo que não exigisse adicionar nada novo à stack, como Redis, por exemplo. Isso ajuda a reduzir custo e complexidade, sem abrir mão de funcionalidades essenciais.

De fato, usar SQLite em ambientes limitados é uma alternativa interessante, parece ter sido um acerto. Mas, em cenários extremamente restritos, a gente não chegou a rodar benchmarks. Então eu recomendaria configurar o Sidequest com baixa concorrência nas filas e na concorrência global, só por precaução.