Shopware 6 Performance Blog
locust statistik 3 locust statistik 3

Shopware 6 Lasttest/Benchmark mit Locust – Teil 2

Im ersten Teil unserer Serie haben wir darüber gesprochen, warum ein Shopware 6 Benchmark ein unverzichtbares Werkzeug ist, um sicherzustellen, dass Dein Shop auch bei hohem Traffic – etwa während Aktionen wie dem Black Friday – stabil bleibt. Ein Ausfall in solchen Spitzenzeiten kann zu erheblichen Umsatzeinbußen führen. Mit Locust, einem meiner bevorzugten Tools für Lasttests, lassen sich realistische Nutzerszenarien simulieren, um mögliche Schwachstellen im Vorfeld zu erkennen und zu beheben. So startest Du optimal in den nächsten großen Sale und vermeidest böse Überraschungen.

Falls Du den ersten Teil verpasst hast, findest Du ihn hier.

In diesem zweiten Teil werden wir nun gemeinsam einen Lasttest in der Praxis durchführen. Ich zeige Dir Schritt für Schritt, wie Du Locust einrichtest und testest, um die Performance Deines Shops zu optimieren.

Codebase für Benchmark

Locust ist auf den ersten Blick ein relativ simples Tool, das auf unsere Anforderungen angepasst werden muss. Um es sinnvoll nutzen zu können, müssen wir dem Tool zunächst mitteilen, welche Szenarien wir testen möchten. Dies können beispielsweise API-Importe oder Besucherzahlen während eines Black Friday Sales sein.

Ein weiterer wichtiger Aspekt ist die Definition der verschiedenen User-Typen, die simuliert werden sollen. Dabei geht es nicht nur darum, wie viele User der Test insgesamt umfassen soll, sondern auch darum, was diese simulierten Nutzer konkret auf der Seite machen. So können wir zum Beispiel definieren, dass ein Teil der Nutzer lediglich das Listing aufruft, andere eine Detailseite besuchen oder sogar einen Artikel kaufen.

Um Locust also für Shopware 6 optimal nutzen zu können, brauchen wir eine passende Codebase, welche die Aufrufe für uns macht.

Tideways Repository

Das Repository von Tideways bietet eine nützliche Grundlage für Lasttests, allerdings gibt es einige Vor- und Nachteile, die beachtet werden sollten. Das Repository findet ihr hier.

Nachteile:

  • Die Codebase ist veraltet und muss von einem Entwickler überarbeitet werden, um wieder voll funktionsfähig zu sein. Bei Bedarf kann ich hierbei Unterstützung anbieten, da ich die Codebase bereits aufgeräumt habe.
  • Ein wesentlicher Nachteil ist, dass die API nicht getestet wird, was für einige Anwendungen entscheidend sein kann.

Vorteile:

  • Die Codebase bietet einen integrierten Command, mit dem Berichte generiert werden können, was den Testprozess vereinfachen kann.
  • Der Benchmark kann auch über Docker ausgeführt werden, wodurch er weniger systemabhängig ist und flexibler genutzt werden kann.
  • Eine Anbindung an Tideways ist ebenfalls vorhanden, was bei der Auswertung und Erstellung von Berichten eine hilfreiche Unterstützung bietet.

Das Tideways-Repo nutze ich in meinen Lasttests nicht, weil mir API-Tests fehlen.

Shopware Repository

Der Shopware 6 Code für Locust-Tests bietet ebenfalls einige Vor- und Nachteile, die bei der Verwendung beachtet werden sollten. Das Repo findest Du hier.

Nachteile:

  • Die Locust-Integration ist nur in der Version 6.6.5.1 von Shopware kurzzeitig vorhanden gewesen und wurde danach wieder entfernt. Da sie nicht weiterentwickelt wird, könnten hier Probleme auftreten.
  • Es wird kein Report generiert, was bedeutet, dass alle relevanten Daten und Ergebnisse manuell gesammelt und ausgewertet werden müssen.
  • Es gibt keine Docker-Integration, wodurch das Setup möglicherweise aufwendiger und systemabhängiger wird.

Vorteile:

  • Die Codebase bietet verschiedene realitätsnahe Testing-Szenarien (unter anderem auch API-Tests), was für präzise und praxisnahe Lasttests hilfreich ist.
  • Die Integration funktioniert out of the box, ohne dass große Anpassungen notwendig sind.

Diese Codebase kann nützlich sein, wenn Du eine einfache, sofort einsatzbereite Lösung suchst, um realistische Testszenarien zu simulieren. Allerdings fehlen mir persönlich die Docker-Integration und das Generieren von Reports, um sie z.B. den Kunden vorzustellen.

