Um worker pode receber SIGTERM enquanto ainda tem uma tarefa na memória. Se ele tratar o sinal como um simples process.exit(), pode perder um checkpoint, confirmar uma mensagem cedo demais ou deixar uma escrita externa em estado incerto.
No Cloud Run, a resposta segura é um handoff controlado. Pare de aceitar novas tarefas, deixe a unidade atual chegar a um ponto seguro, grave o progresso em armazenamento durável e permita que outra instância continue. A comparação entre Service, Job e worker pool no Cloud Run ajuda a escolher o recurso antes de implementar esse ciclo.
Contrato de shutdown
- SIGTERM muda o estado do processo para “parando”.
- O loop deixa de buscar trabalho novo.
- A tarefa ativa grava seu checkpoint fora do container.
- A confirmação da fila acontece depois do estado durável.
O que o Cloud Run garante quando envia SIGTERM?
Em 2026, a documentação do Google Cloud diz que o Cloud Run envia SIGTERM antes de SIGKILL durante o encerramento de uma instância e descreve uma janela de 10 segundos para a saída (Google Cloud, “Container runtime contract”). Trate essa janela como limite operacional. Ela não transforma uma escrita incompleta em uma transação.
Instâncias do Cloud Run são descartáveis. O sistema de arquivos gravável do container fica em uma camada em memória e não é um lugar para manter o checkpoint que a próxima instância precisa. O progresso deve ir para um banco, uma fila, um objeto no Cloud Storage ou outro armazenamento que faça parte do contrato da aplicação (Google Cloud, “What is Cloud Run”).
No Node.js, um listener de sinal recebe o nome do sinal como argumento. Ao instalar um listener para SIGTERM, você assume a responsabilidade de iniciar o encerramento e também remove o comportamento padrão que terminaria o processo automaticamente (Node.js, “Process”). Por isso, o listener deve apenas mudar o estado e interromper a espera por trabalho novo. O loop principal faz o restante.
Regra de operação: use o SIGTERM para parar a entrada, não para inventar uma confirmação. O estado “processando” precisa continuar recuperável se o processo for interrompido antes do checkpoint.
Como transformar SIGTERM em um handoff seguro?
Em 2026, a documentação de encerramento do Cloud Run recomenda um handler de SIGTERM e cita tarefas de limpeza, como descarregar logs, durante a janela de shutdown (Google Cloud, “Select an execution environment for services”). Para um worker, a limpeza precisa incluir o limite de entrada e a persistência do trabalho que já foi aceito.
O ciclo tem quatro partes:
- Pare a entrada. Marque o processo como
stoppinge cancele apenas o polling ou a espera bloqueante da fila. Não faça novos claims depois do sinal. - Drene a unidade ativa. Aguarde a tarefa atual chegar a um ponto que possa ser repetido com segurança. Se ela não puder terminar dentro do limite, deixe-a sem confirmação para que a fila ou o scheduler tente novamente.
- Persista o estado. Grave a identidade da tarefa, a etapa concluída e a versão da entrada em armazenamento externo. Um arquivo local ou uma variável global não basta.
- Confirme depois. Só reconheça a mensagem quando a escrita durável estiver confirmada. Assim, uma nova instância pode encontrar a mesma chave e decidir entre retomar, reutilizar a saída ou executar a etapa restante.
Esse desenho separa vida do processo e vida do trabalho. Um worker pode morrer; a tarefa não pode depender de a memória daquele worker sobreviver. Se o processo executa um agente de IA, a mesma regra vale para tool calls que criam efeitos externos. A ferramenta RemoteCode é um exemplo do tipo de ferramenta do autor que precisa deixar estado e evidência fora do loop quando a execução sai do ambiente local.
Quando esse checkpoint precisa sobreviver a falhas, aprovações e várias etapas, a explicação sobre execução durável para agentes de IA aprofunda a diferença entre fila, checkpoint e workflow.
Como implementar o shutdown em um worker TypeScript?
O exemplo abaixo mostra o esqueleto do ciclo. claimNext, processOne, saveCheckpoint e ack são adaptadores ilustrativos. Eles precisam fornecer lease, escrita condicional e confirmação compatíveis com a fila usada pelo seu sistema.
import process from "node:process";
type WorkItem = { id: string; inputVersion: string };
let stopping = false;
const stopPolling = new AbortController();
const active = new Set<Promise<void>>();
process.once("SIGTERM", () => {
stopping = true;
stopPolling.abort();
console.log(JSON.stringify({ event: "shutdown_requested" }));
});
async function workerLoop() {
while (!stopping) {
const item = await claimNext({ signal: stopPolling.signal });
if (!item) break;
const run = runItem(item);
active.add(run);
void run.finally(() => active.delete(run));
}
await Promise.all(active);
console.log(JSON.stringify({ event: "shutdown_complete" }));
}
async function runItem(item: WorkItem) {
const result = await processOne(item);
await saveCheckpoint({
taskId: item.id,
inputVersion: item.inputVersion,
result,
});
await ack(item.id);
}
void workerLoop().catch((error) => {
console.error(JSON.stringify({ event: "worker_failed", error: String(error) }));
process.exitCode = 1;
});
O ponto importante não é o Set em si. É a ordem. O handler impede novos claims, active representa o trabalho já aceito, e saveCheckpoint acontece antes de ack. Se processOne falhar ou o processo morrer entre as duas últimas operações, a mensagem continua recuperável. A gravação precisa ser idempotente ou condicional para que uma nova tentativa não crie uma segunda saída.
Não use process.exit() para encurtar o caminho. A documentação de boas práticas do Cloud Run alerta que atividade em segundo plano depois do fim da invocação pode parar de receber CPU e causar conexões resetadas (Google Cloud, “Functions best practices”). Deixe a função principal retornar depois de aguardar as tarefas que ainda podem terminar com segurança.
Service, Job ou worker pool: onde esse ciclo se encaixa?
Em 2026, o overview do Cloud Run separa Service, Job e worker pool por modelo de execução: Service atende HTTP e eventos, Job executa tarefas até terminar e worker pool processa trabalho contínuo baseado em pull (Google Cloud, “What is Cloud Run”). O handler de SIGTERM é útil nos três, mas a fronteira de trabalho muda.
Service
Use Service quando uma requisição ou evento inicia o trabalho. A plataforma dá tempo para requisições em andamento terminarem quando uma instância precisa ser encerrada, mas seu código ainda precisa persistir estado e evitar uma atividade que dependa da instância depois da resposta. Para um agente, devolva um identificador de tarefa e deixe o processamento longo em uma camada que tenha sua própria política de recuperação.
Job
Use Job quando cada execução tem entrada definida e deve terminar. Um timeout ou cancelamento também pode enviar SIGTERM para a task. O post sobre evitar duplicação nos retries de Cloud Run Jobs detalha a parte seguinte: chave determinística, checkpoint e escrita idempotente. O handler prepara a saída; o desenho do Job decide como a task será tentada novamente.
Worker pool
Use worker pool quando o container consome uma fila continuamente sem endpoint HTTP. A documentação atual diz que worker pools não escalam automaticamente por fila e que as instâncias ativas continuam sendo cobradas (Google Cloud, “Container runtime contract”). O worker precisa parar o polling, liberar ou renovar o lease da mensagem e deixar a próxima instância assumir o que não foi confirmado.
Como verificar o SIGTERM antes do deploy?
Em 2026, o Google Cloud documenta um teste local com Docker: iniciar a imagem, enviar SIGTERM ao container e observar se o processo encerra corretamente (Google Cloud, “Select an execution environment for services”). O teste deve verificar o handoff, não apenas a última linha do log.
docker run --rm --name worker-under-test worker-image:dev
docker kill --signal=SIGTERM worker-under-test
Adicione uma tarefa artificialmente lenta e faça o teste confirmar quatro eventos: shutdown_requested, nenhum novo claim depois do sinal, checkpoint persistido e shutdown_complete. Em seguida, inicie um segundo worker com o mesmo armazenamento. Ele deve encontrar a tarefa em estado recuperável e produzir uma única saída para a mesma chave.
No ambiente implantado, correlacione logs por taskId, inputVersion e identificador da instância. Verifique o que ocorreu antes e depois do sinal: uma tarefa pode ter completado o efeito externo e morrido antes do ack. Essa é a janela que uma chave idempotente e uma escrita condicional precisam cobrir.
Verificação útil: uma implantação verde prova que o container iniciou. Ela não prova que uma tarefa sobreviveu à troca de instância. O teste de recuperação deve matar o processo em pontos diferentes da etapa.
Quais erros fazem o shutdown perder trabalho?
O primeiro erro é confirmar a fila antes do checkpoint. Se o processo morrer depois do ack, a fila não tem motivo para entregar a tarefa novamente, mesmo que a aplicação nunca tenha persistido o resultado.
O segundo é cancelar a tarefa ativa sem definir sua política de retomada. Abortar uma chamada externa pode ser correto para uma operação sem efeito, mas é perigoso para uma escrita que já começou. Marque a etapa como incerta, consulte o estado pelo identificador idempotente e só então decida se deve repetir.
O terceiro é gravar apenas no disco local. A camada gravável do container é descartável e pode desaparecer junto com a instância. O arquivo final, o checkpoint e a informação de lease precisam estar em um destino que outra instância possa ler.
O quarto é tratar os 10 segundos como garantia. O Google Cloud também documenta encerramento forçado quando há excesso de memória. SIGKILL não pode ser capturado pelo Node.js. Por isso, o sistema precisa ser seguro mesmo quando o handler não consegue terminar.
O quinto é esconder o processo atrás de um shell que não encaminha sinais. Prefira um entrypoint que execute o processo principal diretamente, como ENTRYPOINT ["node", "dist/worker.js"], e valide a imagem localmente. Se houver um script que gerencia subprocessos, ele também precisa encaminhar SIGTERM.
Perguntas frequentes sobre SIGTERM no Cloud Run
SIGTERM garante que a tarefa atual vai terminar?
Não. Em 2026, a documentação do Cloud Run descreve uma janela de 10 segundos antes do SIGKILL, mas uma task pode exceder esse limite, sofrer falha de memória ou cair antes do checkpoint. A tarefa precisa continuar recuperável pela fila, pelo banco ou por outra forma de estado durável.
Devo chamar process.exit() no handler?
Não como primeira reação. Um listener de SIGTERM remove o encerramento padrão do Node.js. Deixe o loop parar a entrada, aguarde o trabalho seguro e defina process.exitCode apenas para falhas que a aplicação não conseguiu tratar. A função principal deve terminar naturalmente.
O que muda para um worker pool?
O worker pool não recebe uma requisição HTTP para marcar o fim da unidade de trabalho. O loop precisa interromper o pull, terminar ou devolver o item ativo e persistir o checkpoint antes de sair. Como o pool não escala automaticamente por fila, a capacidade e o caminho de recuperação também precisam ser observáveis.
Fontes consultadas
- Google Cloud, “Container runtime contract”, consultado em 12/08/2026. URL:
https://docs.cloud.google.com/run/docs/container-contract - Google Cloud, “Select an execution environment for services”, consultado em 12/08/2026. URL:
https://docs.cloud.google.com/run/docs/configuring/execution-environments?hl=en - Google Cloud, “What is Cloud Run”, consultado em 12/08/2026. URL:
https://docs.cloud.google.com/run/docs/overview/what-is-cloud-run - Google Cloud, “Functions best practices”, consultado em 12/08/2026. URL:
https://docs.cloud.google.com/run/docs/tips/functions-best-practices?hl=en - Node.js, “Process”, consultado em 12/08/2026. URL:
https://nodejs.org/api/process.html - Google Cloud, “Graceful shutdowns on Cloud Run: Deep dive”, consultado em 12/08/2026. URL:
https://cloud.google.com/blog/topics/developers-practitioners/graceful-shutdowns-cloud-run-deep-dive