Was ist schlechte Performance? Letztendlich ist das dem Kunden egal. Der Kunde hat eine Störung in seinem Prozessablauf, wenn er von einem Performanceproblem spricht. Die Störung zeigt sich sicherlich auch an der Software, die der schlechten Performance verdächtigt wird. Ob sie die Ursache ist, ist eine andere Frage. Dies wollen wir klären und zeigen, wie man sich Schritt für Schritt der Ursache oder den Ursachen nähern kann.
Im folgenden Fall hatte der Kunde bei einer Anwendung Probleme mit dem Durchsatz. Es stauten sich im Laufe des Arbeitstags Aufträge, die an ein abgesetztes System gesendet werden sollten, das zentrale Aufgaben für mehrere Geschäftsbereiche erbringt. Die nächsten Tage kommen gewöhnlich jeweils noch einmal so viele Aufträge neu hinzu. Das ist ganz klar eine schwerwiegende Störung im Prozessablauf. Der Kunde hat das im fachlichen Monitoring seiner Prozesskette entdeckt. Wie sich das aber (ganz anders?) DV-technisch auf der Prozesskette darstellt, wollen wir uns genauer anschauen.
In dem dem Störfall zugrunde liegenden Szenario sendet eine Anwendung (Abb. 1) im Hintergrund immer einen Auftrag nach dem anderen an das Backend, das zuständig für die zentralen Aufgaben ist. Dieses verarbeitet den Auftrag und aktualisiert seine Datenbank. Ist der Auftrag erledigt, das heißt, hat der Consumer eine Antwort vom Backend erhalten, schaut er sofort in seiner Datenbank nach, bereitet die Daten auf und sendet einen neuen Auftrag los. Wie heute oft weit verbreitet, geschieht die Kommunikation nicht direkt zwischen Consumer und Provider, sondern über eine SOA-Infrastruktur. Das führt noch zu zusätzlicher Latenz und auch Komplexität, besonders bei der Untersuchung.
Der (Auftrags-)Strom Xconsumer ist ein klassisches Beispiel für einen Strom in einem geschlossenen Netzwerk (Closed Queueing Network). Allerdings fällt schon auf den ersten Blick auf, nicht ganz überraschend, dass der Consumer weniger als eine halbe CPU auf dem Backend verbrauchen sollte. Viele andere Prozesse im Backend dominieren also das Verhalten des Servers. In Abbildung 2 ist schon einmal die Last/Queue Q > 12 zu sehen. Näheres zu Queue Q findet sich im Kasten „Bemerkung zur Last, Queue-Length und Runqueue“. Schon diese Überlastung ist ein Indiz, dass der signifikante Performanceeinbruch (hier ein Durchsatzeinbruch) im Tagesverlauf durch die starke Serverüberlastung des Backend-Systems zustande kommt.
Über die anderen Prozesse wissen wir nicht viel. Die Last wird sicherlich von anderen vielen (statistisch) unabhängigen Consumer-Systemen erzeugt. Eine interne Backend-Verarbeitung trägt auch zur gemessenen Last bei. Diese einzeln als Ströme abzubilden, wird uns sicherlich überfordern. Aber aufgrund der Argumente (und weil wir es nicht besser wissen) gehen wir davon aus, dass hier die Zufälligkeit des Auftragseingangs eine dominierende Rolle spielt. Die Mathematik nimmt in so einem Fall an, dass die eingehenden Aufträge einen so genannten Poisson-Strom bilden. Damit hätten wir hier quasi ein offenes Teilsystem innerhalb dieses Queueing-Netzwerks vorliegen. Später werden wir das ausnutzen. Jetzt ist aber nur die Erkenntnis wichtig, dass diese Teilströme zusammen ein Mixed Queueing Network bilden. Unsere Backend-Anwendung sieht zwei Auftragsströme – den kleinen Xconsumer, erzeugt vom vermeintlich maladen Consumer des geschlossenen Teilsystems, sowie den weitaus größeren Strom Xother systems des offenen. Ein Mixed Queueing Network ist es deswegen, weil wir beide Arten von Queueing-Systemen vorfinden, geschlossen und offen.
Wie wir schon im letzten Abschnitt gesehen haben, machen wir etliche Annahmen: Der Consumer hat kein Queueing, daher arbeiten wir hier mit einer konstanten Servicezeit Z. Bei einer Queue Q meist deutlich kleiner zwei, verhält sich unsere SMP-Maschine auf Consumer-Seite mit sieben Cores wie ein infiniter Server (ISRV): Zconsumer = const. Unsere Servicezeit beschaffen wir uns von dem Monitoring-System oder einfach mit den Unix-Tools nmon oder top. Wir sehen einen Anstieg der Auslastung ΔU in dem Moment, in dem der Prozess gestartet wird (oder Abfall -ΔU, wenn der Prozess sich beendet). In der Datenbank protokollieren wir sowieso, wann welcher Auftrag versendet wurde.
Der Durchsatz ist wie folgt definiert: Xconsumer = <Anzahl der Aufträge> / <Zeitintervall>. Durch einfaches Anwenden des Utilization Laws |ΔU| = S * X = Zconsumer * Xconsumer können wir die unbekannte Servicezeit bestimmen (|ΔU| und X sind bekannt). Durch direktes Logging mit P6Spy können wir auch die Latenz der Datenbank...