Home > Blog > Desenvolvimento Web
Desenvolvimento Web
JavaScript
Programação

Graceful Shutdown de workers no Node.js

Atualizado em: 26 de outubro de 2026

Rack de servidores processando fluxos de dados no Node.js

Graceful Worker Shutdown no Node.js encerra um processo sem iniciar novos jobs, permitindo que tarefas ativas terminem ou retornem a um estado recuperável. O fluxo é essencial em deploys, autoscaling, reinícios, manutenção e interrupções do sistema operacional.

Finalizar o processo imediatamente pode deixar transações abertas, arquivos incompletos e mensagens marcadas como em processamento. Esperar indefinidamente também é perigoso. O worker precisa de uma janela controlada e um caminho de saída forçada.

Sinais do processo

process.once('SIGTERM', () => shutdown('SIGTERM'));
process.once('SIGINT', () => shutdown('SIGINT'));

SIGTERM é comum em orquestradores. SIGINT ocorre no terminal. Registre handlers uma vez para não iniciar vários shutdowns.

Estado de encerramento

let shuttingDown = false;

async function shutdown(signal) {
  if (shuttingDown) return;
  shuttingDown = true;

  logger.info({ signal }, 'iniciando shutdown do worker');
  // etapas de encerramento
}

Todas as partes do processo podem consultar esse estado para rejeitar novos trabalhos.

Ordem recomendada

  1. marque o processo como não pronto;
  2. pare de buscar novos jobs;
  3. pare schedulers e timers;
  4. aguarde tarefas ativas;
  5. cancele operações que aceitam AbortSignal;
  6. faça flush de logs e métricas;
  7. feche fila, banco e Redis;
  8. encerre com código apropriado.

Parando a entrada

Com BullMQ:

await worker.close();

O método para de adquirir novos jobs e aguarda os ativos conforme o comportamento da biblioteca. Consulte BullMQ no Node.js.

Worker próprio

while (!shuttingDown) {
  const job = await reserveNextJob({ signal: shutdownController.signal });
  if (!job) continue;
  await runTracked(job);
}

A operação de reserva precisa ser cancelável ou possuir timeout curto para perceber o shutdown rapidamente.

Rastreando jobs ativos

const activeJobs = new Set();

async function runTracked(job) {
  const promise = processJob(job)
    .finally(() => activeJobs.delete(promise));

  activeJobs.add(promise);
  return promise;
}

Durante shutdown:

await Promise.allSettled([...activeJobs]);

Essa espera precisa de deadline.

Deadline global

async function withShutdownDeadline(task, timeoutMs) {
  return Promise.race([
    task,
    new Promise((_, reject) =>
      setTimeout(() => reject(new Error('shutdown timeout')), timeoutMs)
    )
  ]);
}

Defina o timeout menor que a janela oferecida pelo orquestrador, reservando tempo para fechar conexões.

AbortController

const shutdownController = new AbortController();

async function shutdown() {
  shuttingDown = true;
  stopReceivingJobs();
  shutdownController.abort(new Error('worker shutting down'));
}

Propague o sinal para fetch, timers, streams e bibliotecas compatíveis.

await fetch(url, {
  signal: AbortSignal.any([
    shutdownController.signal,
    AbortSignal.timeout(15_000)
  ])
});

Não aborte automaticamente uma operação crítica se isso deixa estado inconsistente. Algumas tarefas devem terminar a etapa atômica antes de respeitar cancelamento.

Transações

Uma transação curta pode concluir. Uma tarefa longa deve dividir trabalho em checkpoints. Se o processo cair, outro worker retoma do último estado confirmado.

Lease e heartbeat

Sistemas próprios podem marcar jobs como processing com um lease.

UPDATE jobs
SET lease_until = now() + interval '60 seconds',
    worker_id = $2
WHERE id = $1;

O worker renova enquanto trabalha. No shutdown, para de renovar depois de concluir ou liberar o job. Se morrer, outro worker recupera quando o lease expira.

Idempotência

Shutdown pode ocorrer depois do efeito externo e antes de marcar conclusão. O retry precisa ser seguro.

Consulte Job Deduplication no Node.js e Consumer Idempotente no Node.js.

Jobs não canceláveis

CPU síncrona longa bloqueia handlers de sinal até retornar. Mova trabalho pesado para Worker Threads ou subprocessos, com protocolo de término e timeout.

