Sempre que o assunto é performance de APIs, o uso de cache é uma das soluções mais abordadas. Ao utilizar cache para armazenar respostas estáticas (ou que mudam com pouca frequência), podemos reduzir a carga do servidor e aumentar os tempos de resposta, o que o torna uma solução essencial para a otimização do desempenho.
Há muitas maneiras de implementar cache, desde a camada de aplicação (no código) até a camada de infraestrutura. Hoje vamos testar uma das maneiras mais fáceis de fazer isso: ativando-o diretamente no Amazon API Gateway. Esse método exige pouco esforço de configuração e nenhuma atualização de código, mas será que ele realmente faz diferença? Vamos testar!
Ambiente de testes
A arquitetura da aplicação utilizada no teste será composta por uma Lambda que simulará a API consumida e uma API criada no Amazon API Gateway com dois stages: cache, para o teste com cache ativado e nocache, para o teste com o cache desativado (é, eu sei, óbvio né? xD).
A API (lambda) retorna sempre o mesmo body e tem um tempo de resposta entre 300ms e 600ms para simular uma API real. Já no API Gateway, o TTL (Time to live) do cache foi configurado com 180s (3 minutos).
Código da lambda:
import time
import random
def lambda_handler(event, context):
# Gera uma espera entre 300ms (0.3s) e 600ms (0.6s)
delay = random.uniform(0.3, 0.6)
# Adiciona o delay no código
time.sleep(delay)
# Retorna a mensagem de sucesso.
return {
'statusCode': 200,
'body': 'Success',
}
Como o terraform para criação dos recursos é extenso, deixei o código disponível no GitHub.
O teste de carga
Primeiro, temos que definir as condições dos testes:
- O teste tem um limite de execução de 10 minutos
- O numéro de Usuários Virtuais (VU) é de 990 para ser equivalente a quantidade da concorrência provisionada na Lambda e evitar throttling.
- Foi adicionado um delay de 200ms entre cada request para o mesmo usuário, para simular o uso “real” de uma API.
- O teste terminará assim que 1M de requests forem atingidos OU o tempo acabar.
Com as condições definidas, é hora de criar o código para o teste. Neste cenário, iremos usar o k6, ferramenta de testes de carga open-source do Grafana Labs.
import http from 'k6/http';
import { sleep } from 'k6';
// Le o path da API via variável de ambiente
const API_PATH = __ENV.API_PATH;
const BASE_URL = 'https://<API_GATEWAY_ID>.execute-api.<REGION>.amazonaws.com';
const API_URL = BASE_URL + API_PATH;
// Define as condições do teste
export let options = {
duration: '10m',
vus: 990, // Number of Virtual Users
iterations: 1000000, // Total number of requests
};
export default function () {
// Faz a chamada GET as APIs
http.get(API_URL);
// Opcional: Adiciona um delay para simular o mundo real.
sleep(0.2);
}
Primeiro teste (cache desativado):
O primeiro teste levou 9m17s para atingir 1M de requisições com um throughput de ~1792 requisições por segundo e um tempo de resposta médio de ~552ms. As requisições foram distribuídas desta maneira durante o teste:
** Aparentemente, as métricas do Lambda chegam ao CloudWatch mais rapidamente do que as métricas do API Gateway, o que dá a impressão de que o Lambda começou primeiro, mas não começou.
Segundo teste (com cache ativado):
O segundo teste levou 5m52s para atingir 1M de requisições com throughout de ~2923 requisições por segundo e um tempo de resposta médio de ~338ms. As requisições foram distribuídas desta maneira durante o teste:
** Aparentemente, as métricas do Lambda chegam ao CloudWatch mais rapidamente do que as métricas do API Gateway, o que dá a impressão de que o Lambda começou primeiro, mas não começou.
Resumo e conclusões
- Throughput: 1792,42 requsições/seg vs 2922,82 requsições/seg ( ⬆️ ˜63% mais alto com cache)
- Tempo de resposta médio: 551,88ms vs 338,27ms ( ⬆️ ~39% mais rápido com cache)
- Backend API invocations (lambda): 1M invocações vs 5 invocações ( ⬆️ ~99.8% menos invocações com cache)
Com base nos dados, a implementação do cache proporciona melhorias substanciais no desempenho com pouco esforço de implementação. Outra nota importante é que a implementação de cache não significa necessariamente um aumento nos custos, pois menos chamadas serão feitas para o backend da API, o que pode levar a uma redução nos custos nessa camada. Nosso teste é um bom exemplo disso, já que o lambda é cobrado por invocações e ~98% menos invocações foram feitas com o cache ativado.
Qualquer dúvida, é só dizer 🙂
Descubra mais sobre contains(cloud)
Assine para receber nossas notícias mais recentes por e-mail.
Seja o primeiro a comentar