Nesta página
Em uma palestra publicada em 3 de outubro de 2026 pela AI Engineer, um responsável por relações com desenvolvedores da Crusoe iniciou um job de treinamento em PyTorch em dois nós A100. Depois, informou ao próprio sistema de monitoramento da demo que uma GPU havia gerado um erro XID 79. O nó foi marcado como fora do ar e o job, cancelado. O processo recebeu um SIGTERM e “até dois minutos para tratar esse SIGTERM, seja para salvar seus checkpoints, seja para descarregar seus logs”. Um nó reserva entrou e “o job do slurm vai reiniciar. Ele vai carregar o checkpoint e continuar o treinamento de onde parou”. Pouco menos de quinze minutos do início ao fim. Ninguém foi acionado no plantão.
Uma sincronização é um bicho muito menor do que um treinamento. A pergunta é a mesma. Do que uma execução precisa para que matar o worker custe minutos e nenhuma duplicata?
Esta é a resposta para o executor de sincronizações do próprio Tajo, lida no código e não em um folheto. O Reverse ETL descreve a forma do trabalho. Isto é o que acontece quando o trabalho morre.
Acompanhe um contato
Uma execução lê a origem uma página por vez, a partir de um cursor salvo. Vamos chamar o cursor da página de c3 e o nosso contato de 212º registro dessa página. Três coisas são verdadeiras sobre esse contato antes que qualquer coisa seja escrita.
Primeiro, ele tem uma chave de entrega. A chave é um hash do id da sincronização, da origem, do cursor da página, da posição na página e do próprio registro. O cursor e o registro entram no hash e nunca são incorporados à chave, então a chave não carrega nenhum endereço de e-mail.
Segundo, a escrita passa por um livro de registro, e não direto pelo destino. Antes de o destino ser chamado, a chave é reservada como pendente. Quando a escrita tem sucesso, a linha é marcada como concluída e guardada por sete dias.
Terceiro, o cursor não se moveu. Ele só se move no fim de um lote, só se cada registro desse lote chegou, e só depois de a execução ter provado que ainda é dona da sincronização.
Agora mate o worker entre a escrita e o checkpoint.
Em um SIGTERM, cada worker de fila é drenado sob um único prazo compartilhado de quinze segundos, e o processo encerra após vinte e cinco segundos, não importa o que ainda esteja rodando; o systemd lhe dá quarenta e cinco antes de matá-lo. Uma execução que não terminou dentro dessa janela continua marcada como em andamento, e o próximo worker a retoma pela recuperação de jobs travados da fila. Em uma interrupção forçada, não há drenagem nenhuma. Em ambos os casos, os mesmos dois fatos valem. O destino tem o nosso contato. O nosso cursor ainda diz c3.
A repetição
A execução seguinte começa em c3, lê a mesma página, deriva a mesma chave para o registro 212 e consulta o livro de registro. O livro diz: concluída. O destino não é chamado; o resultado armazenado é reproduzido e a execução segue em frente. No caminho em massa, em que o endpoint de importação do Brevo recebe uma página inteira em uma só chamada, a mesma verificação roda por chave antes de a requisição ser montada. Uma página em que todas as chaves já estão concluídas não envia nada.
Esse é todo o truque, e vale a pena dizer por que ele funciona. A chave é derivada do cursor da página no início do lote, e não da execução que por acaso estava rodando. Uma queda, uma nova tentativa sessenta segundos depois e uma execução agendada para amanhã derivam todas a mesma chave para o mesmo registro. Para o livro de registro, tanto faz qual delas está perguntando.
Agora, o custo. O livro de registro esquece depois de sete dias. O próprio comentário do código diz que sete dias cobrem uma nova tentativa atrasada e uma indisponibilidade curta, e não afirma mais do que isso. Uma repetição a partir de um cursor mais antigo do que isso escreve de novo. E uma execução interrompida à força pode ter de esperar. A concessão temporária da execução morta vale por até quinze minutos, e, numa sincronização agendada, uma execução cuja concessão temporária está livre só é varrida e reagendada quando começou há mais de uma hora. A demo da Crusoe volta em menos de quinze minutos. A nossa, nem sempre.
Quem é dono da sincronização enquanto ela reinicia
Uma única execução é dona de uma sincronização por vez. A execução obtém uma concessão temporária no Redis com seu próprio token, uma validade de quinze minutos e uma renovação a cada cinco minutos. Antes de cada checkpoint, ela confirma que ainda detém essa concessão. Se não detém, ela para. A execução é registrada com a frase simples que a pessoa vai ler: “The run lost its slot part-way through and stopped to avoid writing twice.” (A execução perdeu sua vez no meio do caminho e parou para evitar escrever duas vezes.) Uma segunda execução que colide com uma dona ativa não é tentada de novo. Ela termina com “Another run of this sync was already in progress” (Outra execução desta sincronização já estava em andamento), e a próxima execução agendada começa normalmente.
A responsabilidade pela correção também está escrita. Cada código de falha corresponde a uma frase sobre o que aconteceu, uma sobre o que fazer em seguida e um responsável: você ou o Tajo. Um destino que rejeitou todos os registros porque o mapeamento está errado é com você. Uma concessão temporária perdida é com a gente. A página da execução mostra o próximo passo; o guia de recuperação da sincronização indica qual dos dois.
Até aqui, essa é a metade fácil. Uma queda custa uma repetição e nada chega duas vezes. Dois incidentes nos ensinaram a outra metade.
Falha após falha
Uma sincronização falhou repetidas vezes. Não em um loop apertado: uma vez por intervalo do agendador, execução após execução, com a mesma mensagem toda vez, a de que um id personalizado não pode conter dois-pontos. A execução nunca chegou ao destino. Ela morria enquanto era colocada na fila, antes que qualquer executor a visse, então um alerta baseado no executor nunca teve no que se basear.
Nada vigiava a sequência. Essa é toda a descoberta.
A correção é um disjuntor. Depois de cada falha registrada, ele lê as últimas cinco execuções concluídas da sincronização desde a última vez que ela foi alterada. Se as cinco forem erros, ele pausa a sincronização, grava uma linha de auditoria com o motivo circuit_breaker_tripped e dispara um alerta crítico. A sequência é derivada das execuções armazenadas, e não de um contador no worker. Uma falha registrada por qualquer caminho conta, inclusive a falha ao entrar na fila que causou o incidente. O comentário do código diz que ele teria parado aquele incidente na quinta execução. Um único sucesso zera a sequência. Tirar a pausa rearma o disjuntor em vez de fazê-lo desarmar de novo com as linhas antigas. O guia do conector Shopify diz a mesma coisa em uma linha: depois de cinco execuções com falha consecutivas, a sincronização se pausa sozinha e a página dela mostra o último erro.
O que o disjuntor não faz é consertar nada. A Crusoe substitui o nó e reinicia o job sem perguntar. O Tajo para e pergunta: uma sincronização pausada continua pausada até que uma pessoa corrija a causa e a publique de novo. Esse padrão cauteloso é proposital: uma nova tentativa automática de uma sincronização que escreve em registros de clientes é, ela mesma, uma escrita.
O silêncio que ninguém viu
A objeção é justa, e é mais ou menos assim: “Um disjuntor que pausa depois de cinco falhas também teria pegado o segundo incidente. Por que construir um segundo vigia?” Porque o segundo incidente não parecia uma falha. Durante um longo período, um espaço de trabalho teve uma sincronização ativa, uma pilha crescente de linhas de erro e nem um único sucesso, nunca, e nada alertou. Todo sinal que existia se baseava em atividade: uma execução falhou, um webhook chegou, um orçamento foi ultrapassado. Zero sucessos não é um evento. É a ausência de um, e um disjuntor só roda quando uma falha é registrada. Um agendador morto, uma fila travada, um worker que nunca inicia: nada disso registra coisa alguma, e um disjuntor nunca acorda.
O dispositivo de homem morto de uma locomotiva funciona ao contrário. O maquinista precisa continuar pressionando-o, e o silêncio para o trem. A versão para sincronizações roda uma vez por hora, aos treze minutos. Para cada espaço de trabalho com uma sincronização ativa, ela faz uma única pergunta: alguma execução teve sucesso nas últimas vinte e quatro horas? Um espaço de trabalho cuja sincronização ativa mais antiga foi ativada há menos de um dia fica em paz; ele ainda não teve um dia para dar certo. Um mais antigo e em silêncio recebe um alerta crítico. O alerta diz de que silêncio se trata: execuções registradas, mas nenhuma com sucesso, ou nenhuma execução registrada. Ele dispara uma vez por incidente. Um marcador no Redis o mantém fechado até a primeira execução bem-sucedida e depois é apagado, para que o próximo incidente volte a alertar.
Os dois vigias chegaram em um único commit, depois do fato. O comentário do disjuntor diz que ele teria parado o primeiro incidente na quinta execução. O do homem morto diz que uma sincronização ativada há muito tempo com zero execuções precisa alertar. Nenhum dos dois existia quando foi preciso. Nós os escrevemos depois, com os dois incidentes registrados no cabeçalho do arquivo para que ninguém esqueça o motivo.
O que está construído e o que não está
Construído, no momento em que escrevemos:
- O cursor é salvo no fim de cada lote sem erros, e de novo em caso de queda.
- A chave de entrega de cada registro é derivada do cursor da página e da sua posição, e conferida em um livro de registro de sete dias antes de cada chamada ao destino.
- Uma concessão temporária por sincronização, com validade de quinze minutos.
- Cinco tentativas por execução, com espera crescente a partir de sessenta segundos.
- Um disjuntor que pausa a sincronização após cinco falhas consecutivas e registra o motivo.
- Um alerta de hora em hora após vinte e quatro horas sem um sucesso.
Não construído, e vale saber antes de confiar nisso:
- O homem morto vigia o espaço de trabalho, não a sincronização. Uma sincronização saudável esconde uma irmã silenciosa no mesmo espaço de trabalho.
- O alerta dele chega aos operadores do Tajo: sempre uma linha de log, e Sentry, um webhook ou um e-mail onde estiverem configurados. Ele não aparece na página da sincronização. A pausa do disjuntor, sim.
- Nada reinicia uma sincronização pausada. Uma pessoa faz isso.
- A recuperação depois de uma interrupção forçada não é rápida. A concessão temporária pode valer por quinze minutos e a varredura espera uma hora.
- Os dois limites, cinco falhas e vinte e quatro horas, são configurações da implantação. Você não pode defini-los por sincronização.
Uma longa cadeia de passos quase perfeitos ainda falha; a saída é definir o estado final e verificá-lo. Este artigo é a metade operacional disso. Defina o que uma execução morta deixa para trás, e verifique o nada que o silêncio deixa para trás.
A frase de encerramento da Crusoe foi que “as falhas são inevitáveis”. A nossa vai um passo além. A queda era a metade fácil, porque deixava linhas. O silêncio era a metade que tivemos de ir procurar.
Perguntas frequentes
O que acontece com uma sincronização em andamento quando o worker morre no meio de um lote?
O cursor continua apontando para o início do lote interrompido, porque o Tajo só o move depois que um lote chega sem erros. A execução seguinte lê a mesma página de novo. Cada registro carrega uma chave de entrega derivada do cursor da página e da sua posição, e um livro de registro guarda cada escrita concluída por sete dias, então os registros já escritos são reproduzidos a partir desse livro em vez de serem enviados ao destino uma segunda vez.
Por que uma sincronização repetida não escreve o mesmo contato duas vezes?
Antes de cada chamada ao destino, a execução confere a chave de entrega do registro em um livro de registro. A chave é um hash da sincronização, da origem, do cursor da página, da posição na página e do próprio registro, então uma queda, uma nova tentativa um minuto depois e uma execução agendada no dia seguinte derivam a mesma chave para o mesmo registro. Uma chave já marcada como concluída dentro da janela de sete dias não é enviada de novo.
O que é um alerta de homem morto para uma sincronização?
Uma verificação que dispara diante de uma ausência, e não de uma falha. Uma vez por hora, o Tajo examina cada espaço de trabalho com uma sincronização ativa e alerta quando nenhuma execução teve sucesso nas últimas vinte e quatro horas, incluindo o caso em que nenhuma execução foi registrada. Ele dispara uma vez por incidente e volta a ficar armado depois da primeira execução bem-sucedida. Ele existe porque um alerta de falha só consegue ver as falhas que foram registradas.