Apanhar Apps que se Avariam Logo Após a Publicação
No dia 25 de setembro, três publicações foram marcadas como bem-sucedidas, embora a API da app se avariasse segundos após cada arranque. Como tinham "sucesso", nenhum rollback foi executado; uma verificação em segundo plano falhou-as alguns minutos depois, e o site inteiro passou a devolver 404. O health check estava a contar as falhas na lista de eventos da plataforma de alojamento, que só guarda os últimos cinco eventos por máquina, por isso um ciclo de falhas mantém a lista sempre cheia e a contagem nunca sobe. Agora compara a hora da falha mais recente, e uma publicação em ciclo de falhas falha e repõe a versão anterior.
O que uma publicação espera
Quando publicas uma app com backend na Mythex, a nova versão é instalada em máquinas da nossa plataforma de alojamento, e um health check decide se a publicação ficou ativa ou falhou. Se falhou, um rollback repõe a versão anterior.
O difícil é que "arrancou" não é o mesmo que "está a responder". A plataforma indica uma máquina como iniciada assim que ela está de pé. Uma app que se avaria um segundo depois de arrancar é reiniciada de imediato, e momentos depois volta a aparecer como iniciada. Por isso, o health check foi ganhando algumas camadas ao longo do tempo:
- Olhar duas vezes. Depois de uma máquina aparecer como iniciada, espera um pouco e olha outra vez. Uma falha ao arrancar costuma ser apanhada na segunda verificação.
- Vigiar os ciclos de falhas. Se a plataforma indicar que uma máquina esteve a falhar repetidamente e que desistiu de a reiniciar, falha.
- Contar as falhas desde o início da publicação. Duas verificações de "iniciada" podem apanhar um reinício entre elas. Por isso, se tiver havido alguma falha desde o início desta publicação, olha uma terceira vez; se houver mais falhas do que na verificação anterior, a app não está a manter-se de pé.
Foi a terceira verificação que falhou aqui.
Porque é que a contagem ficava parada
A informação de falhas vem da lista de eventos de cada máquina: arranques, saídas, reinícios. A plataforma guarda apenas os últimos cinco eventos por máquina.
Imagina uma app que se avaria 2,5 segundos após cada arranque, como uma delas fazia. Cada ciclo acrescenta uma falha e um arranque. Ao fim de alguns ciclos, a lista está cheia. A partir daí, cada novo evento empurra um antigo para fora. A lista avança, mas mantém sempre aproximadamente a mesma mistura: duas falhas e dois arranques.
Por isso, "quantas falhas desde o início da publicação" devolvia o mesmo número em cada verificação. A segunda verificação via, digamos, duas falhas. A terceira também via duas: falhas diferentes, mas a mesma contagem. A verificação concluía que as falhas tinham parado e aprovava a publicação.
Isso aconteceu três vezes a 25 de setembro, em duas apps. Em cada caso:
- a publicação ficou marcada como ativa,
- nenhum rollback foi executado, porque nada tinha falhado,
- o nosso reconciliador, um job em segundo plano que verifica as implementações depois do facto, falhou a implementação alguns minutos mais tarde,
- e o site inteiro passou a devolver 404.
Uma publicação que falha a tempo é recuperável: o rollback repõe a versão anterior. Uma publicação que falha alguns minutos tarde, depois de ter sido declarada um sucesso, já saltou o seu rollback.
Como o descobrimos
Reproduzimos localmente: pegámos numa API a funcionar, deixámo-la adormecer e depois republicámo-la com uma alteração que se avariava ao arrancar. O health check marcou-a como ativa.
Isso correspondia à produção: nas três publicações, a app morria segundos após cada arranque, mas a contagem de falhas que o health check comparava nunca subia entre verificações.
A correção: comparar a falha mais recente, não a contagem
Em vez de contar falhas, o health check passa a olhar para a hora da falha mais recente desde o início da publicação.
Se a segunda verificação mostrar qualquer falha desde o início da publicação, o check espera e faz uma terceira verificação. Se a falha mais recente na terceira verificação for posterior à falha mais recente da segunda, a máquina voltou a avariar-se entretanto. Não está a manter-se de pé, e a publicação falha com uma mensagem a dizer que a máquina se avariou repetidamente desde a publicação e continua a reiniciar.
Isto funciona independentemente de quão curta é a lista de eventos. Uma janela deslizante de cinco eventos pode esconder quantas falhas houve, mas não pode esconder que acabou de acontecer uma nova: o evento mais recente é sempre o que fica guardado.
Com esta alteração, a reprodução local comporta-se corretamente: a publicação falha e a imagem anterior é reposta. Existe também um teste que simula o comportamento da plataforma (cada verificação acrescenta uma falha e um arranque e corta a lista para cinco) e verifica que o veredicto é uma falha, não uma aprovação.
O que retirámos disto
- Conhece a forma dos dados que estás a contar. Uma contagem sobre uma lista limitada deixa de significar algo quando a lista está cheia. O limite não estava escondido; simplesmente não tínhamos desenhado a verificação a contar com ele.
- Prefere "aconteceu algo novo?" a "quantas coisas aconteceram?". O carimbo temporal mais recente sobrevive ao truncamento. Um total não.
- Uma falha tardia é pior do que uma falha precoce. A mesma compilação avariada é inofensiva se a publicação falhar e repuser a versão anterior, mas derruba um site se for declarada um sucesso primeiro.
- Reproduz nas condições reais. O erro exigia uma máquina que já se tivesse avariado vezes suficientes para encher a sua lista de eventos. Um único crash rápido num teste não o teria mostrado.
Esta correção complementa outra do mesmo dia, que garantiu que o rollback repõe a versão antiga em vez de a apagar: uma republicação falhada nunca apaga um backend que funciona. Se a tua própria app não consegue publicar porque se avaria ao arrancar, a documentação de resolução de problemas de publicação e o nosso guia sobre como depurar uma app criada com IA são bons pontos de partida.