Testes de stress e porquê fazê-los em produção
Apresentando Faustão, o framework de load test do PicPay
É provável que pelo menos uma vez na vida você já tenha se encontrado em uma empresa que precisava de escalar bastante para atender alta demanda, geralmente envolvendo sistemas legados e bases de dados antigas, mal configuradas. É onde estávamos em 2019, ao sermos surpreendidos com a notícia: A empresa iria patrocinar um grande programa de televisão, com alta audiência. Isso, é claro, significaria que o apocalipse estava batendo à nossa porta.
PicPay é uma fintech com grandes aspirações. Provemos serviços financeiros na forma de uma carteira digital, uma store aberta onde pessoas podem contratar serviços e bens digitais, sistema de pagamentos de contas/boletos, transações P2P sem custo aos usuários, transações com lojistas, e-commerce e diversos outros serviços. Neste cenário, num país de proporções continentais como o Brasil, temos que escalar para atender as demandas de todos os usuários e suas necessidades. Enquanto escrevo este texto, temos mais de 20 milhões de usuários (e crescendo!), e movimentamos bilhões de reais todos os meses.
Esclarecendo um pouco mais da nossa posição, a empresa tinha começado a migração para uma plataforma de micro serviços a alguns anos, com algumas funções importantes já fora do monolito legado. Porém o core transacional do nosso sistema financeiro ainda estava profundamente enraizado neste, e precisaria de pelo menos vários meses para ser separado.
A situação pedia pela criação do nosso primeiro squad 100% funcional, alguns dos melhores desenvolvedores e SRE’s foram reunidos com uma missão: tornar tudo muito mais escalável. Para aprofundar nos aspectos gerenciais, o squad recebeu um alvo direto no OKR de usuários online, no primeiro ciclo da implantação de OKRs da companhia.
Contextualizando o momento, estávamos no meio da migração dos microserviços para kubernetes, vindo do nosso sistema caseiro de orquestração (feito a base de bastante cafeína). Todos os serviços já funcionavam em docker, então a migração fluía de forma suave para a terra dos HPAs e da infra monitorada por Prometheus.
Como o squad foi criado oficialmente, a primeira coisa a ser definida era uma meta alvo. Naquele momento, a empresa era capaz de sobreviver a picos de tráfego de X, recebemos então a modesta meta de 3X. Como um fato engraçado, na época utilizávamos o Google Analytics para medir usuários online no nosso app. E então ele foi descontinuado. Provavelmente o google só fez isso para tornar nosso desafio mais glorioso.
Com o alvo definido, o time começou a se mover. No começo tivemos que configurar ferramentas de monitoramento e APM para entender instabilidades em nossos maiores sistemas e criar o primeiro backlog. Nas primeiras sprints já eram visíveis grandes melhorias nos nossos endpoints mais críticos, mas o time de marketing precisava de aumentar incrementalmente o número de usuários online para que se tornassem visíveis novos alvos de melhoria. Certamente que marketing não é tão fácil de escalar como tecnologia e acabamos perdendo visibilidade. Então veio a ideia de começar a fazer load tests em produção, para mostrar as futuras falhas do sistema.
A busca por ferramentas de teste de carga nos levou ao Locust, não apenas uma ferramenta open source, mas também funciona como um sistema distribuído. Os primeiros testes foram tímidos, enquanto aumentava o entendimento do time sobre a ferramenta e gerava conhecimento de como escrever testes e buscar resultados. Cadastrar alguns users e fazer login era o máximo que fazíamos.
Depois de conseguir um engenheiro de software mobile para mapear todas as requests na jornada do usuário, os testes foram bem enriquecidos. O time saiu de apenas cadastros para envio de emails falsos e sms para inserir delays no sistema, coleta de vários contatos e envio em massa, recarregar o feed diversas vezes, curtir transações, buscas aleatórias. O maior objetivo era simular o comportamento do usuário mais complexo possível.
A pergunta principal que recebemos é: por que testar em produção e não no ambiente de staging ou algo mais preparado para isso? Esta era uma idéia válida nos primeiros rascunhos do plano, mas para criar todo o ambiente, carregar dados, configurar algumas proxies com parceiros, etc, poderia levar várias horas e se tornar altamente custoso. Certamente que criar outro cluster de kubernetes e copiar manifestos seria rápido, mas muita da infraestrutura ainda era legada, sem IaC, sem gerenciamento de configuração, sem testes e com diversos ajustes para criar as coisas tão rápido quanto foram necessárias no passado. Bancos de dados também se provariam absurdamente complexos para configurar, já que foram muito customizados e tinham enormes datasets. VPC Peerings, VPNs, tudo poderia ser um inimigo da verdade neste cenário.
Provado que era impossível testar no ambiente de staging, o time direcionou os esforços para testes de produção. Branches de vários serviços foram criadas e configuradas com bypasses, conseguimos bênçãos do compliance e dados, foi escolhido um período para rodar os testes com o mínimo de usuários possível e diversas pessoas ficaram disponíveis para ressuscitar o qualquer coisa que pudesse precisar (Isto se provou bem crítico, já que mais de uma vez matamos algumas bases de dados e os DBAs precisaram logar e agir imediatamente). Os primeiros testes não foram nada suaves, como era necessário entender algumas coisas ao vivo (trocar as asas enquanto o avião voava). Boa parte das ações era bem manual, como escalar e desescalar workers, deployar alterações ao vivo nos testes, iniciar e parar swarming em muitos cenários de uma vez, coletar os dados para serem enviados num grupo do Slack e ainda tirar screenshots de diversas métricas de APM para serem atacadas nas próximas sprints.
A prova de conceito foi de acordo com o planejado e gerou várias métricas trabalháveis e backlog para diversas sprints futuras. Como o plano era executar estes testes de forma agendada para buscar por melhorias e re-criar o backlog do time, o trabalho manual deveria ser removido (é pouco sabido, mas boa parte dos engenheiros também precisa dormir). Foi então que surgiu a ideia do Faustão.
(para gerações futuras:) Faustão é um programa de auditório brasileiro, que exibido por uma das maiores redes de televisão do país. O mesmo canal que exibia o programa que gerou todo o nosso motivo de existir. Como ele era um cara bem grande, o nome pegou na ferramenta de load test como uma piada interna.
A ferramenta foi remodelada como um framework de load test completo. Os cenários independentes do locust foram feitos cada um em um Helm chart, que poderia ser deployado no kubernetes e escalado separadamente. Também foram adicionados uma base de dados Aurora Serverless para salvar os resultados e configuração, uma ferramenta de CI para geração de imagens, os repositórios foram separados para permitir que todos na empresa pudessem escrever testes e executar de forma agendada e também foi criado um orquestrador para executar os testes baseado em arquivos de configuração, este funcionava utilizando o Apache Airflow.
O funcionamento era o seguinte: os cenários eram parte de um trecho do funil, como registro, social, transações, etc. Como a carga era sempre distribuída entre eles, os testes eram divididos e executados em Locusts paralelos, como um conjunto de master-workers. Dividir em centenas tornou o teste altamente escalável, para que o time pudesse criar nodes on demand no nosso cluster apenas para atacar produção, com a carga tão alta quanto necessária.
O Faustão basicamente se executa sozinho, depois de escritos os testes e adicionada a configuração no repositório, todos os agendamentos são executados em série/paralelo de acordo com o alvo de usuários online, e então o time pode coletar resultados na manhã e criar um backlog de melhorias de acordo com o necessário (talvez no máximo acordar um DBA de plantão). Como um serviço, o framework incorpora diversas filosofias de arquitetura distribuída que tentamos empregar em todos os microserviços. É distribuído, escalável, tolerante a falhas e pode fazer o inferno em produção se precisar.
O próximo grande alvo é integrar o Faustão com ferramentas de engenharia de Chaos, criando o Evil Fausto, o próximo grande inimigo do time de SRE.