Cloud Intelligence™Cloud Intelligence™

Cloud Intelligence™

Firestore skalieren: die 500/50/5-Regel und wie Sie sie testen

By Matthias BaetensOct 28, 20246 min read

Diese Seite ist auch in English, Español, Français, Italiano, 日本語 und Português verfügbar.

Unter den NoSQL-Cloud-Datenbanken sticht Firestore als flexible und skalierbare Lösung für Mobile-, Web- und Server-Entwicklung hervor. Trotz dieser beeindruckenden Fähigkeiten hält sich ein hartnäckiger Irrtum: dass Firestore jede beliebige Last mühelos verkraftet. Theoretisch stimmt das – in der Praxis sieht es etwas differenzierter aus. Stellen Sie sich vor, Sie veröffentlichen Ihr neuestes Feature und hatten in der Vergangenheit bereits massive Traffic-Spitzen. Oder Sie kennen Ihre Nutzungsmuster genau und erwarten zu einer bestimmten Tageszeit einen drastischen Lastanstieg. In diesem Beitrag beleuchten wir ein typisches Skalierungsproblem von Firestore und zeigen Ihnen einen praxistauglichen Weg, es zu testen und zu vermeiden.

Die 500/50/5-Regel: ein sanfter Einstieg in Firestore

Firestore ist auf Skalierung ausgelegt, doch wie jedes elastische und verteilte System braucht es Zeit, um sich an steigende Lasten anzupassen. Genau hier kommt die 500/50/5-Regel ins Spiel:

Beginnen Sie mit maximal 500 Operationen pro Sekunde auf einer neuen Collection und steigern Sie den Traffic anschließend alle 5 Minuten um 50 %.

Diese Faustregel sorgt dafür, dass die internen Skalierungsmechanismen von Firestore mit Ihrem Wachstum Schritt halten und sich typische Probleme wie hohe Latenzen oder DEADLINE_EXCEEDED-Fehler vermeiden lassen.

Auftritt k6: Ihr Verbündeter beim Lasttest

Um die Bedeutung der 500/50/5-Regel zu veranschaulichen, haben wir ein Skript mit k6 erstellt, einem Open-Source-Tool für Lasttests. k6 ist aus mehreren Gründen eine ausgezeichnete Wahl:

  • Einfache Bedienung dank einer JavaScript-basierten Skriptsprache.
  • Performancemetriken in Echtzeit und detaillierte Auswertungen.
  • Hochgradig skalierbar – eine einzelne Instanz schafft 100.000 bis 300.000 Requests pro Sekunde.

Das Skript

Das Skript finden Sie hier. Ein Überblick, was es tut:

Anfangs- und Ziellast:

  • Startet bei 500 Requests pro Sekunde (RPS)
  • Strebt 1500 RPS an (selbstverständlich anpassbar)

Ramp-up-Strategie:

  • Hält jede Laststufe 5 Minuten (300 Sekunden) konstant
  • Steigert die Last innerhalb von 1-Minuten-Intervallen um 50 %
  • Setzt dieses Muster fort, bis die Ziel-RPS erreicht oder überschritten sind

Dynamische Stage-Generierung:

  • Berechnet automatisch die Anzahl der benötigten Stages
  • Erzeugt eine Abfolge abwechselnder \"Stable\"- und \"Ramp-up\"-Stages
  • Protokolliert Ziel-RPS und Dauer jeder Stage zur besseren Nachvollziehbarkeit

Dokumentauswahl:

  • Liest Dokument-IDs aus einer Datei (\"orders.txt\")
  • Wählt für jeden Request zufällig eine Dokument-ID aus
  • Sie müssen die Dokument-IDs für Ihren eigenen Anwendungsfall selbst beschaffen – ich habe für die Demo einen fiktiven Datensatz generiert

Request-Ausführung:

  • Führt GET-Requests gegen die Firestore-REST-API aus
  • Inklusive Authentifizierung per Bearer-Token
  • Ein Skript zum Erzeugen eines Tokens ist ebenfalls enthalten

Performance-Monitoring:

  • Erfasst erfolgreiche Reads und Fehler
  • Loggt alle Status-Codes ungleich 200 inklusive Details

Nach der Installation von k6 (z. B. via brew auf dem Mac) starten Sie das Skript mit k6 run warm-up.js. Ein Token holen Sie sich mit generate-firebase-token.py. In beiden Skripten müssen einige Variablen angepasst werden – nutzen Sie dazu die Suchfunktion Ihres Editors und suchen Sie nach INSERT.

Das Experiment: Erfolg vs. Misserfolg

Wir haben zwei Experimente durchgeführt, die zeigen, was passiert, wenn man die 500/50/5-Regel befolgt – oder eben nicht:

Experiment 1: Auf Misserfolg programmiert

In diesem Szenario starteten wir mit 2000 Requests pro Sekunde (RPS) und erhöhten innerhalb von 5 Minuten auf 2500 RPS – die 500/50/5-Regel wurde dabei vollständig ignoriert.

```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));
```
Ran between 1/1/10 between 0110 and 0115 CEST
Results:
```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
```

So sieht das im Key Visualiser aus:

Das Ergebnis? Eine Erfolgsquote unter 5 %. Autsch.

Experiment 2: Auf Erfolgskurs

Für diesen Test haben wir uns an die 500/50/5-Regel gehalten, mit 500 RPS gestartet und über rund 20 Minuten schrittweise auf 1500 RPS hochgefahren.

```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));
```

Ran between 1/1/10 between 0140 and 0158 CEST

Results:
```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
```

So sieht das im Key Visualiser aus:

Beginn der Skalierung

Ende der Skalierung

Das Ergebnis? Beeindruckende 99,97 % Erfolgsquote.

Eigene Tests durchführen

Das Skript lässt sich lokal mit k6 ausführen – das hat einige Vorteile: schnelles Setup, keine Kosten und so weiter. Allerdings sind Sie dabei durch die Ressourcen Ihres lokalen Rechners limitiert, haben nur eine Instanz und die Ergebnisse können durch Ihre Netzwerkanbindung beeinflusst werden. Für aussagekräftigere Resultate ist es oft sinnvoll, das Skript auf einer (Google-Cloud-)VM laufen zu lassen.

Die 500/50/5-Regel ist nicht bloß eine Empfehlung – sie ist eine entscheidende Leitlinie, damit Ihre Firestore-Implementierung reibungslos und effizient skaliert. Wer sich an diese Regel hält und Tools wie k6 für Skalierungstests einsetzt, umschifft Performance-Fallen und hält die Anwendung auch bei steigender Last stabil am Laufen.

Denken Sie daran: Beim Skalieren von Datenbanken gewinnt, wer mit Bedacht vorgeht. Viel Erfolg beim Skalieren!

— -

Möchten Sie tiefer in die Firestore-Skalierung einsteigen oder benötigen Sie Unterstützung bei der Optimierung Ihrer Cloud-Infrastruktur? Auf doit.com erfahren Sie, wie wir Ihnen helfen, das volle Potenzial Ihrer Cloud auszuschöpfen.