Cloud Intelligence™Cloud Intelligence™

Cloud Intelligence™

Firestore: a regra 500/50/5 e como testá-la na prática

By Matthias BaetensOct 28, 20246 min read

Esta página também está disponível em English, Deutsch, Español, Français, Italiano e 日本語.

No mundo dos bancos NoSQL na nuvem, o Firestore se destaca como uma solução flexível e escalável para desenvolvimento mobile, web e server-side. Mesmo com todos os seus recursos, existe um mito comum de que o Firestore aguenta qualquer carga sem suar a camisa. Em teoria, sim — mas, na prática, a história tem nuances. Imagine que você está lançando um novo recurso e já enfrentou grandes picos de tráfego antes. Ou que conhece bem o comportamento dos seus usuários e sabe que, em determinado horário do dia, a carga aumenta drasticamente. Neste post, vamos explorar um problema comum de escalabilidade no Firestore e mostrar uma forma prática de testá-lo e evitá-lo.

A regra 500/50/5: uma introdução tranquila ao Firestore

O Firestore foi feito para escalar, mas, como qualquer sistema elástico e distribuído, precisa de tempo para se ajustar a cargas crescentes. É aí que entra a regra 500/50/5:

Comece com no máximo 500 operações por segundo em uma nova coleção e aumente o tráfego em 50% a cada 5 minutos.

Essa orientação garante que os mecanismos internos de escalonamento do Firestore consigam acompanhar o seu crescimento, evitando problemas comuns como alta latência ou erros DEADLINE_EXCEEDED.

Conheça o k6: seu aliado nos testes de carga

Para mostrar a importância da regra 500/50/5, criamos um script com o k6, uma ferramenta open-source de testes de carga. O k6 é uma ótima escolha por vários motivos:

  • É simples de usar, com linguagem de scripting baseada em JavaScript.
  • Oferece métricas de performance em tempo real e insights detalhados.
  • É altamente escalável, capaz de gerar de 100.000 a 300.000 requisições por segundo a partir de uma única instância.

O script

Você encontra o script aqui. Veja uma visão geral do que ele faz:

Carga inicial e alvo:

  • Começa em 500 requisições por segundo (RPS)
  • Tem como meta atingir 1500 RPS (você pode ajustar esse valor, claro)

Estratégia de ramp-up:

  • Mantém cada nível de carga por 5 minutos (300 segundos)
  • Aumenta a carga em 50% em períodos de 1 minuto
  • Repete o padrão até atingir ou ultrapassar o RPS alvo

Geração dinâmica de stages:

  • Calcula automaticamente quantas stages são necessárias
  • Cria uma série alternada de stages "estáveis" e de "ramp-up"
  • Registra o RPS alvo e a duração de cada stage para deixar tudo claro

Seleção de documentos:

  • Lê os IDs de documento a partir de um arquivo ("orders.txt")
  • Seleciona aleatoriamente um ID de documento para cada requisição
  • Você vai precisar obter esses IDs para o seu próprio caso de uso, já que aqui usei um conjunto de dados fictício

Execução das requisições:

  • Faz requisições GET para a REST API do Firestore
  • Inclui autenticação via bearer token
  • Também incluí um script que gera o token para você

Monitoramento de performance:

  • Acompanha leituras bem-sucedidas e erros
  • Registra qualquer status code diferente de 200, com detalhes

Você pode rodar o script com k6 run warm-up.js depois de instalar o k6 (via brew, por exemplo, se estiver no Mac). Para gerar um token, use generate-firebase-token.py. Em ambos os scripts há algumas variáveis para atualizar — use o "Localizar" do seu editor e procure por INSERT.

O experimento: sucesso vs. falha

Rodamos dois experimentos para mostrar o impacto de seguir (ou ignorar) a regra 500/50/5, para você ver a diferença na prática:

Experimento 1: receita para o fracasso

Neste cenário, começamos com 2000 requisições por segundo (RPS) e subimos para 2500 RPS em 5 minutos, ignorando completamente a regra 500/50/5.

```js
// Warmup parameters
const initialRPS = 2000;
const targetRPS = 2500;
const stablePeriodSeconds = 300; // 5 minutes
const rampPeriodSeconds = 0;
const stageCount = Math.ceil(Math.log(targetRPS / initialRPS) / Math.log(1.5));
```
Executado em 1/1/10, entre 01h10 e 01h15 CEST
Resultados:
```bash
INFO[0335] Warmup Stages:                                source=console
INFO[0335] Stage 1: Target RPS: 2500, Duration: 300s     source=console
     ✗ status is 200
      ↳  4% — ✓ 6408 / ✗ 123474
     checks.........................: 4.93%  ✓ 6408       ✗ 123474
     data_received..................: 23 MB  70 kB/s
     data_sent......................: 4.6 MB 14 kB/s
     dropped_iterations.............: 1      0.003028/s
     errors.........................: 123474 373.866077/s
     http_req_blocked...............: avg=473.49ms min=0s       med=0s     max=59.9s  p(90)=0s     p(95)=0s
     http_req_connecting............: avg=287.6ms  min=0s       med=0s     max=38.39s p(90)=0s     p(95)=0s
     http_req_duration..............: avg=806.08ms min=0s       med=0s     max=1m3s   p(90)=0s     p(95)=2.01s
       { expected_response:true }...: avg=12.62s   min=311.44ms med=9.48s  max=1m0s   p(90)=30.92s p(95)=36.56s
     http_req_failed................: 95.06% ✓ 123474     ✗ 6409
     http_req_receiving.............: avg=82.85ms  min=0s       med=0s     max=59.4s  p(90)=0s     p(95)=30µs
     http_req_sending...............: avg=651.35µs min=0s       med=0s     max=8.82s  p(90)=0s     p(95)=92µs
     http_req_tls_handshaking.......: avg=261.72ms min=0s       med=0s     max=57.6s  p(90)=0s     p(95)=0s
     http_req_waiting...............: avg=722.58ms min=0s       med=0s     max=1m2s   p(90)=0s     p(95)=1.89s
     http_reqs......................: 129883 393.271845/s
     iteration_duration.............: avg=32.65s   min=2.58µs   med=33.98s max=1m12s  p(90)=48.47s p(95)=51.64s
     iterations.....................: 129883 393.271845/s
     successful_reads...............: 4.93%  ✓ 6408       ✗ 123474
     vus............................: 47     min=0        max=25000
     vus_max........................: 25000  min=4179     max=25000
running (5m30.3s), 00000/25000 VUs, 129882 complete and 21 interrupted iterations
firestore_warmup ✓ [======================================] 00021/25000 VUs  5m0s  2105.47 iters/s
```

Veja como isso aparece no Key Visualiser:

O resultado? Taxa de sucesso abaixo de 5%. Doeu.

Experimento 2: caminho para o sucesso

Neste teste, seguimos a regra 500/50/5, começando em 500 RPS e subindo gradualmente até 1500 RPS ao longo de cerca de 20 minutos.

```js
// Warmup parameters
const initialRPS = 500;
const targetRPS = 1500;
const stablePeriodSeconds = 300; // 5 minutes
const rampPeriodSeconds = 60; // 1 minute
const stageCount = Math.ceil(Math.log(targetRPS / initialRPS) / Math.log(1.5));
```

Executado em 1/1/10, entre 01h40 e 01h58 CEST

Resultados:
```bash
INFO[1111] Warmup Stages:                                source=console
INFO[1111] Stage 1: Target RPS: 500, Duration: 300s      source=console
INFO[1111] Stage 2: Target RPS: 750, Duration: 60s       source=console
INFO[1111] Stage 3: Target RPS: 750, Duration: 300s      source=console
INFO[1111] Stage 4: Target RPS: 1125, Duration: 60s      source=console
INFO[1111] Stage 5: Target RPS: 1125, Duration: 300s     source=console
INFO[1111] Stage 6: Target RPS: 1500, Duration: 60s      source=console

     ✗ status is 200
      ↳  99% — ✓ 863739 / ✗ 231

     checks.........................: 99.97% ✓ 863739     ✗ 231
     data_received..................: 1.5 GB 1.4 MB/s
     data_sent......................: 173 MB 156 kB/s
     dropped_iterations.............: 20999  18.915827/s
     errors.........................: 231    0.208084/s
     http_req_blocked...............: avg=50.75ms  min=0s       med=0s       max=41.09s p(90)=1µs      p(95)=1µs
     http_req_connecting............: avg=35.83ms  min=0s       med=0s       max=29.93s p(90)=0s       p(95)=0s
     http_req_duration..............: avg=554.84ms min=0s       med=334.66ms max=1m0s   p(90)=728.85ms p(95)=1.29s
       { expected_response:true }...: avg=554.07ms min=304.44ms med=334.66ms max=59.46s p(90)=728.84ms p(95)=1.29s
     http_req_failed................: 0.02%  ✓ 231        ✗ 863739
     http_req_receiving.............: avg=68.05ms  min=0s       med=6.92ms   max=59.42s p(90)=21.82ms  p(95)=160.12ms
     http_req_sending...............: avg=288.4µs  min=0s       med=32µs     max=12.11s p(90)=89µs     p(95)=150µs
     http_req_tls_handshaking.......: avg=16.93ms  min=0s       med=0s       max=46.68s p(90)=0s       p(95)=0s
     http_req_waiting...............: avg=486.5ms  min=0s       med=327.66ms max=1m0s   p(90)=615.6ms  p(95)=943.67ms
     http_reqs......................: 863970 778.261194/s
     iteration_duration.............: avg=609.81ms min=2.2µs    med=334.94ms max=1m0s   p(90)=748.42ms p(95)=1.35s
     iterations.....................: 863970 778.261194/s
     successful_reads...............: 99.97% ✓ 863739     ✗ 231
     vus............................: 14     min=14       max=5720
     vus_max........................: 5849   min=1000     max=5849

running (18m30.1s), 00000/05849 VUs, 863970 complete and 14 interrupted iterations
firestore_warmup ✓ [======================================] 00014/05849 VUs  18m0s  1499.93 iters/s
```

Veja como isso aparece no Key Visualiser:

Início do escalonamento

Fim do escalonamento

O resultado? Uma taxa de sucesso impressionante de 99,97%.

Rodando seus próprios testes

Dá para rodar o script com o k6 localmente, o que traz algumas vantagens, como configuração simples e custo zero. Por outro lado, você pode esbarrar nos limites de recursos da sua máquina, ficar restrito a uma única instância e ter os resultados afetados pela sua rede. Para resultados mais precisos, pode fazer sentido rodar o script em uma VM (no Google Cloud).

A regra 500/50/5 não é só uma sugestão — é uma orientação fundamental para que sua implementação do Firestore escale de forma suave e eficiente. Seguindo essa regra e usando ferramentas como o k6 para testar suas estratégias de escalonamento, você evita armadilhas de performance e mantém sua aplicação rodando bem à medida que cresce.

Lembre-se: quando o assunto é escalar bancos de dados, devagar e sempre é o que vence a corrida. Bom escalonamento!

— -

Quer se aprofundar no escalonamento do Firestore ou precisa de ajuda para otimizar sua infraestrutura na nuvem? Acesse doit.com e descubra como podemos ajudar você a extrair o máximo do potencial da sua nuvem.