Executando verificação de segurança...
14

K6, Vamos testar sua API!

Esse post é uma tradução do meu conteúdo publicado no substack https://andredemattosferraz.substack.com/p/k6-lets-test-our-api

Teste de carga (Load Test)

O teste de carga se preocupa principalmente com a avaliação de desempenho.

Você deve executar um teste de carga para:

  • Avalie o desempenho atual do seu sistema sob carga típica e de pico.
  • Certificar de continuar atendendo aos padrões de desempenho ao fazer alterações no seu sistema (código e infraestrutura).

Virtual Users = simulação de um usuário

O gráfico VU (Virtual Users) de um teste de carga típico é semelhante a este

LoadTest

Se o seu sistema travar sob um teste de carga, significa que seu teste de carga funcionou como um teste de estresse. E seu sistema não está suportando o número de usuários que você deseja.

Teste de estresse (Stress Test)

O teste de estresse está relacionado à estabilidade do sistema sob condições extremas

Normalmente, você deseja fazer um teste de estresse em uma API ou site para determinar:

  1. Como seu sistema se comportará sob condições extremas.
  2. Qual é a capacidade máxima do seu sistema em termos de usuários ou performance.
  3. O ponto de ruptura do seu sistema e seu modo de falha.
  4. Se o seu sistema se recuperará sem intervenção manual após o término do teste de estresse.

Ao testar o estresse, você configura o teste para incluir mais usuários simultâneos ou gerar maior rendimento do que:

  • Seu aplicativo normalmente vê.
  • Você acha que seu aplicativo pode lidar com isso.

Um exemplo clássico da necessidade de testes de estresse é a “Black Friday”

O gráfico VU de um teste de estresse deve ser semelhante a este:

StressTest

Teste de saturação (Soak Test)

O teste de saturação se preocupa com a confiabilidade ao longo de um longo período de tempo.

Problemas de confiabilidade geralmente estão relacionados a bugs, vazamentos de memória, armazenamento insuficiente, configuração incorreta ou falhas de infraestrutura. Problemas de desempenho geralmente estão relacionados a performance de banco de dados, vazamentos de memória, vazamentos de recursos ou uma grande quantidade de dados.

Com um teste de saturação, você pode simular dias de tráfego em apenas algumas horas.

Normalmente, você executa este teste para:

  1. Verifique se o seu sistema não sofre de bugs ou vazamentos de memória, que resultam em travamento ou reinicialização após várias horas de operação.
  2. Verifique se as reinicializações esperadas do aplicativo não perdem solicitações.
  3. Encontre bugs relacionados a "race condition" que aparecem esporadicamente.
  4. Certificar de que seu banco de dados não esgote o espaço de armazenamento alocado e pare.
  5. Certificar de que seus logs não esgotem o armazenamento em disco alocado.
  6. Certificar de que os serviços externos dos quais você depende não parem de funcionar após uma certa quantidade de solicitações serem executadas.

Race condition é um problema comum que acontece em programação concorrente, onde duas threads modificam o mesmo recurso simultaneamente.

O gráfico VU de um teste de imersão deve ser semelhante a este:

SoakTest

O teste de saturação ajuda você a descobrir bugs e problemas de confiabilidade que surgem ao longo de um período prolongado. Muitos sistemas complexos têm bugs dessa natureza.

Você deve executar testes de saturação depois que seus testes de carga padrão forem bem-sucedidos e seu sistema for considerado estável ao executar um teste de estresse.

Compreendendo o código k6

O K6 usa linguagem javascript para definir (escrever) os testes.

Veja a compatibilidade aqui: https://k6.io/docs/using-k6/javascript-compatibility-mode

O exemplos a seguir podem ser encontrados no repositório https://github.com/mfandre/k6-tutorial.

Se você abrir algum teste na pasta "tests", verá isto:

export const options = {
  stages: [
    { duration: '10s', target: 200 }, // simulate ramp-up of traffic from 1 to 200 users over 10s.
    { duration: '30s', target: 200 }, // stay at 200 users for 30 seconds
    { duration: '10s', target: 0 }, // ramp-down to 0 users
  ],
  thresholds: {
    'http_req_duration': ['p(99)<1500'], // 99% of requests must complete below 1.5s
    'checks{myTag:status200less1500}': ['rate>0.9999'],
  },
};

Este bloco define como o k6 aumentará e diminuirá o número de VUs e quanto tempo será gasto em cada fase. Você pode interpretar VU como um usuário que está interagindo com sua API.

Agora, imagine que você tem uma página da web que, quando algum usuário entra, 5 solicitações são enviadas ao serviço de backend. Essas solicitações são solicitações ajax que serão executadas em paralelo e obterão alguns dados do banco de dados. Você pode definir esse comportamento aqui:

const BASE_URL = 'http://<seu_domain>:5000';

export default () => {
    let urls = [
        ['GET', `${BASE_URL}/health_check`, null],
        //['GET', `${BASE_URL}/long_duration`, null],
        ['GET', `${BASE_URL}/short_duration`, null],
    ]

    const responses = http.batch(urls);
    for (let i = 0; i < responses.length; i++) {
        check( 
            responses[i], {
                'status was 200': (res) => res.status === 200,
            },
            { myTag: 'status200less1500' }
        );
    }
    sleep(1);
};

Esses blocos juntos formam a lógica de como seu teste será aplicado e quanto mais próximo de como o usuário interage com sua API, mais precisos serão os resultados em comparação com o mundo real.

Agora vamos brincar pra valer!

Vamos executar uma API Python Flask simples com 3 endpoints:

  1. health_check
  2. long_duration
    • Endpoint with a longer process
  3. short_duration
    • Endpoint with a short process

Usaremos o k6 para testar esta aplicação.

Configuração e rodando a API

Instale o k6

https://k6.io/docs/getting-started/installation/

Dentro da pasta API do projeto vamos instalar Pyenv e as dependências do projeto:

https://github.com/pyenv/pyenv?tab=readme-ov-file#installation
pyenv install 3.9.0
python -m venv venv
source venv/bin/activate
pip install -r requirements.txt 
python api.py

Executando os teste do k6

k6 run load_test.js
k6 run stress_test.js

Execuçao_1

Alguns erros podem aparecer se sua API não estiver suportando a carga que você configurou:

Execuçao_2

Resultados

Se sua API estiver suportando sua carga, você provavelmente verá um resultado como este:

Resultado_ok

Caso contrário, você receberá algumas requisições com falha:

Resultado_fail

É importante variar o número de VUs e o tempo do teste até que você consiga entender como sua API irá se comportar em tais casos. No exemplo acima, você notará que a API sofre para lidar com 400 VUs e apresenta falhas em 32% das requisições.

No seu caso de uso, sua API deve lidar com quantos usuários? Quantas request ela deve suportar? A duração das request devem se manter estáveis entre 0-500ms?

Para mais posts assim, fique ligado no substack e tabnews

É importante lembrar que a performance da sua API está diretamente ligada com a performance da máquina que está hospedando-a. Desta forma o resultado do teste local pode ser completamente diferente do ambiente produtivo. Rode os testes em um ambiente que seja muito próximo ou igual ao seu ambiente produtivo para ter resultado REAIS.

Carregando publicação patrocinada...
3
2

Teste de carga e estresse, pra mim era igual caviar. As empresas que atuei sempre falavam desses testes, mas raramente ou nunca implementaram na prática, até por causa dos custos gerados.

Vou orar por mais posts assim como o seu por aqui.

1