Child processes

child.kill('SIGTERM');

const timer = setTimeout(() => {
  child.kill('SIGKILL');
}, 10_000);

child.once('exit', () => clearTimeout(timer));

Confirme implicações em diferentes sistemas operacionais e grupos de processos.

Streams e arquivos

Use pipeline() e aguarde finalização. Arquivos temporários devem ser renomeados atomicamente somente depois da escrita completa. Remova parciais durante recuperação.

Timers

Cancele intervalos e timeouts que criariam novos jobs.

clearInterval(reconciliationTimer);
clearTimeout(scheduleTimer);

Não use process.exit() enquanto callbacks e flushes ainda são necessários; ele encerra o event loop imediatamente.

Banco e Redis

Feche recursos depois que jobs ativos terminaram.

await queue.close();
await redis.quit();
await pool.end();

Se fechar o pool antes, jobs em andamento falham artificialmente.

Logs e telemetria

Faça flush com timeout. Registre sinal, jobs ativos, tempo de shutdown, jobs concluídos, abortados e forçados.

Readiness no Kubernetes

Ao iniciar shutdown, o pod deve ficar não pronto para sair do balanceamento e parar de receber comandos HTTP auxiliares.

let ready = true;

app.get('/ready', (req, res) => {
  res.sendStatus(ready ? 200 : 503);
});

No shutdown, defina ready = false antes de fechar serviços. Consulte Kubernetes Probes no Node.js.

terminationGracePeriodSeconds

A janela do Kubernetes precisa cobrir:

  • propagação de readiness;
  • jobs ativos;
  • flush;
  • fechamento de conexões;
  • margem de segurança.

Jobs maiores que a janela devem ser checkpointáveis ou executados em ambiente adequado para tarefas longas.

preStop

Um hook pode sinalizar drenagem antes do SIGTERM, mas não dependa exclusivamente dele. O processo ainda precisa tratar sinais corretamente.

Jobs agendados

Schedulers devem parar primeiro. Um job criado durante shutdown pode ficar sem worker. Consulte Delayed Jobs no Node.js.

Exit code

Use código zero quando o shutdown planejado concluiu. Use código diferente para falha fatal ou deadline excedida. Supervisores podem usar o código para diagnóstico, mas normalmente reiniciam o processo conforme política.

Erros fatais

Após uncaughtException ou estado corrompido, não continue processando novos jobs. Inicie drenagem limitada e encerre. Não tente transformar todo erro desconhecido em operação normal.

Teste do shutdown

  1. inicie job controlado;
  2. envie SIGTERM;
  3. confirme que novo job não inicia;
  4. confirme que o ativo conclui ou volta à fila;
  5. verifique fechamento de conexões;
  6. confirme exit code;
  7. repita com job travado até deadline.

Observabilidade

Monitore:

  • quantidade de shutdowns;
  • duração;
  • jobs ativos no início;
  • jobs concluídos;
  • jobs recuperados depois;
  • timeouts;
  • encerramentos forçados;
  • leases expirados.

Erros comuns

  • chamar process.exit() imediatamente;
  • continuar buscando jobs;
  • fechar banco antes dos processors;
  • não ter deadline;
  • abortar transação no ponto errado;
  • job sem checkpoint;
  • não marcar readiness;
  • ignorar subprocessos;
  • não testar SIGTERM.

Checklist

  • handlers once;
  • estado de draining;
  • entrada interrompida;
  • timers cancelados;
  • jobs rastreados;
  • deadline global;
  • AbortSignal propagado;
  • idempotência e leases;
  • recursos fechados na ordem;
  • teste real de sinais.

Conclusão

Graceful Worker Shutdown no Node.js é um protocolo de drenagem. O processo para de aceitar trabalho, conclui o que é seguro e deixa o restante recuperável.

Use deadline, idempotência, checkpoints e readiness. Com fechamento ordenado de fila, banco e telemetria, deploys deixam de causar jobs duplicados ou abandonados.

Consulte a documentação do process no Node.js e a documentação do ciclo de vida de Pods no Kubernetes.

10 melhores cursos de programação em 2026

Descubra os melhores cursos de programação. Aprenda a escolher o curso ideal para iniciar ou avançar na carreira de desenvolvedor

POSTS RELACIONADOS

Ver todos

Seta para a direita