top of page

Faxina feita: como finalmente usamos o cache do GitHub direito 🧹

Foto do escritor: Marcel Dütscher
Marcel Dütscher
27 de jul.
4 min de leitura

No último post de tecnologia medimos quanto o nosso pipeline de build e verificação realmente custa e descobrimos: não é dinheiro, é tempo de espera e armazenamento. O artigo terminava com “a faxina está planejada, mas ainda não foi feita”. Foi mais rápido do que esperávamos: desde o meio-dia de hoje ela está na branch principal. Aqui estão os números.


Primeiro o resultado. O uso de cache do nosso repositório caiu de 5,32 GB em 63 entradas para 2,46 GB em 13. A análise de segurança de C# que rodava em todo pull request agora leva 4 segundos em vez de 4 minutos e meio, e continua verificando tudo o que vai para o jogo. Tudo isso sem abrir mão de nenhuma verificação que protege vocês.


Resumindo: para que serve um cache? Todo build começa em uma máquina nova e vazia. Para que ele não comece do zero toda vez, pode guardar resultados intermediários: da próxima vez, baixa esses resultados em vez de recriá-los. Vocês têm 10 GB por repositório para isso. Quando enche, o GitHub descarta o que ficou mais tempo sem uso. E esse era exatamente o nosso problema: o espaço estava ocupado, só que com as coisas erradas.


Erro 1: uma chave sem plataforma. O Unity cria uma pasta intermediária enorme ao fazer o build. Quatro builds a usam (Windows, Linux, macOS, WebGL) e cada um precisa da sua. O build do Windows era o único cuja chave de cache não tinha marcação de plataforma. Por isso ele pegava a primeira entrada disponível, que era a do WebGL: 1,7 GB baixados, que o Unity então reconhecia corretamente como “não pertence aqui” e importava tudo do zero. No log: 257 segundos de importação em vez de 85. A chave agora é Library-Windows-, e o fantasma acabou.


Erro 2: ninguém nunca salvou. A descoberta mais vergonhosa. Os quatro builds tentavam direitinho restaurar a pasta intermediária, mas nenhum workflow jamais a gravava. A única entrada que existia veio de uma ferramenta que se inicia à mão. Ou seja, o cache mais importante do projeto era mantido por acaso. 🙈 Agora ele é gravado de propósito.


A decisão que economiza espaço: só o caminho crítico conta. O mais óbvio seria guardar cache das quatro plataformas. Olhar os tempos de um release real mostra por que isso seria besteira: os quatro builds começam ao mesmo tempo, o Windows termina em 11 minutos, Linux e macOS em 13,7 e o WebGL em 31,6. Enquanto o WebGL está rodando, os outros esperam de qualquer jeito. Então só o cache dele encurta a espera; os outros três teriam custado cerca de 4,5 GB dos nossos 10 GB e economizado exatamente zero minutos. Por isso guardamos só um.


Erro 3: o Docker comeu o resto. Ao construir as imagens do nosso servidor, todas as camadas intermediárias eram guardadas como cache: cerca de 3,27 GB em 24 entradas, 64% do orçamento inteiro, atualizadas a cada push. Essa enxurrada expulsava com regularidade o único cache do Unity que importa. Agora só o estágio final é guardado, e cada uma das nossas quatro imagens tem o próprio espaço de nomes; antes elas dividiam um só e viviam se expulsando. Os 3,27 GB viraram cerca de 150 MB.


Análise de segurança: verificar onde importa. O CodeQL, a ferramenta do GitHub para encontrar falhas de segurança, era o nosso segundo maior consumidor de tempo: 230 minutos em oito dias, quase tudo na parte de C#, que reconstrói o projeto inteiro para a análise, a cada pull request. Agora ela roda completa ao fazer merge na branch principal e uma vez por semana; os pull requests mantêm as verificações rápidas. Medido depois da mudança: 4 segundos em um pull request, e uns bons 4 minutos, iguais a antes, na branch principal. Assim, toda linha que realmente chega até vocês continua sendo verificada, só que não três vezes.


Pequenas coisas que somam. Os pacotes do .NET agora ficam em cache, mas só no caminho em que uma pessoa fica esperando. Um emulador para arquiteturas de processador estrangeiras só é configurado quando realmente construímos para uma delas. Um comando de limpeza que não tinha o que fazer em uma máquina recém-iniciada, mas custava até 1,4 minuto por build, foi embora. E os arquivos intermediários de um build de release agora são apagados depois de 7 dias em vez de 90: tinham se acumulado 703 deles, somando 7,4 GB.


O que deliberadamente não fizemos. Duas propostas da nossa própria análise foram descartadas depois de medir, em vez de vendidas como linhas bonitas em um changelog. Dividir os testes em dois jobs paralelos ganharia exatamente quatro segundos (uma parte leva 2:54, a outra 4 segundos) e renomearia uma verificação obrigatória, deixando todo pull request preso em “aguardando” para sempre. E pular o grande portão de testes antes de um release também não economizaria nada, porque ele termina bem antes de o WebGL acabar de construir. Uma otimização que não ganha nada, mas tira uma rede de segurança, não é otimização.


E aí um bug de verdade apareceu. No meio dessa faxina, de repente todo pull request falhava por limite de tempo: um único teste levava 212 segundos quando o permitido são 120. O segundo mais lento da mesma execução: 10,5 segundos. A causa era um caminho de erro no nosso servidor que simplesmente cortava uma requisição HTTP quebrada em vez de encerrá-la direito. No Linux, o sistema só recolhe essas conexões em uma varredura que roda a cada dois minutos, e o desligamento ficava esperando por ela, pacientemente. No Windows isso nunca aparecia, e por isso só dava problema no servidor de build. Agora o servidor responde a esse caso com um código de erro honesto e fecha a conexão ele mesmo: o teste termina em uns bons 20 segundos, e quem joga de verdade e esbarrar nesse problema vai receber um erro compreensível em vez de uma conexão cortada. Um dia de faxina de CI que encontra um bug de produção é um bom dia. 🐛


Nada disso muda algo diretamente para vocês: nenhum bloco novo, nenhum animal novo. Só encurta o caminho entre “programação pronta” e “instalável para vocês”. E prova a mesma frase da última vez: primeiro medir, depois mexer. 🚀

Posts recentes

Ver tudo

Comentários


Não é mais possível comentar esta publicação. Contate o proprietário do site para mais informações.
bottom of page