8mylez Codebase für Lasttest

Für unsere Lasttests nehme ich den Shopware-Code und erweitere ihn mit den für mich relevanten Optimierungen.

Verbesserungen:

  • Außerdem hast Du eine Docker-Integration hinzugefügt, was die Codebase unabhängiger vom jeweiligen System macht und den Einsatz deutlich vereinfacht.
  • Ich habe an einigen Stellen zusätzliche Konfigurationsmöglichkeiten eingebaut

Durch diese Anpassungen ist das Tool optimal für meinen Benchmark einsatzbereit.

Wie funktioniert der Code?

Als locustfile nutze ich integration-benchmark.py von Shopware, um sowohl API- als auch Storefront-Nutzer zu simulieren. In dieser File kommen vier verschiedene User-Klassen zum Einsatz, die jeweils unterschiedliche Testfälle abdecken:

Sync User

  • Der User simuliert API-Requests, um …
    • neue Produkte anzulegen bzw. bestehende Daten zu ändern
    • Produkt-Preise oder Lagerbestände zu aktualisieren
  • Die API-credentials werden hierbei in der env.json bereitgestellt

Visitor User

  • Simuliert einen Besucher, der sich im Storefront bewegt, ohne eine Bestellung aufzugeben.
  • Öffnet Listings, führt Filterungen aus, besucht Detailseiten und nutzt die Suchfunktion. Dieses Szenario deckt die typischen Bewegungen eines Nutzers ab, der einfach durch den Shop stöbert.

SurfWithOrder User

  • Diese Klasse verhält sich ähnlich wie der Visitor, mit dem Unterschied, dass der Nutzer sich registriert und anschließend eine Bestellung aufgibt.
  • Sie simuliert den typischen Bestellvorgang, bei dem ein neuer Kunde zuerst die Seite erkundet, bevor er einen Kauf abschließt.

FastOrder

  • Dieser Nutzertyp führt direkt eine Bestellung durch, ohne zuvor Listings zu durchsuchen oder zu filtern.
  • Das Szenario ähnelt dem Verhalten eines Nutzers, der über Google Ads oder eine andere Werbekampagne direkt auf eine Produktseite gelangt und dort eine Bestellung aufgibt.

Je nach den spezifischen Anforderungen und Zielen des Kunden können diese Nutzerklassen flexibel angepasst werden. So lässt sich der Test genau auf das Verhalten der Zielgruppe zuschneiden und ermöglicht gezielte Optimierungen.

Die Kombination dieser vier Nutzerklassen bietet eine umfassende Testabdeckung für unterschiedliche Nutzungsszenarien, sowohl im API- als auch im Storefront-Bereich.

Schritt-für-Schritt-Anleitung

Stellen wir uns folgendes Szenario vor: Es ist Oktober, und ein Kunde kommt auf uns zu, weil er sich auf das wichtige Weihnachtsgeschäft vorbereitet. Der Großteil seiner Bestellungen passiert saisonal rund um Weihnachten. Im Frühling/ Sommer lief sein Shop stabil, doch für dieses Jahr erwartet er ab November einen viermal so hohen Traffic. Verständlicherweise hat der Kunde nun Bedenken, dass sein Shop diesem Ansturm nicht standhalten könnte, was in der umsatzstärksten Zeit des Jahres zu erheblichen Verlusten führen würde.

Der Kunde wendet sich also mit der dringenden Bitte an uns, den Shop auf Belastbarkeit zu prüfen und sicherzustellen, dass er dem erwarteten Traffic gewachsen ist. Unser Ziel ist es, durch gezielte Lasttests eventuelle Schwachstellen zu identifizieren und rechtzeitig zu beheben, sodass der Kunde sein Weihnachtsgeschäft ohne Sorgen und Ausfälle angehen kann.

In meinem Fall werde ich die Lasttests bei shop.pagespeedy.de durchführen.

Schritt 1: Ziel bestimmen

Im Grunde ist das Ziel für unseren Benchmark sehr klar: Der Shop darf nicht abstürzen, und die Ladezeiten sollen auch bei erhöhtem Traffic einigermaßen schnell bleiben. Um dieses Ziel zu erreichen, müssen wir zunächst definieren, welche Metriken für uns wichtig sind und wo die Belastungsgrenzen des Shops liegen.

