Executando Jobs em Segundo Plano e Tarefas Agendadas em um App Construído com IA
Resposta rápida
Nem tudo que o seu app faz pode acontecer enquanto um usuário está olhando para um spinner de carregamento. Enviar um lote de lembretes de faturas atrasadas, recalcular um ranking durante a madrugada, limpar sessões expiradas — nada disso pertence a uma requisição que deveria responder em menos de um segundo. Isso precisa do seu próprio agendamento, do seu próprio gatilho e do seu próprio tratamento de falhas, separado da página ou chamada de API que um usuário está esperando de verdade.
Por que empilhar tudo dentro de um request handler quebra primeiro
O atalho tentador é grudar o trabalho em segundo plano em qualquer requisição que esteja por perto. Um job de "enviar resumo semanal" é disparado de dentro do carregamento da página do dashboard, então o resumo só sai quando alguém visita o dashboard naquele dia. Uma tarefa de limpeza roda dentro do handler de cadastro porque foi ali que alguém lembrou de adicioná-la.
Já vi esse padrão causar dois tipos de falha. Ou a requisição estoura o tempo limite porque agora ela está gerando um relatório de cinco minutos antes de conseguir responder, ou o job simplesmente nunca roda porque a requisição "certa" não aconteceu naquele dia. Nenhum dos dois é um bug que você percebe em uma demonstração. Os dois aparecem duas semanas depois do lançamento, geralmente como um cliente perguntando por que o lembrete da fatura nunca chegou.
O sinal de que você precisa de um job de verdade
Se as palavras "toda noite", "toda hora" ou "eventualmente, não agora" aparecem quando você descreve um pedaço de trabalho, ele não pertence a um ciclo de request/response. Ele pertence a um agendamento ou a uma fila.
As três formas que o trabalho em segundo plano realmente assume
A maior parte do trabalho agendado ou assíncrono em um app de verdade se encaixa em uma de três categorias, e elas não são intercambiáveis.
Jobs agendados rodam no relógio, sem nenhum usuário envolvido — faturamento noturno, e-mails semanais de resumo, uma limpeza às 3h de tokens de redefinição de senha expirados. Eles são disparados pelo tempo, não por algo que um usuário fez.
Jobs em fila são disparados por um evento, mas não devem bloquear a resposta a esse evento. Um usuário envia um vídeo; a resposta do upload volta instantaneamente, e um processo separado cuida da transcodificação em segundo plano. O usuário recebe um estado de "processando" e depois uma notificação quando terminar.
Jobs com retentativa são o subconjunto de qualquer uma das categorias acima que pode falhar por motivos fora do seu controle — um provedor de e-mail está fora do ar, uma API de terceiros está limitando sua taxa de chamadas — e precisam de backoff e um limite de tentativas em vez de uma única falha silenciosa.
Misturar essas categorias é onde os times se queimam. Tratar um job em fila como se fosse agendado faz com que ele espere o próximo tique do relógio em vez de rodar agora. Tratar um job agendado como se ele não pudesse falhar significa que uma única noite ruim derruba todo o faturamento sem nenhum registro do que aconteceu.
Onde isso vive em um app construído com a Greta
Como a Greta monta um app Next.js com banco de dados via Prisma, o trabalho agendado e em segundo plano tem um lugar óbvio para morar em vez de precisar de infraestrutura separada montada depois. Um job agendado é uma rota de API do Next.js — digamos, app/api/cron/send-digest/route.ts — disparada em um temporizador pela configuração de cron da plataforma, não uma página que alguém visita. O trabalho em fila ganha uma tabela de jobs simples no mesmo banco de dados: uma linha por tarefa, uma coluna de status e uma rota leve que processa as linhas pendentes. Nenhum broker de mensagens separado para configurar em um projeto que está enviando alguns milhares de e-mails por semana.
Esse último ponto importa mais do que parece. Um sistema de fila dedicado, como um broker gerenciado, é a escolha certa quando você está processando dezenas de milhares de jobs por hora. Abaixo disso, uma coluna de status e uma rota que faz polling é mais simples de entender, mais fácil de depurar e um serviço a menos que pode cair de forma independente do seu app.
Uma comparação simples: feito à mão vs. gerado pela plataforma
| Aspecto | Feito à mão do zero | Gerado pela Greta |
|---|---|---|
| Infraestrutura de agendamento | Configurar um serviço de cron separado ou gatilho serverless | Configuração de cron ao lado do app, no mesmo deploy |
| Rastreamento de estado do job | Tabela e lógica de status personalizadas por projeto | O schema do Prisma já traz um padrão de tabela de jobs desde o início |
| Retentativa e backoff | Escrito job a job, muitas vezes ignorado sob prazo apertado | Padrão consistente aplicado em todos os tipos de job |
| Visibilidade de falhas | Logs espalhados entre serviços | Um único banco de dados, um único lugar para consultar "o que falhou ontem à noite" |
| Escalar depois | Migrar para um sistema de fila no meio do projeto | Mesmas rotas Next.js; troca a tabela de jobs por um broker quando o volume exigir |
Como isso funciona de ponta a ponta
Digamos que você esteja rodando um app de assinatura e precise rebaixar qualquer conta cujo período de teste expirou sem cartão cadastrado. Isso é um job agendado: uma vez por dia, consulta os testes expirados, muda o status deles, envia um e-mail de aviso. Isso não tem nada a ver com a requisição de nenhum usuário específico — precisa rodar quer alguém entre no app naquele dia ou não.
Agora digamos que um usuário exporte um relatório grande desse mesmo app. A exportação não deveria travar a página por trinta segundos enquanto é gerada. A requisição cria uma linha de job e responde imediatamente com "avisaremos por e-mail quando estiver pronto", e um processo separado pega essa linha, monta o arquivo e atualiza o status. Se a geração do relatório falhar porque uma fonte de dados de terceiros deu timeout, o job tenta de novo duas vezes com um pequeno atraso antes de desistir e se marcar para um humano dar uma olhada.
Nenhum dos dois é complicado isoladamente. O que pega os times de surpresa é pular a lógica de retentativa por achar que as coisas "geralmente simplesmente funcionam", e descobrir o contrário na primeira vez que um provedor de e-mail tem cinco minutos ruins.
Perguntas frequentes
Eu preciso de uma fila de mensagens como Redis ou SQS logo no primeiro dia? Quase nunca. Uma tabela no banco de dados com uma coluna de status aguenta um volume surpreendente. A maioria dos apps não ultrapassa isso antes de encontrar product-market fit. Adicione uma fila dedicada quando você estiver processando jobs suficientes por minuto para o polling da tabela virar o gargalo, não antes disso.
Com que frequência um job agendado pode rodar de verdade? Depende da granularidade de cron da plataforma, mas gatilhos por minuto são comuns em cron serverless. Para qualquer coisa mais frequente do que isso, geralmente você está olhando para um processo worker persistente em vez de uma função agendada.
O que acontece se um job agendado se sobrepor a ele mesmo — digamos, ainda estiver rodando quando o próximo gatilho disparar? Isso é um modo de falha real, não hipotético. A correção é um lock: verificar se a última execução terminou antes de começar uma nova, ou usar uma flag de status no próprio registro do job.
Posso disparar um job em segundo plano a partir de um webhook em vez de um agendamento? Sim — esse é o padrão de job em fila. Um webhook chega, você grava uma linha de job em vez de processar direto, e uma rota ou worker separado cuida dela um instante depois. Veja conectando webhooks e APIs de terceiros para o lado de entrada disso.
Encerramento
Jobs em segundo plano são uma daquelas coisas que não aparecem em uma demonstração — ninguém aplaude um cron job que rodou quietinho às 3h — mas fazem a diferença entre um app que funciona quando alguém está olhando e um que realmente roda um negócio sem supervisão. Acerte o agendamento, as retentativas e a visibilidade de falhas, e a maior parte do que parecia "trabalho de infraestrutura" acaba sendo uma tabela de banco de dados e uma rota.
