Tarefas lentas no ciclo requisição-resposta
Imagine que, ao se cadastrar, você quer enviar a um usuário um email de boas-vindas. Enviar o email leva 2 segundos. Se você fizer isso dentro do handler, o usuário espera esses 2 segundos para a página carregar. Pior ainda: processar um vídeo ou gerar um relatório pode levar minutos.
A regra: tire as tarefas lentas do ciclo requisição-resposta. Responda rápido ao usuário e faça o trabalho pesado em segundo plano.
Filas de mensagens
Uma fila de mensagens desacopla quem pede o trabalho de quem o faz:
- O produtor (seu handler) enfileira um trabalho e responde na hora.
- O consumidor (um processo à parte, o worker) tira trabalhos da fila e os processa no seu próprio ritmo.
- Um broker (intermediário) guarda a fila: RabbitMQ, ou Redis com BullMQ no mundo Node.
// Produtor: enfileira e responde já
await fila.add("enviarEmail", { para: "ana@mail.com" });
res.status(202).json({ estado: "em andamento" });
// Consumidor (worker): processa em segundo plano
worker.process("enviarEmail", async (trabalho) => {
await enviarEmail(trabalho.dados.para);
});
Retentativas
Se um trabalho falha (a API de email não responde), a fila pode repetir automaticamente algumas vezes antes de mandá-lo para uma dead-letter queue para revisão manual. Isso dá resiliência diante de falhas transitórias.
Idempotência
Se um trabalho é repetido, ele pode executar mais de uma vez. Uma operação é idempotente quando executá-la N vezes produz o mesmo efeito que executá-la uma vez. "Fixar o saldo em 100€" é idempotente; "somar 100€" não é (seria duplicado a cada retentativa).
A técnica habitual: dar a cada trabalho um id único e registrar os já processados, de modo que uma retentativa do mesmo id não aplique de novo o efeito.