1. Wichtige Metriken prüfen

  • Ladezeiten: Ein zentraler Indikator für die Shop-Performance sind die Ladezeiten. Hier messen wir die durchschnittlichen Ladezeiten der wichtigsten Seiten im Shop:
    • Startseite
    • Listing-Seiten
    • Produktdetailseiten
    • Checkout-Prozess
    Diese Werte dienen als Ausgangsbasis, um später zu prüfen, wie sich der erhöhte Traffic auf die Geschwindigkeit des Shops auswirkt.
  • Server-Ressourcen: Neben den Ladezeiten werfen wir einen Blick auf die Ressourcen des Servers und deren aktuelle Auslastung:
    • CPU: Wie viel Rechenleistung steht aktuell zur Verfügung, und wie stark wird die CPU unter normaler Last beansprucht?
    • Arbeitsspeicher: Wie viel RAM ist vorhanden, und wie hoch ist die momentane Speichernutzung?

2. Performance-Budget festlegen

Nachdem wir die grundlegenden Metriken gesammelt haben, definieren wir ein sogenanntes Performance-Budget. Das bedeutet, wir setzen klare Grenzen, die unter hoher Last nicht überschritten werden dürfen. Zum Beispiel:

  • Wie weit die durchschnittlichen Ladezeiten abweichen dürfen, muss mit dem Kunden besprochen werden. Im Idealfall sollten sie in etwas gleich bleiben.
  • Die CPU-Auslastung kann in der Konsole z.B. mit dem htop-Befehl ausgelesen werden. Diese sollte nie über der Gesamtanzahl der CPU-Kerne liegen. Haben wir also 2 CPU-Kerne, sollte die Load Average bei max. 2 sein.
  • Der Arbeitsspeicher darf nicht komplett ausgeschöpft werden, um sicherzustellen, dass der Server nicht ins Stocken gerät.

Mit diesen festgelegten Zielen und Metriken schaffen wir die Grundlage für den Benchmark. So können wir überprüfen, ob der Shop auch bei dem zu erwartenden, viermal höheren Traffic stabil und performant bleibt.

Für meinen Lasttest definiere ich folgendes Performance-Budget:

Startseite ø ca. 300ms

Listing ø 500ms

Detailseite ø 310ms

CPU load average max. 2

RAM-Verbrauch max 8GB

Schritt 2: Tool lokal einrichten

Um den Benchmark durchzuführen, müssen wir die Codebase lokal einrichten und vorbereiten. Dies umfasst das Klonen des Repositories, die Installation der benötigten Abhängigkeiten sowie das Einrichten der Testdaten.

Klont Euch hierbei das Shopware-Repo in der passenden Version oder nutzt Eure eigene Codebase. Für meinen Code müssten auf dem System docker & docker-compose installiert sein.

Damit die Lasttests realitätsnahe Szenarien abbilden, müssen wir die Produkt- und Listing-URLs sowie andere notwendige Daten aus dem Shop abrufen. Dazu verwenden wir den enthaltenen Setup-Command, der die relevanten Daten über die Shop-API abfragt und lokal speichert. Für den Shopware-Code müssen hierfür in der .env die Zugangsdaten der Datenbank hinterlegt werden. Der Command speichert alle nötigen Daten im Fixtures-Ordner ab, damit locust darauf zugreifen kann.

Schritt 3: Lasttest abstimmen

Nicht jeder Lasttest ist gleich, und es ist entscheidend, den Test individuell auf die Bedürfnisse des Kunden abzustimmen. Ein optimaler Test muss den simulierten Traffic so realistisch wie möglich gestalten, um das erwartete Szenario in der Peak-Zeit nachzustellen. Wenn der Test den tatsächlichen Traffic nicht korrekt simuliert, sind die Ergebnisse weniger aussagekräftig.

1. Traffic realistisch simulieren

Das Ziel ist es, den Traffic so genau wie möglich nachzubilden, wie er während der Hochphase – in diesem Fall zur Weihnachtszeit – auf den Shop zukommt. Wir simulieren nicht nur die Menge des Traffics, sondern auch dessen Zusammensetzung (also Mischung Shopkunden vs API-Requests). Denn wenn der Test die tatsächliche Auslastung des Shops nicht richtig abbildet, ist er nur bedingt aussagekräftig.

2. Auswahl des richtigen Szenarios

Für diesen speziellen Kunden wollen wir den alltäglichen Traffic simulieren, der sowohl aus API-Updates als auch aus Storefront-Usern besteht. Um dies zu erreichen, setzen wir das bereits erwähnte integration-benchmark.py ein, das die typischen Aufgaben des Shops am besten widerspiegelt.

Da in diesem Fall die Suche über einen externen Dienstleister läuft, müssen wir die Suchfunktion nicht simulieren. Ebenso entfällt die Storefront-API, da sie vom Kunden nicht genutzt wird. Wir konzentrieren uns also auf die für diesen Shop relevanten Szenarien: Produktupdates über die API und Kundeninteraktionen in der Storefront.

