Blogue ·
Um prazo não é um botão de paragem
Um prazo termina a espera, não o trabalho. O que um limite de uma hora faz a uma cópia longa, porque dois relógios discordam e como registar o incerto.
Por Provibr
Todos os comandos remotos precisam de um prazo. Essa parte não é controversa: um processo que pode esperar para sempre acabará por esperar para sempre, e um pedido preso ocupa um trabalhador, uma ligação e uma linha na fila até alguém reiniciar alguma coisa.
Muito menos óbvio é o que o prazo faz de facto. Termina a espera do nosso lado. Não termina, no caso geral, o trabalho do outro lado. Tudo o que é difícil nas operações longas vive nessa diferença.
Corremos um agente nos hipervisores dos clientes e enviamos-lhe comandos. Uma cópia de segurança de uma máquina virtual foi o comando que nos obrigou a levar essa diferença a sério, porque uma cópia pode honestamente demorar uma hora, e uma hora é exatamente onde estão os nossos próprios limites.
O limite não é uma preferência
O nosso agente restringe a uma hora o prazo de todos os comandos. A plataforma pode pedir mais; não o vai receber. A restrição é um único min() sobre o valor que chega no pedido, com zero a significar «não definido» e um recurso de dois minutos quando o campo falta.
Essa restrição tem uma consequência que escapa a quem define tempos limite num ficheiro de configuração. Se a plataforma acreditar num prazo em que o agente não acredita, ficam dois sistemas em desacordo sobre quando é que um comando está morto, e o desacordo aparece com o pior sintoma possível: o agente desiste, comunica e segue em frente, enquanto a plataforma continua a manter a linha aberta à espera de uma resposta que nunca chegará com a forma que ela espera.
Portanto a regra a que chegámos é aborrecida e vale a pena escrevê-la na mesma. Todos os tempos limite do lado da plataforma são escolhidos com o teto do agente presente na sala. Os nossos prazos de cópia e de restauro são de uma hora precisamente por ser esse o teto, e de várias dezenas de prazos de comandos são os dois únicos que lhe tocam.
Os prazos não são uma questão de estilo
Eis o aspeto real dos nossos prazos de comandos, e a razão de cada um ser o que é.
| Comando | Prazo | Porquê esse número |
|---|---|---|
| Ler hardware, ler regras de firewall | 30 s | Uma leitura. Mais tempo do que isto significa que outra coisa está avariada. |
| Escrever regras de firewall | 120 s | Uma chamada à API por regra, nos dois sentidos, no teto de regras. |
| Listar pontos de restauro | 60 s | Uma chamada por armazenamento de cópias no nó, e um armazenamento em diretório com milhares de arquivos lê do disco o seu próprio índice. |
| Atualização do próprio agente | 300 s | Descarregar, calcular o resumo, escrever. Generoso para uma linha lenta em casa do cliente. |
| Criar uma cópia de segurança | 3600 s | O teto do agente. |
| Restaurar uma cópia de segurança | 3600 s | O mesmo teto, atingido mais cedo: um restauro move sensivelmente o dobro dos bytes que a cópia moveu. |
| Preparar uma máquina de nuvem nova | 1200 s | Atualizações de pacotes numa imagem que pode ter semanas, e depois a instalação. |
| Correr uma migração de painel | 1800 s | Dois painéis lidos por inteiro, escritos e recontados. |
Duas dessas linhas são histórias em vez de números.
A da máquina de nuvem caía antes no valor por omissão de dois minutos. Dois minutos chegam para uma imagem que por acaso está atualizada, e não chegam para mais nada, pelo que a plataforma comunicava uma falha enquanto a instalação dentro da máquina prosseguia e terminava uns minutos mais tarde. A encomenda ficava marcada como falhada. A máquina estava a correr. É o pior tipo de erro: não é um erro, é uma contradição, e é o cliente quem tem de a resolver.
A da migração é o prazo mais longo que não é o teto do agente, e o raciocínio por trás dela é uma contagem em vez de um pressentimento. O migrador faz uma chamada à API por utilizador e uma por servidor contra o painel de destino. Com mil servidores são mil chamadas para fora e mil respostas de volta, com uma leitura completa dos dois lados antes disso e uma recontagem depois.
Os custos de errar não são simétricos
É este o princípio por baixo das duas histórias. Um prazo demasiado curto e um prazo demasiado longo falham de maneiras completamente diferentes, e tratá-los como dois lados do mesmo botão dá o número errado de todas as vezes.
Demasiado longo: uma operação que morreu genuinamente mantém uma linha aberta por mais algum tempo. Alguém espera. Nada fica corrompido, nada fica duplicado, e a limpeza que acaba por acontecer não tem história.
Demasiado curto: fica registada uma falha para trabalho que teve sucesso. Agora os registos e a realidade discordam, e todos os caminhos de recuperação existentes (repetir, recriar, reembolsar, alertar) apontam para um problema que não existe e vão criar um segundo.
Não são igualmente maus, pelo que não devem receber margens do mesmo tamanho. Para tudo o que cria, move ou elimina recursos reais, ficamos deliberadamente do lado generoso e escrevemos porquê.
Dois relógios que começam em momentos diferentes
Existe em qualquer sistema de comandos em fila uma subtileza fácil de ignorar e fácil de medir. A nossa plataforma calcula o prazo como um momento absoluto, no instante em que a linha do comando é escrita. O agente calcula o seu prazo como uma duração, a começar quando inicia a execução.
São relógios diferentes. Se o agente estiver offline, ou ocupado, ou se o comando ficar trinta segundos na fila, a plataforma já gastou trinta segundos do seu orçamento e o agente não. A plataforma desiste primeiro.
É o sentido certo, e vale a pena ser explícito quanto ao motivo. O prazo da plataforma responde à pergunta que o cliente está realmente a fazer: quanto tempo passou desde que carreguei? O do agente responde a outra: quanto tempo pode correr este bocado de trabalho? O tempo de relógio desde o clique é o número sobre o qual versa uma conversa de suporte. Fazer deles o mesmo número seria mais arrumado e responderia à pergunta errada.
Esconde-se ali dentro um terceiro relógio. No nosso agente, a espera por uma permissão de concorrência acontece fora do prazo de execução. Um comando em fila atrás de outro comando contra a mesma integração não está a queimar o seu próprio orçamento enquanto espera. Se os prazos forem apertados, verifique qual dos seus relógios inclui o tempo em fila; a resposta raramente é a que está na documentação.
O que o prazo cancela de facto
Quando o prazo do agente expira, ele sinaliza o cancelamento e deixa de esperar. Aquilo que esse cancelamento alcança é o nosso próprio código: o ciclo de sondagem que perguntava ao hipervisor como ia a tarefa.
Não alcança o hipervisor. A cópia continua a correr. Vai terminar, ou vai falhar, inteiramente sem nós. Nada do cancelamento viajou até ao sistema de armazenamento.
Isto não é uma limitação para resolver com engenharia. É a forma honesta do problema, e o movimento útil é registar o resultado com verdade em vez de fingir. A taxonomia que sobreviveu ao contacto com a produção tem três desfechos e não dois.
- Falhou. Sabemos que não aconteceu nada. O cliente pode repetir e não há nada para limpar.
- Desconhecido. Deixámos de escutar antes de haver resposta. Pode existir alguma coisa.
- Precisa de reconciliação. Temos razões positivas para pensar que existe alguma coisa de que não conseguimos dar conta.
Um restauro que excede o prazo é o caso mais afiado. Pode haver agora uma máquina a correr no hipervisor para a qual a nossa base de dados não tem linha nenhuma. Marcá-lo como falhado seria uma mentira que esconde uma máquina; marcá-lo como bem-sucedido seria uma mentira que inventa outra. Fica marcado como precisando de uma vista de olhos, e o painel pede ao cliente que verifique em vez de lhe oferecer um botão de repetição que construiria uma segunda máquina ao lado da primeira.
Pusemos essa regra no código como uma lista dos casos certos, não dos incertos. Quatro códigos de erro específicos significam que o trabalho nunca começou, porque os quatro vêm de verificações que o agente faz antes de fazer a primeira chamada. Todo o resto, incluindo qualquer código de erro que acrescentemos mais tarde e nos esqueçamos de classificar, cai por omissão em incerto. A direção importa: um código esquecido que fique discretamente registado como falha faz desaparecer dos registos uma máquina existente, e um código esquecido que fique registado como incerto só custa a alguém uma vista de olhos.
Um resultado incerto precisa de uma forma de se tornar certo
Um estado que significa «alguém devia verificar» só é honesto se alguém, ou alguma coisa, verificar mesmo. O nosso é um trabalhador que pede periodicamente a cada máquina com histórico de cópias que volte a listar os seus pontos de restauro, para que um arquivo que desapareceu em silêncio no fornecedor se torne uma notificação em vez de uma lista que ficou mais curta no dia em que um cliente calhou de olhar.
Existe uma restrição deliberada de baixo custo nesse ciclo, porque cada listagem é um comando a correr no anfitrião de outra pessoa. Só visita máquinas que tenham uma política ou pelo menos um ponto de restauro conhecido, e leva um lote limitado por ronda. Quando isso deixar de chegar, a correção certa é um cursor e não um número maior.
O ciclo também precisa de sobreviver a dois dos nossos próprios servidores decidirem corrê-lo no mesmo segundo. Fazemo-lo com uma chave de idempotência durável feita da máquina e do número da janela, não de um instante: dois servidores nunca disparam no mesmo milissegundo, mas estão certamente dentro da mesma janela de seis horas, pelo que um instante não desduplicaria nada.
O que aconselhamos a escrever
Para quem está a escolher prazos para trabalho remoto, quatro perguntas levam quase até ao fim.
- Qual é o teto rígido, e quem o impõe? Se dois componentes discordarem, na prática ganha o menor e o maior é ficção.
- Quando é que cada relógio começa? O tempo em fila está no orçamento ou não está, e ambas as respostas são defensáveis. Não saber não é.
- O que é que a expiração cancela? Se a resposta for «a nossa própria espera», diga-se em voz alta, porque o resto do desenho decorre daí.
- Para que lado cai um desfecho não classificado? Faça da incerteza o valor por omissão e deixe que a certeza seja aquilo que tem de escrever.
Nada disto torna mais curta uma cópia longa. Torna verdadeiro o registo dela, que é a parte que se vai ler às duas da manhã.