In diesem Blog gehe ich auf die Herausforderungen des Last- und Performancetestings vor dem Hintergrund aktueller Technologien und Architekturen wie Microservices und Cloud ein. Im Rahmen moderner Softwareentwicklung müssen traditionelle Ansätze im nichtfunktionalen Test kritisch hinterfragt werden.
Was ändert sich mit Cloud und Microservices?
Zunächst einmal ergeben sich aus dem Wandel hin zu einer verteilten Architektur mit Microservices besondere Implikationen. Im traditionellen Monolithen war die gesamte Anwendung en bloc an einer Stelle konzentriert, abgesehen natürlich von benötigten externen Umsystemen. Die Anwendung war entweder komplett „up and running“ oder eben komplett „down“. Im Kontext Microservices kann es nun sein, dass einzelne Services kurzzeitig nicht verfügbar sind, ohne dass es dem Gesamtkonstrukt von aussen absehbar wäre. In der Softwareentwicklung haben sich für solche Szenarien Lösungsansätze entwickelt wie beispielsweise asynchrone Kommunikation mit Queues, die damit eben nicht eine sofortige und ständige Verfügbarkeit aller Services bedürfen.
Zusätzlich ermöglicht die Cloud für den Betrieb einer Anwendung einen ad-hoc Selfservice, etwa um zusätzliche Rechenleistung zu akquirieren, falls die Anwendung einen erhöhten Bedarf aufweist. Im Zusammenspiel mit Microservices ermöglicht dies somit, einzelne Services separat horizontal zu skalieren, d.h. es werden zur Bewältigung der Last einzelne Services repliziert um mit mehr Instanzen diese Last abbauen zu können. Moderne Orchestrierungsframeworks wie OpenShift oder Kubernetes ermöglichen ein automatisches Skalieren schon out of the box, abhängig von der Last oder z.B. bestimmten Tageszeiten.
Implikation für den LuP-Test
Auch heute sehen wir noch häufig, dass Projekte den Last- und Performancetest erst kurz vor dem Release angehen. Als Erklärung dafür wird oft angeführt, dass das System eben vorher nicht komplett fertig war und damit keine end-2-end Tests möglich waren. Die eigentliche Durchführung der Tests sieht dann so aus, dass auf einer produktivnahen Test-Umgebung - oder sogar einer noch nicht öffentlich freigeschalteten Produktivumgebung - Lasttestsuiten mit end-2-end Szenarien gefahren werden. Daraus ergeben sich aber folgende 3 Probleme:
- Falls der Lasttest Performanceprobleme aufdeckt, ist die Zeit bis zum geplanten Release meist viel zu kurz, um das Problem zu lösen. In der Folge muss dann entweder das Release verschoben werden oder es wird mit dem bekannten Performanceproblem trotzdem live gegangen.
- End-2-end Testsuiten mögen zwar die Präsenz von Performanceproblemen gesamthaft aufdecken, aber sie können meistens nicht direkt die Ursache des Problems benennen. In sehr vielen Fällen sind die Ursachen auch nicht direkt auf einzelne Stellen im Code beschränkt, sondern sind Teil eines architektonischen Problems. Dieses zu lösen ist wie bei Punkt 1 nicht in wenigen Stunden getan, sondern bedarf eines Reviews und möglicherweise auch ein Refactoring der Architektur. Damit sind solche nicht-funktionalen Fehler in ihrer Kostenwirkung analog zu funktionalen Fehlern, die erst spät gefunden werden: die Kosten steigen stark je später der Fehler gefunden wird.
- End-2-end Testsuiten können ein falsches Bild der tatsächlichen Performance liefern. Wie weiter oben erwähnt kann die automatisierte Skalierung einzelner Services Bottlenecks durch horizontale Erweiterung lindern. Im Gesamtergebnis sieht die Performance dann einigermassen in Ordnung aus. Das böse Erwachen kommt dann, wenn das System live geht und der Cloudprovider die erste Rechnung für die genutzten Ressourcen schickt. Dann fällt das erste Mal auf, dass die Performancesteigerungen durch Einsatz von zusätzlichen Ressourcen erkauft wurden und die Anwendung eben nicht per Design performant ist.
Unsere Tipps für Sie
Auch für nicht-funktionale Anforderungen gelten die Implikationen der Testpyramide: Versuchen sie Performancetests bereits in den frühen Phasen der Entwicklung zu etablieren. Halten Sie Performance in Ihren User Stories als Anforderung oder Akzeptanzkriterien fest und versuchen sie Performancetests bereits auf Service-Level zu implementieren. Nur so können Sie punktgenau in einer Microservice Architektur frühzeitig Bottlenecks identifizieren.
Implementieren Sie ausserdem ein umfassendes Monitoring für Ihre Applikation. Nur so sind sie in der Lage, etwaige Probleme auch ex-post fundiert analysieren zu können und mögliche Skalierungseffekte zu identifizieren, bevor die Rechnung des Providers kommt.
Generell macht es Sinn so früh wie möglich im Entwicklungsprozess mit dem Testen anzufangen. Und das gleich auf Unit Ebene. Wo noch überall getestet werden kann, können sie hier erkunden.