3. Anpassungen für spezielle Lastverteilungen

In einigen Fällen könnte es interessant sein, bestimmte Lastszenarien zu simulieren, wie zum Beispiel einen extrem hohen Traffic auf einzelne Produkte. Dies könnte der Fall sein, wenn der Shop bestimmte Bestseller oder stark beworbene Produkte hat, die während der Hochphase außergewöhnlich oft gekauft werden. Hier könnte ein spezieller nvidia-benchmark durchgeführt werden, bei dem nicht der Traffic gleichmäßig über den Shop verteilt wird, sondern einzelne Produktseiten besonders stark belastet werden.

Indem wir diese Details berücksichtigen und den Test präzise auf die Anforderungen des Kunden zuschneiden, können wir sicherstellen, dass die Ergebnisse den tatsächlichen Anforderungen des Shops gerecht werden und das Risiko von Ausfällen minimiert wird.

Schritt 4: Test-Server vorbereiten

Um den Benchmark sauber und effizient durchzuführen, sollte zunächst eine exakte Kopie des aktuellen Setups des Shops erstellt werden. Dies ist notwendig, da im Test Testbestellungen generiert werden und wir sicherstellen wollen, dass der Test möglichst realitätsnah ist. Nur wenn das Testsystem dieselben Serverressourcen, die gleiche Codebase und Konfiguration wie das Produktivsystem nutzt, können wir aussagekräftige Ergebnisse erzielen.

Zusätzlich sollten auf dem Testsystem die nötigen Tools eingerichtet werden, die zur späteren Auswertung benötigt werden. Die durchschnittlichen Ladezeiten kann ich mit Locust selbst oder Tideways am besten auswerten. Für die Serverressourcen (CPU, RAM etc.) bietet unser Hoster (Maxcluster) eine Statistik, in der man die jeweiligen Werte über einen zeitlichen Verlauf einsehen kann.

Meinen Lasttest für diese Anleitung führe ich auf einem Hetzner-Server durch. Bei einem echten Kunden solltest Du mit Deinem Hoster wegen einer Testumgebung sprechen.

Schritt 5: User-Anzahl für den Tests berechnen

Um den vierfachen Traffic zu messen, müssen wir zunächst ermitteln, wie viele Locust-User den aktuellen Traffic grob abbilden. Da verschiedene User-Typen (API-Requests & Storefront) involviert sind, ist es nicht ganz einfach, genaue Zahlen zu bekommen. Hier ist das Vorgehen, das ich in unserem Fall anwende:

1. Anzahl der Sync-User ermitteln

Der Kunde führt viele Stock- und Price-Updates über die Bulk-API durch. Für den Test müssen wir herausfinden, wie viele dieser Requests aktuell in einem bestimmten Zeitraum stattfinden.

  • Access-Logs analysieren: Ich schaue mir die Access-Logs an, um die Anzahl der Requests (Stock-, Price- und allgemeine Produkt-Updates) zu ermitteln. Nehmen wir an, unser Kunde hat aktuell folgende Werte:
    • 1000 Price-Updates
    • 4000 Stock-Updates
    • 20 allgemeine Produkt-Updates
  • User-Anzahl berechnen: Für jeden API-Request-Typ erstellen wir einen eigenen User. Um die notwendige Anzahl an Usern zu bestimmen, kalkuliere ich basierend auf der wait_time zwischen den Tasks.
    • Bei Price-Updates haben wir z.B. 1000 Requests pro Stunde. Mit einer wait_time von ca. 5 Sekunden zwischen den Requests brauchen wir etwa 1,4 User. In dieser Rechnung ist der Request selbst aber nicht mit eingerechnet. Realistisch betrachtet liegt die Anzahl der benötigten User höher, weshalb wir auf 2 aufrunden.
  • Fixed Count verwenden: Mit der Einstellung fixed_count=2 können wir bei einem User sicherstellen, dass für diesen Task immer genau 2 User verwendet werden, egal wie viele User insgesamt im Testsystem unterwegs sind. Dies ist sinnvoll, da die API-Request-Menge in der Realität nicht stark schwankt, selbst bei höherem Traffic.
  • Test der API-User-Anzahl: Ein erster Test könnte zeigen, ob die Anzahl von 2 Usern tatsächlich etwa 1000 Price-Updates in der Stunde abdeckt. Falls nötig, passen wir die User-Anzahl nochmals an.

Diesen Ansatz verfolgen wir auch für die anderen API-Requests (Stock und Produkt-Updates), sodass wir am Ende die Anzahl aller API-User haben.

