Black-Friday-Traffic realistisch simulieren, bevor er real wird
Wer Magento-Shops ohne systematisches Load-Testing auf den Black-Friday-Traffic vorbereitet, verlässt sich auf Vermutungen statt auf Messwerte. Dieser Artikel zeigt, wie Load-, Stress- und Spike-Tests sich unterscheiden, wie realistische Traffic-Muster mit k6, JMeter und Locust modelliert werden und welche Metriken jenseits der Antwortzeit über die tatsächliche Kapazität eines Shops entscheiden.
Inhaltsverzeichnis
- 1. Load-Testing, Stress-Testing und Spike-Testing im Vergleich
- 2. Realistische Traffic-Muster für Black-Friday-Szenarien
- 3. Think Time und Nutzerverhalten realistisch simulieren
- 4. Cart- und Checkout-Funnel-Gewichtung im Lastmodell
- 5. Tooling-Vergleich: k6, JMeter und Locust
- 6. Über die Response Time hinaus: Fehlerquote und Durchsatz
- 7. Datenbank-Connections, Queue-Tiefe und Cache-Hit-Ratio unter Last
- 8. Warum die Staging-Umgebung die Produktion wirklich spiegeln muss
- 9. CI/CD-Automatisierung und Tooling im direkten Vergleich
- 10. Zusammenfassung
- 11. FAQ
1. Load-Testing, Stress-Testing und Spike-Testing im Vergleich
Load-Testing, Stress-Testing und Spike-Testing werden in der Praxis oft synonym verwendet, obwohl sie unterschiedliche Fragen beantworten. Load-Testing simuliert die erwartete, realistische Last über einen längeren Zeitraum, etwa den prognostizierten Traffic am Black-Friday-Wochenende, und prüft, ob der Shop unter dieser Last innerhalb definierter Antwortzeiten und Fehlerquoten bleibt. Das Ziel ist keine Grenzwertsuche, sondern die Bestätigung, dass eine konkrete, geplante Last zuverlässig bedient wird, inklusive Checkout, Zahlungsanbindung und Bestandsprüfung unter realistischer Parallelität.
Stress-Testing geht bewusst über die erwartete Kapazität hinaus, bis das System an seine Grenzen stößt oder ausfällt. Entscheidend ist dabei nicht der Ausfall selbst, sondern wie er passiert: Degradiert der Shop kontrolliert, etwa durch Warteschlangen oder Ratenlimits, oder kippt er abrupt mit 502-Fehlern und vollen Datenbank-Connection-Pools? Stress-Tests liefern die Kapazitätsreserve in Prozent und zeigen, welche Komponente zuerst limitiert, meist die MySQL-Connection-Pool-Größe oder der PHP-FPM-Worker-Pool, seltener die reine Netzwerkbandbreite.
Spike-Testing prüft die Reaktion auf einen plötzlichen, kurzen Lastanstieg ohne Ramp-up, wie er bei einem Newsletter-Versand, einer Influencer-Erwähnung oder dem exakten Startzeitpunkt eines Flash-Sale entsteht. Anders als beim Load-Test steigt die Last hier nicht über Minuten, sondern innerhalb von Sekunden auf ein Vielfaches. Spike-Tests decken auf, ob Auto-Scaling schnell genug reagiert, ob der Cache nach einem Deployment kalt ist und ob sich das System nach dem Spike wieder normalisiert, statt in einem degradierten Zustand zu verharren.
2. Realistische Traffic-Muster für Black-Friday-Szenarien
Ein realistisches Lastmodell für Black-Friday-Traffic beginnt nicht bei der Anzahl virtueller Nutzer, sondern bei der Ramp-up-Kurve. Echter Traffic steigt selten linear: E-Mail-Kampagnen und Push-Benachrichtigungen erzeugen in den ersten Minuten nach Versand einen steilen Anstieg, gefolgt von einem Plateau und einem langsameren Abklingen über Stunden. Ein Lasttest, der stattdessen gleichmäßig über 30 Minuten hochfährt, testet ein Szenario, das in der Realität nie eintritt, und verschleiert genau die Engpässe, die beim tatsächlichen Ansturm auftreten.
Concurrent User Modeling unterscheidet zwischen virtuellen Nutzern (VUs) und tatsächlichem Durchsatz: Ein VU, der ständig Requests feuert, erzeugt eine völlig andere Last als ein VU mit realistischer Denkzeit zwischen den Klicks. Für Magento-Shops empfiehlt sich ein gestuftes Modell mit mehreren stages, das den Basis-Traffic, den Anstieg zur Kampagnenzeit und die Spitzenlast am Aktionsstart als getrennte Phasen abbildet, jede mit eigener Ziel-VU-Zahl und eigener Dauer, wie es k6 über die Konfiguration stages nativ unterstützt.
// k6 load test: Black Friday ramp-up scenario for a Magento storefront
import http from 'k6/http';
import { sleep, check } from 'k6';
import { randomIntBetween } from 'https://jslib.k6.io/k6-utils/1.2.0/index.js';
export const options = {
scenarios: {
// Baseline browsing traffic: category and product pages
browsing: {
executor: 'ramping-vus',
exec: 'browseCatalog',
startVUs: 50,
stages: [
{ duration: '5m', target: 200 }, // early campaign ramp-up
{ duration: '10m', target: 800 }, // plateau during peak hour
{ duration: '15m', target: 800 }, // sustained peak
{ duration: '10m', target: 150 }, // gradual decay
],
},
// Checkout traffic: smaller share, weighted funnel
checkout: {
executor: 'ramping-vus',
exec: 'runCheckout',
startVUs: 5,
stages: [
{ duration: '5m', target: 20 },
{ duration: '10m', target: 80 },
{ duration: '15m', target: 80 },
{ duration: '10m', target: 15 },
],
},
},
thresholds: {
http_req_duration: ['p(95)<800'],
http_req_failed: ['rate<0.01'],
},
};
export function browseCatalog() {
const res = http.get(`${__ENV.TARGET_URL}/catalog/category/view/id/24`);
check(res, { 'status is 200': (r) => r.status === 200 });
sleep(randomIntBetween(2, 8)); // think time between page views
}
export function runCheckout() {
http.get(`${__ENV.TARGET_URL}/checkout/cart`);
sleep(randomIntBetween(3, 6));
const res = http.post(`${__ENV.TARGET_URL}/checkout/onepage/success`);
check(res, { 'checkout completed': (r) => r.status === 200 });
}
3. Think Time und Nutzerverhalten realistisch simulieren
Ohne Think Time simuliert ein Lasttest Bots, keine Menschen. Reale Nutzer verbringen Zeit auf der Produktdetailseite, lesen Bewertungen, vergleichen Varianten - das erzeugt Pausen zwischen den Requests, die in einem Lastmodell als sleep()-Aufrufe mit realistischer Streuung abgebildet werden müssen, typischerweise 2 bis 8 Sekunden statt eines festen Werts. Ein Test ohne Think Time erzeugt künstlich hohe Anfragen-pro-Sekunde-Werte bei unrealistisch wenigen virtuellen Nutzern und führt zu falschen Kapazitätsannahmen, weil reale Nutzer nie so dicht aufeinanderfolgende Requests senden.
Ebenso wichtig ist die Streuung der Think Time über eine Zufallsverteilung statt eines festen Werts, weil synchronisierte VUs sonst im Gleichtakt Anfragen senden und künstliche Lastspitzen erzeugen, die es in echtem Traffic so nicht gibt - dieses Artefakt nennt man Coordinated Omission. k6 und Locust bieten dafür eingebaute Zufallsfunktionen wie randomIntBetween(), die Think Time innerhalb eines Bereichs streuen und jeden virtuellen Nutzer leicht phasenversetzt agieren lassen.
4. Cart- und Checkout-Funnel-Gewichtung im Lastmodell
Nicht jeder Nutzer verhält sich gleich, und ein Lastmodell, das alle Requests gleich gewichtet, überschätzt die Kapazität an der kritischsten Stelle: dem Checkout. In einem typischen E-Commerce-Funnel besuchen deutlich mehr Nutzer die Startseite und Kategorieseiten als tatsächlich bis zur Zahlungsseite vordringen, realistische Verteilungen liegen oft bei 100 % Seitenaufrufen, 40 % Produktdetailseiten, 15 % Warenkorb und 5 % abgeschlossenem Checkout. Ein Lasttest muss diese Trichterform abbilden, statt Checkout-Requests mit derselben Häufigkeit wie Produktseitenaufrufe zu erzeugen.
Technisch wird diese Gewichtung in k6 über Szenarien mit unterschiedlichen VU-Anteilen oder in JMeter über Thread-Gruppen mit eigenen Verteilungen umgesetzt. Wichtig ist, dass der Checkout-Pfad trotz geringeren Anteils an Requests überproportional viel Last auf der Datenbank erzeugt, weil dort Bestandsprüfung, Preisberechnung, Rabattlogik und Zahlungsanbindung synchron zusammenlaufen. Ein Lastmodell, das den Checkout-Anteil unterschätzt, übersieht damit genau die Stelle, an der ein Shop unter echter Black-Friday-Last zuerst einbricht.
5. Tooling-Vergleich: k6, JMeter und Locust
k6 von Grafana Labs skriptet Testszenarien in JavaScript/ES6, kompiliert sie aber intern zu Go-Routinen, was eine deutlich höhere Last pro Testrechner erzeugt als interpretierte Alternativen. Für verteilte Lastgenerierung setzt k6 auf k6 Cloud oder den Operator für Kubernetes, der mehrere Pods parallel als Lastgeneratoren startet und Ergebnisse zentral aggregiert. Das eingebaute Threshold-System erlaubt es, den Test direkt anhand von SLA-Kriterien wie p95-Antwortzeit oder Fehlerquote automatisch als bestanden oder fehlgeschlagen zu werten, ideal für die CI/CD-Integration.
Apache JMeter ist das etablierteste Tool mit der größten Plugin-Bibliothek und einer GUI zum visuellen Erstellen von Testplänen, skaliert aber pro Thread mit spürbar höherem Speicherverbrauch als k6, weil jeder virtuelle Nutzer ein echter JVM-Thread ist. Verteilte Ausführung funktioniert über einen Master-Slave-Modus mit mehreren Remote-Instanzen. Locust skriptet Szenarien in reinem Python, was für Teams mit Python-Hintergrund die Einstiegshürde senkt, und verteilt Last nativ über mehrere Worker-Prozesse und -Maschinen, gesteuert von einem Master-Prozess mit Web-UI zur Live-Beobachtung der laufenden Last.
#!/usr/bin/env bash
# run-load-test.sh - execute k6 against a specific environment
set -euo pipefail
ENVIRONMENT="${1:?Usage: run-load-test.sh <staging|production>}"
case "$ENVIRONMENT" in
staging)
TARGET_URL="https://staging.mironsoft-shop.de"
;;
production)
TARGET_URL="https://shop.mironsoft.de"
echo "[WARN] Running against production, confirm before proceeding"
;;
*)
echo "[ERROR] Unknown environment: $ENVIRONMENT" >&2
exit 1
;;
esac
k6 run \
--env TARGET_URL="$TARGET_URL" \
--out json=results/k6-"$(date +%Y%m%d-%H%M%S)".json \
--summary-export=results/summary.json \
black-friday-scenario.js
6. Über die Response Time hinaus: Fehlerquote und Durchsatz
Response Time allein verschleiert, wie ein System unter Last tatsächlich degradiert. Die Fehlerquote ist die wichtigste Zweitmetrik: Ein Shop, der bei steigender Last plötzlich HTTP-500- oder Timeout-Antworten produziert, versagt, selbst wenn die durchschnittliche Antwortzeit der erfolgreichen Requests unauffällig bleibt. Ebenso entscheidend ist die Durchsatz-Degradation, der Punkt, an dem Requests pro Sekunde trotz weiter steigender virtueller Nutzerzahl nicht mehr wachsen oder sogar sinken. Das ist das verlässlichste Signal für die tatsächliche Kapazitätsgrenze, zuverlässiger als jeder einzelne Antwortzeit-Wert.
Die Queue-Tiefe in PHP-FPM, Nginx oder einer vorgeschalteten Message-Queue zeigt, ob Requests bereits warten, bevor sie überhaupt verarbeitet werden, ein Frühwarnsignal, das oft Minuten vor dem sichtbaren Antwortzeit-Anstieg auftritt. k6 erlaubt es, solche Metriken über Trend- und Counter-Custom-Metrics direkt im Testskript zu erfassen und gemeinsam mit den Standardmetriken auszuwerten, statt sie nachträglich aus separaten Server-Logs zu rekonstruieren.
{
"thresholds": {
"http_req_duration": ["p(95)<800", "p(99)<1500"],
"http_req_failed": ["rate<0.01"],
"checkout_duration": ["p(95)<2000"],
"db_queue_depth": ["p(95)<50"]
},
"summaryTrendStats": ["avg", "min", "med", "p(90)", "p(95)", "p(99)", "max"],
"noConnectionReuse": false,
"discardResponseBodies": true
}
7. Datenbank-Connections, Queue-Tiefe und Cache-Hit-Ratio unter Last
Die Datenbank-Connection-Saturation ist in Magento-Shops meist die erste harte Grenze unter Last, lange bevor CPU oder Arbeitsspeicher des Webservers ausgeschöpft sind. Jeder PHP-FPM-Worker hält während der Verarbeitung eine MySQL-Verbindung, und sobald max_connections erreicht ist, warten neue Requests oder scheitern mit Verbindungsfehlern. Während eines Lasttests sollte SHOW STATUS LIKE 'Threads_connected' oder die entsprechende Cloud-Metrik parallel zum Testlauf beobachtet werden, um den Sättigungspunkt exakt einem Lastniveau zuzuordnen, statt ihn erst aus einem nachträglichen Postmortem zu rekonstruieren.
Die Cache-Hit-Ratio von Redis oder Varnish verändert sich unter Last oft dramatisch, weil neue Produktkombinationen, Sonderpreise und personalisierte Segmente Cache-Einträge erzeugen, die im Normalbetrieb nie entstehen. Eine Hit-Ratio von 95 % im Ruhezustand kann unter Black-Friday-Last mit vielen neuen Rabattcodes auf 70 % fallen, jeder Cache-Miss löst dann einen vollen Anwendungs-Rebuild aus, der die Datenbank zusätzlich belastet. Ein guter Lasttest misst diese Ratio live über INFO stats bei Redis oder die Varnish-VCL-Statistiken, nicht nur die resultierende Antwortzeit.
#!/usr/bin/env bash
# monitor-saturation.sh - poll DB connections and cache hit ratio during a load test
set -euo pipefail
INTERVAL=5
LOG_FILE="results/saturation-$(date +%Y%m%d-%H%M%S).csv"
echo "timestamp,mysql_threads_connected,mysql_max_connections,redis_hit_ratio" > "$LOG_FILE"
while true; do
connected=$(mysql -N -e "SHOW STATUS LIKE 'Threads_connected';" | awk '{print $2}')
max_conn=$(mysql -N -e "SHOW VARIABLES LIKE 'max_connections';" | awk '{print $2}')
hits=$(redis-cli INFO stats | grep keyspace_hits | cut -d: -f2 | tr -d '\r')
misses=$(redis-cli INFO stats | grep keyspace_misses | cut -d: -f2 | tr -d '\r')
hit_ratio=$(awk -v h="$hits" -v m="$misses" 'BEGIN { print (h+m > 0) ? h/(h+m) : 1 }')
echo "$(date -Iseconds),$connected,$max_conn,$hit_ratio" >> "$LOG_FILE"
sleep "$INTERVAL"
done
8. Warum die Staging-Umgebung die Produktion wirklich spiegeln muss
Ein Lasttest ist nur so aussagekräftig wie die Umgebung, gegen die er läuft. Eine Staging-Umgebung mit halbierter Server-Größe, einem Zehntel des Produktkatalogs und leerem Cache liefert Ergebnisse, die mit der Produktion nichts zu tun haben, weder die absoluten Antwortzeiten noch der Punkt, an dem das System kippt, lassen sich verlässlich übertragen. Infrastruktur-Parität bedeutet: gleiche Instanztypen, gleiche PHP-FPM-Worker-Zahl, gleiche Datenbank-Konfiguration und, wo möglich, dieselbe Netzwerktopologie inklusive CDN und Load Balancer.
Ebenso entscheidend ist der Cache-Warmup-Zustand: Ein frisch deployter Shop mit kaltem Redis- und Varnish-Cache verhält sich unter Last fundamental anders als ein seit Tagen laufendes System mit warmem Cache, weil praktisch jeder erste Request in einen vollständigen Seiten-Rebuild mündet. Staging-Tests sollten deshalb entweder bewusst den Kaltstart-Fall abdecken oder den Cache vor dem eigentlichen Lasttest gezielt vorwärmen, je nachdem, welches Szenario getestet werden soll. Die Datenvolumen-Parität rundet das ab: Ein Katalog mit 500 statt 50.000 Produkten verändert Indexgrößen, Kategoriebaum-Tiefe und Suchindex-Performance so stark, dass Testergebnisse auf einem verkleinerten Datensatz kaum Rückschlüsse auf die Produktion zulassen.
9. CI/CD-Automatisierung und Tooling im direkten Vergleich
Load-Tests, die nur manuell vor großen Events laufen, entdecken Regressionen zu spät. Die robustere Praxis integriert einen reduzierten Lasttest als eigene Stage in die CI/CD-Pipeline, der nach jedem Deployment auf Staging automatisch gegen definierte Schwellenwerte für p95-Antwortzeit, Fehlerquote und Durchsatz prüft. k6 gibt dafür über Thresholds einen klaren Exit-Code zurück, der die Pipeline bei Überschreitung fehlschlagen lässt, genau wie ein fehlgeschlagener Unit-Test.
Für den echten Black-Friday-Vorbereitungslauf reicht die CI-Pipeline allein nicht aus: Zusätzlich braucht es einen groß angelegten, geplanten Lasttest gegen eine vollständig produktionsähnliche Staging-Umgebung, typischerweise ein bis zwei Wochen vor dem Event, mit genug Zeitpuffer, um gefundene Engpässe noch zu beheben und erneut zu testen. Die CI-Integration fängt Regressionen im Tagesgeschäft ab, der große Vorbereitungslauf validiert die tatsächliche Kapazität für den Ausnahme-Traffic.
# .gitlab-ci.yml excerpt: automated load test stage against staging
stages:
- deploy
- load-test
deploy_staging:
stage: deploy
script:
- ./deploy.sh staging
environment:
name: staging
k6_load_test:
stage: load-test
image: grafana/k6:latest
needs: ["deploy_staging"]
script:
- k6 run --env TARGET_URL=https://staging.mironsoft-shop.de
--summary-export=summary.json
black-friday-scenario.js
artifacts:
when: always
paths:
- summary.json
expire_in: 30 days
rules:
- if: '$CI_COMMIT_BRANCH == "main"'
k6, JMeter und Locust lösen dieselbe Aufgabe mit unterschiedlichen Schwerpunkten bei Skriptsprache, Ressourcenverbrauch und verteilter Lastgenerierung. Die folgende Übersicht fasst zusammen, wofür sich welches Tool am besten eignet, ergänzend zur Tooling-Einordnung aus Abschnitt 5.
| Werkzeug | Scripting-Sprache | Typische Einschränkung | Empfehlung |
|---|---|---|---|
| k6 | JavaScript/ES6 (kompiliert zu Go) | Kein natives GUI, Skripting erforderlich | Beste Wahl für CI/CD und ressourceneffiziente Tests |
| JMeter | Java (GUI-Testpläne oder XML) | Hoher Speicherverbrauch pro Thread | Größte Plugin-Bibliothek, etabliertes Ökosystem |
| Locust | Python | Python-Kenntnisse im Team nötig | Sehr flexibel für komplexe, code-basierte Szenarien |
In der Praxis entscheidet selten die reine Rohleistung eines Tools, sondern die Passung zum Team und zur bestehenden Toolchain. Wer bereits JavaScript in der CI/CD-Pipeline nutzt, integriert k6 am reibungslosesten. Teams mit Python-Backend-Expertise finden sich in Locust schneller zurecht. JMeter bleibt die richtige Wahl, wenn Fachabteilungen Testpläne ohne Code selbst erstellen sollen.
Mironsoft
Load-Testing, Performance-Engineering und Skalierungs-Beratung für Magento-Shops
Bereit für den nächsten Black Friday?
Wir bauen realistische Lastmodelle für euren Magento-Shop, identifizieren die tatsächliche Kapazitätsgrenze und rüsten Checkout, Datenbank und Cache gezielt für den Ansturm nach, mit k6-Testsuiten, die dauerhaft in eurer CI/CD-Pipeline bleiben.
Load-Testing-Setup
Realistische k6- oder JMeter-Szenarien mit Ramp-up-Kurven und Checkout-Funnel-Gewichtung
Kapazitäts-Audit
Datenbank-Connections, Queue-Tiefe und Cache-Hit-Ratio unter Last systematisch analysieren
CI/CD-Integration
Automatisierte Lasttests mit Schwellenwerten als feste Pipeline-Stage etablieren
10. Zusammenfassung
Load-Testing-Methodik für Magento-Shops beginnt mit der richtigen Begriffsklärung: Load-Tests bestätigen erwartete Kapazität, Stress-Tests finden die Belastungsgrenze, Spike-Tests prüfen die Reaktion auf abrupte Lastspitzen. Realistische Lastmodelle bilden Ramp-up-Kurven, Think Time und die Trichterform des Checkout-Funnels ab, statt gleichverteilte künstliche Last zu erzeugen. k6, JMeter und Locust unterscheiden sich vor allem in Skriptsprache, Ressourcenverbrauch pro virtuellem Nutzer und der Art der verteilten Lastgenerierung, die Wahl hängt vom Team-Skillset und der benötigten Skalierung ab.
Response Time ist nur eine von vielen relevanten Metriken: Fehlerquote, Durchsatz-Degradation, Queue-Tiefe, Datenbank-Connection-Saturation und Cache-Hit-Ratio zeigen gemeinsam, wo und wie ein Shop unter Last tatsächlich versagt. All diese Messwerte sind nur so vertrauenswürdig wie die Testumgebung selbst, eine Staging-Umgebung ohne Infrastruktur-, Cache- und Datenvolumen-Parität zur Produktion liefert Ergebnisse, die im Ernstfall in die falsche Richtung weisen. Kontinuierliche Lasttests in der CI/CD-Pipeline und ein großer Vorbereitungslauf vor dem eigentlichen Event ergänzen sich, statt sich zu ersetzen.
Load-Testing-Methodik für Magento-Shops - Das Wichtigste auf einen Blick
Load vs. Stress vs. Spike
Load-Tests bestätigen erwartete Kapazität, Stress-Tests finden die Grenze, Spike-Tests prüfen abrupte Lastspitzen ohne Ramp-up.
Realistische Lastmodelle
Ramp-up-Kurven, gestreute Think Time und Checkout-Funnel-Gewichtung statt gleichverteilter künstlicher Last.
Tooling
k6 für CI-Integration und Ressourceneffizienz, JMeter für Plugin-Tiefe, Locust für Python-Teams.
Metriken & Staging
Fehlerquote, Queue-Tiefe und Cache-Hit-Ratio neben Response Time messen, immer gegen produktionsnahe Staging-Umgebung.