Engenharia

Um orchestrator que não sobrevive ao próprio crash não é um

Um crash que apaga o raciocínio do orchestrator perde a única coisa que você não consegue reconstruir.

ASR

Apollo Space Research

Apollo Space

· 11 min de leitura

Um orchestrator está há quarenta minutos coordenando seis workers. Ele leu a spec, dividiu o trabalho, despachou cinco dos seis, decidiu que o sexto tem que esperar pelo terceiro, e está segurando na cabeça uma centena de pequenos julgamentos que ninguém escreveu: qual worker é confiável, qual resultado pareceu estranho, o que ele estava prestes a fazer em seguida. Aí o processo morre. Falta de memória, uma conexão derrubada, um reboot de host, a causa mal importa. O que importa é o que volta. Os workers ainda estão rodando, ainda escrevendo arquivos, ainda gastando dinheiro. O orchestrator se foi, e com ele cada razão que ele tinha para tudo que pôs em movimento.

Os workers nunca foram a parte frágil. O raciocínio do orchestrator era.

Um crash que apaga o raciocínio do orchestrator perde a única coisa que você não consegue reconstruir.

Este post é sobre por que essa única coisa é a mais difícil de proteger, por que as formas óbvias de protegê-la silenciosamente falham, e o que fazemos no lugar para que um supervisor morto seja um evento recuperável e não um pequeno desastre.

O orchestrator segura algo que os workers não seguram

Comece com o formato do problema, porque não é onde as pessoas olham.

Numa frota, os workers são a parte visível, cara, perigosa. Eles escrevem código, batem em bancos de dados, chamam o mundo lá fora. Então é para lá que a atenção de engenharia vai: sandbox o worker, limite seu gasto, contenha seu raio de explosão. Tudo correto. Tudo necessário. E tudo isso deixa a coisa de fato frágil completamente exposta.

O orchestrator não escreve muito. Todo o trabalho dele é julgamento, o modelo corrente de o que está acontecendo e por quê. Ele sabe que o worker quatro voltou com um resultado que cheirou mal e foi mandado para re-checagem. Ele sabe que o worker dois está bloqueado pelo worker cinco e não pode ser avisado de que terminou. Ele sabe que o usuário pediu três coisas e só duas estão em andamento. Nada disso está num arquivo. Está no estado de trabalho do orchestrator, do jeito que o plano de uma reunião vive na cabeça da pessoa que a conduz.

Os workers podem ser reiniciados. O raciocínio que decidiu iniciá-los não pode.

Perca um worker e você perde algum compute. Você o re-despacha; o resto da frota não nota. Perca o orchestrator e você perde a única coisa que sabia que os workers eram para algo. Eles continuam rodando, essa é a parte cruel, desconectados da intenção que os justificava, gastando dinheiro real num plano que nenhum processo sobrevivente lembra. Um crash que apaga o raciocínio do orchestrator perde a única coisa que você não consegue reconstruir.

Duas falhas comparadas: um worker em crash derruba apenas seu próprio compute enquanto a frota continua, mas um orchestrator em crash deixa órfãos todos os workers ainda rodando porque o raciocínio que os justificava se foi.

Então a pergunta não é como fazer o orchestrator nunca dar crash. Tudo dá crash. A pergunta é como um novo orchestrator, iniciado a frio num host diferente, recupera o suficiente da mente do morto para assumir o volante sem re-decidir tudo do zero, e sem dois deles agarrarem o volante de uma vez.

O conserto ingênuo: só reinicie

A resposta óbvia é aquela com que todo supervisor vem. O orchestrator morre, um watchdog nota, um novo começa. Supervisão de processo resolveu isso décadas atrás. Por que agents seriam diferentes?

Porque reiniciar o processo não é reiniciar o raciocínio.

O novo orchestrator boota com a cabeça vazia. Ele não sabe que seis workers estão em pleno voo. Ele não sabe que o worker quatro já foi sinalizado como suspeito, então ou ignora um resultado que deveria ter re-checado ou re-checa um que já tinha sido liberado. Ele não sabe que o worker dois está esperando, então pode mandá-lo prosseguir para uma dependência que não está pronta. Pior de tudo, ele não sabe que os workers antigos existem, então pode despachar um segundo conjunto contra as mesmas tarefas, e agora doze workers estão rodando onde seis estavam planejados, metade deles pisando nos arquivos da outra metade.

Um reinício sem memória não é recuperação. É um estranho entrando numa sala de controle no meio de um lançamento e começando a virar interruptores.

A dor é mais aguda exatamente quando você menos pode bancá-la: sob carga, fundo numa execução longa, com mais estado em andamento. A demo da tarde funciona porque nada deu crash ainda. O job overnight das 2h da manhã é onde o supervisor morre quarenta minutos adentro e o reinício pelado transforma uma falha num engavetamento. Qualquer um que tenha rodado uma frota por uma noite real conhece esse formato de falha, o orchestrator não deu crash porque o trabalho era difícil; ele deu crash porque era longo, e “longo” é precisamente quando perder a mente custa mais.

Então um reinício honesto é o chão. Ele mantém o serviço vivo. Não faz nada pelo raciocínio, e o raciocínio era o ponto inteiro.

Nosso jeito: faça checkpoint do raciocínio, não só do resultado

Aqui está a jogada, e a ideia-chave é simples: o orchestrator faz checkpoint de si mesmo do mesmo jeito que faz checkpoint dos seus workers.

A maioria das frotas já persiste o output do worker, o artefato, o diff, o resultado. Nós persistimos a camada acima dele. Conforme o orchestrator raciocina, ele escreve um heartbeat corrente do próprio estado para armazenamento durável fora do seu processo: o plano que está executando, quais workers despachou e por quê, quais resultados aceitou ou sinalizou, o que estava prestes a fazer em seguida, para que cada worker serve. Não um dump de transcrição. Um relato estruturado das decisões, atual até segundos atrás.

A disciplina é uma frase. O orchestrator é o único processo no sistema que nunca tem permissão de manter seu raciocínio apenas na própria cabeça.

Quando ele morre, um orchestrator novo não boota cego. Ele lê o último checkpoint e reconstrói a mente do morto: estes seis workers estão vivos, o worker quatro é suspeito, o worker dois está bloqueado, o usuário ainda é devedor de uma terceira coisa não iniciada. Ele adota a frota rodando em vez de deixá-la órfã. O trabalho em andamento mantém seu significado, porque o significado foi escrito uma batida antes do crash. Um crash que apaga o raciocínio do orchestrator perde a única coisa que você não consegue reconstruir, então garantimos que o crash não possa apagá-lo.

Esta é a mesma lição que os workers já nos ensinaram, aplicada um nível acima. Nunca confiamos no “pronto” de um worker que vivia apenas no próprio relato do worker; nós o escrevíamos onde um processo mais frio pudesse relê-lo. O orchestrator recebe o mesmo tratamento. O julgamento dele é valioso demais para viver em qualquer lugar que um crash possa alcançar.

Um loop onde o orchestrator continuamente escreve seu plano, despachos e sinalizações para um checkpoint durável fora do seu processo, para que um orchestrator novo possa ler esse checkpoint e adotar a frota ainda rodando em vez de começar cego.

A parte que todo mundo esquece: dois orchestrators é pior que nenhum

Há uma armadilha esperando do outro lado da recuperação, e é a que transforma um conserto esperto num desastre novo.

Se um orchestrator novo pode adotar uma frota rodando, o que impede dois deles de adotá-la de uma vez? O watchdog inicia um substituto, mas o original não estava morto, só lento, congelado numa chamada de rede travada por noventa segundos. Agora há dois orchestrators, ambos convencidos de que possuem os mesmos seis workers, ambos emitindo instruções. Eles re-despacham as mesmas tarefas. Eles se contradizem. A frota ganha dois capitães e obedece a ambos. Isso é pior que um crash limpo, porque um crash limpo ao menos deixa uma história coerente; um cérebro dividido deixa duas, brigando.

O instinto ingênuo é tornar a recuperação mais rápida e torcer para a janela de sobreposição encolher até zero. Ela nunca encolhe até zero. Esperança não é uma primitiva de concorrência.

O que de fato fecha a lacuna é uma única regra: adotar uma frota requer vencer um claim atômico, e exatamente um processo pode vencer. Antes de um orchestrator novo tocar num único worker, ele tem que tomar um lock exclusivo daquela execução. O primeiro que entra o pega. O segundo, o original congelado, descongelando, ou um segundo watchdog disparando, pede o mesmo lock e ouve não, tomado, e recua. Dois orchestrators correm; um vence; o perdedor não ganha uma frota de consolação. O lock é o que torna “adotar o trabalho rodando” seguro em vez de imprudente.

Recuperação sem um lock não é recuperação. É um segundo capitão num navio que já tem um.

Essa ordem importa: claim primeiro, então adote, então aja. Leia o checkpoint para saber o que está acontecendo, vença o lock para ganhar o direito de agir sobre ele, e só então emita uma instrução. Pule o lock e uma mente lindamente reconstruída simplesmente vira a segunda voz brigando sobre a mesma frota.

O que isso custa, e por que pagamos

Nada disso é de graça, e fingir o contrário seria o tipo de “parece pronto” em que não confiamos.

Fazer checkpoint do raciocínio do orchestrator custa escritas, um fluxo constante de estado para armazenamento durável a cada decisão significativa, não só no final. Custa a disciplina de tratar o checkpoint como um artefato real, estruturado o suficiente para que um processo frio consiga de fato lê-lo, não um log que ninguém parseia. E o claim lock custa um round-trip antes de qualquer um poder agir, mais o cuidado de torná-lo genuinamente atômico, porque um lock com uma corrida nele é pior que nenhum lock, ele te dá falsa confiança enquanto o cérebro dividido se forma assim mesmo.

Pagamos tudo isso de propósito, porque a coisa do outro lado do balancete é a cara. O custo de um orchestrator perdido nunca foi o orchestrator. Foi a frota que ele abandonou: workers rodando num plano que ninguém lembra, dinheiro gasto rumo a uma intenção que morreu com o processo, uma longa execução overnight que tem que recomeçar do topo porque o supervisor esqueceu por que começou. Pegar isso com um checkpoint e um lock é barato ao lado de descobri-lo de manhã.

Quando o humano entra

São 2h da manhã e o supervisor morreu quarenta minutos adentro de uma execução de seis horas. Imagine as duas manhãs.

Na primeira, a pessoa de plantão acorda com um alerta, abre o laptop e começa a arqueologia: quais workers estavam rodando, o que estavam fazendo, algum deles terminou, qual era o plano, por que começou, alguma coisa pode ser salva ou a noite inteira vai pro lixo. Uma hora de reconstrução para recuperar uma mente que estava plenamente intacta noventa minutos atrás e simplesmente não foi escrita. Eles estão fazendo, na mão e meio acordados, exatamente o trabalho que o checkpoint existe para fazer.

Na segunda manhã, não há alerta que valha a pena acordar. Um orchestrator novo leu o último checkpoint, tomou o lock, adotou os seis workers em pleno passo e levou a execução até o fim. O crash aconteceu. Só não foi um evento, foi um blip que o sistema absorveu, do jeito que um bom time absorve uma pessoa indo embora doente sem o projeto parar.

Essa segunda manhã é o ponto inteiro. Não que nada quebre, coisas quebram, mas que quando o coordenador quebra, nenhuma pessoa precisa virar o coordenador às 2h da manhã para manter o trabalho vivo. O raciocínio nunca esteve preso num processo frágil, então nenhum humano precisa reconstruí-lo de um log frio. O sistema lembra por que estava fazendo o que estava fazendo, mesmo através da própria morte, para que as pessoas que dependem dele possam continuar dormindo.

Essa última parte é a que você não consegue comprar com hardware mais confiável. Uma máquina que nunca dá crash ainda esquece tudo no instante em que dá. O que ganha confiança é um sistema que assume que vai dar crash e arruma sua mente para que o crash não possa levar o raciocínio junto.


É isso que estamos construindo na Apollo Space: um sistema operacional cujo coordenador trata a própria morte como um evento normal, faz checkpoint do raciocínio antes que ele possa ser perdido, e entrega a frota rodando ao seu sucessor sem uma pessoa no loop. Se suas piores lembranças de plantão são as noites em que você acordou e teve que virar a coisa que deu crash, esse é o trabalho que achamos que o sistema deveria segurar, para você não ter que segurar.

A Apollo cuida da operação repetitiva da sua empresa pro seu time não precisar.

Entre na lista de espera: acesso antecipado, preço de usuário fundador e um lugar na primeira fila enquanto a gente constrói.

Entrar na lista de espera