2. Kunden-Requests analysieren

Bei den Kunden-Requests (Storefront-Nutzer) ist die Analyse etwas komplexer, da wir mehrere User-Typen (User mit und ohne Bestellungen) haben, die alle eine unterschiedliche Anzahl an Listings & Detailseiten aufrufen.

Ich gehe hierbei so vor, dass ich mir zunächst die Anzahl der Bestellungen pro Stunde anschaue und darüber ausrechne, wie viele SurfWithOrder und FastOrder User ich ungefähr brauche. Über fixed_count kann ich hier ebenfalls eine feste Anzahl definieren.

Im nächsten Schritt lasse ich einen Lasttest laufen und prüfe, wie viele Requests allgemein noch fehlen. Diese fülle ich dann mit normalen Visitor-Usern auf, die keine Bestellung durchführen und nur normale Requests machen.

Durch diese Vorgehensweise ermitteln wir den aktuellen Traffic, sowohl für API-Requests als auch für Kunden-Requests, und stellen sicher, dass unser Lasttest realistisch abläuft.

3. Erster Test zur Überprüfung der Systemressourcen

Nachdem die User-Anzahl für den Benchmark festgelegt ist, führen wir einen ersten Testlauf durch. Dieser Test hilft uns zu prüfen, ob die gesammelten Kennzahlen für durchschnittliche Ladezeiten, CPU-Auslastung und RAM-Nutzung im Testsystem den Werten des Produktivshops entsprechen. Falls nötig, passen wir die Konfiguration an.

4. Traffic für den Lasttest vervierfachen

Nach diesem ersten Test haben wir eine Basis, die dem aktuellen Live-Traffic entspricht. Nun können wir die ermittelten User-Zahlen ganz einfach vervierfachen, um den erwarteten Peak-Traffic zu simulieren, und starten den finalen Benchmark.

Meinen Test führe ich mit folgender Konfiguration durch:

User insgesamt: 10

Sync-User
    fixed_count 2
    wait_time = between(2,5)

Visitor
    weight = 10
    wait_time = between(2,5)

SurfWithOrder
    weight = 6
    wait_time = between(2, 5)

FastOrder
    weight = 4
    wait_time = between(2,5)

Schritt 6: Benchmark durchführen

Der eigentliche Lasttest ist nun der einfachste Schritt, nachdem alle Vorbereitungen getroffen wurden. Es geht darum, den Test mit dem richtigen Szenario und der zuvor ermittelten Konfiguration zu starten.

Über den entsprechenden Command starten wir den Benchmark und können schonmal die durchschnittlichen Ladezeiten und Auslastung nebenbei beobachten. In diesem Schritt ist allerdings nicht viel zu tun.

In meinem Fall führe ich dafür folgenden Command in der Konsole aus:

php bin/console emz:run

Die Locust-Statistiken sehe ich bei mir unter https://localhost:8089/

Schritt 7: Auswertung des Lasttests

Nach dem erfolgreichen Abschluss des Lasttests geht es nun darum, die gesammelten Daten auszuwerten und zu prüfen, ob die vorher definierten KPIs (z.B. Ladezeiten, CPU-Auslastung) im Rahmen des festgelegten Performance-Budgets geblieben sind.

Die Ladezeiten können wir theoretisch einfach aus Tideways oder Locust selbst entnehmen und sie mit den Ladezeiten unseres ersten Lasttests (mit dem gleichen Traffic wie Produktiv) vergleichen.

Die Server-Ressourcen (CPU, RAM etc.) können wir wie oben besprochen über z.B. das Monitoring-Tool von Maxcluster vergleichen.

locust statistik 1

In meinem Fall kann ich folgende Werte messen:

Startseite ø ca. 110ms

Listing ø 410ms

Detailseite ø 240ms

CPU load average ø ca. 0.98

RAM-Verbrauch ø 2,14GB

Die Performance-Budgets wurden eingehalten und der Shop war während des Tests weiterhin stabil besuchbar. Der Lasttest wurde damit bestanden 🙂

locust statistik 1

Schritt 8: Probleme untersuchen

Falls Dein Shop nicht mit der getesteten Last zurechtkommt, geht es in die Fehlersuche. Ich würde wahrscheinlich im ersten Schritt mit Tideways die einzelnen Requests anschauen und prüfen, welche Prozesse am meisten Last verursachen, diese entsprechend optimieren und dann erneute Lasttests starten.

Hier ist noch ein Beitrag von mir, wie man den Server mit ein paar PHP-Einstellungen optimieren kann.

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert