Startseite / Artikel

Jenseits von P95: Messung der Verzögerung, die Ihre Nutzer tatsächlich erleben

Ein gesunder P95-Wert kann neben einem langsamen Produkt liegen. Wie Wartezeiten und Ausbreitungszeiten vor den Dashboards verborgen bleiben und wie eine schrittweise Beobachtbarkeit Spielchen bezüglich der Latenz beseitigt.

Verlag: REACTAPP.TOP17 Min. Lesezeit3974 Wörter

Quellen: Your P95 Latency Is Lying to You.

Ein Kunde berichtet, dass das Produkt langsam wirke. Jemand öffnet das Latenz-Dashboard – alles zeigt grün: Der P95-Wert liegt im Zielbereich, die Fehlerrate ist konstant, der CPU-Verbrauch niedrig, und die Datenbankverbindungen liegen weit unter dem Pool-Limit. Die verlockende Schlussfolgerung ist, dass sich der Beschwerde ein Fehler unterliegen muss; doch dieses Instinkt, einem Diagramm mehr Glauben zu schenken als einer Person, die einfach gewartet hat, ist der eigentliche Fehler.

In den folgenden Abschnitten wird erklärt, warum gleichzeitig ein akzeptabler P95-Wert und ein langsam wirkendes Produkt möglich sind. Sie werden erfahren, wo sich die Latenz vor den Service-Level-Metriken (Warteschlangen, Wiederholungsversuche, Verteilung der Anfragen, blockierende Entscheidungen) versteckt, warum der „Schwanz“ der Verteilung wichtiger ist, als seine Größe vermuten lässt, und wie die einfache Gewohnheit, jeden Schritt einer Anfrage nach seinem Namen zu zeitigen, vage Beschwerden in konkrete Beweise verwandelt. Am Ende sollten Sie in der Lage sein, Aussagen wie „Der P95-Wert ist in Ordnung“ sinnvoll zu hinterfragen, anstatt sie entweder hinzunehmen oder abzutun.

Was die P95 Ihnen mitteilt und was nicht

Es ist sinnvoll, präzise zu sein, bevor man diese Kennzahl kritisiert. Die P95 ist der Wert, bei oder unter dem 95 Prozent der gemessenen Anfragen abgeschlossen werden. Es handelt sich dabei um eine wirklich nützliche Zahl, die einen erheblichen Vorteil gegenüber der durchschnittlichen Latenz bietet. Bei dem Durchschnitt verschwinden die Probleme in der Produktion: Wenn neunundneunzig Anfragen in 50 ms abgeschlossen werden und eine fünf Sekunden dauert, beträgt der Durchschnitt etwa 100 ms – eine Zahl, wegen der niemand jemanden kontaktieren würde. Ein Prozentsatz hingegen lenkt zumindest die Aufmerksamkeit auf die Ränder der Verteilung.

Daher liegt das Problem nicht im Prozentil an sich. Das Problem ist die Bedeutung, die Teams ihm nach der Lektüre beimessen. Der Satz „P95 ist in Ordnung“ dient oft als Rechtfertigung, die die Diskussion beendet. Damit wird stillschweigend etwas Grundlegendes vergessen: Niemand, der Ihr Produkt verwendet, hat mit einem Prozentil zu tun. Die Nutzer erleben Wartezeiten. Sie drücken einen Knopf und fragen sich, ob die Aktion registriert wurde. Sie sehen eine Seite, die scheinbar geladen ist, aber nicht reagiert. Sie beobachten einen Ladebalken, während mehrere dahinterliegende Dienste jeweils völlig angemessene Werte melden.

Eine API kann ein hervorragendes P95 aufweisen, während das darauf basierende Produkt schwerfällig wirkt. Beide Fakten gleichzeitig zu berücksichtigen, ist der Ausgangspunkt für alles Weitere.

Drei „Uhren“ laufen bei jeder Anfrage

Die meisten Diskussionen über Latenz gehen von einer einzigen „Uhr“ aus. In der Praxis gibt es mindestens drei, und sie messen unterschiedliche Dinge.

  • Die Serveruhr. Eine Anfrage erreicht Ihren Service, eine Antwort wird gesendet, der Timer stoppt. Das ist es, was die meisten Backend-Dashboards angeben.
  • Die Abhängigkeitsuhr. Innerhalb und um diese Anfrage herum vergeht Zeit mit der Authentifizierung, dem Warten auf eine gemeinsam genutzte Datenbankverbindung, internen HTTP-Aufrufen, Cache-Fehlern, Wiederholungsversuchen, Warteschlangen, Abfragen von Feature-Flags, Nutzung von Drittanbieter-APIs sowie allen Komponenten, die „normalerweise schnell“ sind.
  • Die menschliche Uhr. Der Benutzer klickt, wartet anschließend, bis der Bildschirm wieder nutzbar ist. Dies ist das einzige Intervall, das für den Benutzer von Bedeutung ist.

Teams neigen dazu, zunächst die Zeitmessung zu optimieren und das Produkt anschließend als schnell zu bezeichnen. Das untenstehende Diagramm zeigt die drei Werte nebeneinander: Der Service weist einen sauberen Wert von 180 ms auf, während die Abhängigkeitskette eine Wiederholung mit 600 ms beinhaltet und die Person am Keyboard immer noch fragt, warum die Seite lädt.

SERVER CLOCK

request in  ------------------------------> response out
              "API P95: 180ms"

DEPENDENCY CLOCK
request in  ---> auth ---> db ---> service A ---> retry ---> service B ---> response out
                 40ms    70ms      120ms        600ms       90ms

HUMAN CLOCK
click  ----------------------------------------------------------------> usable screen
       "Why is this still loading?"

Am Serveruhrzeitwert liegt nichts falsch. Er ist einfach unvollständig. Die Geschwindigkeit des Produkts daran zu beurteilen, ist wie das Messen der Körpertemperatur und die Feststellung, alle Organe seien gesund: Manchmal ist genau diese Messung entscheidend, manchmal übergeht sie jedoch die eigentliche Erkrankung.

Ein Endpunkt ist eine Tür, eine Nutzerreise ist ein Gebäude

Ein nützliches mentales Modell: Ein Backend-Endpunkt ist eine einzelne Tür, während eine Nutzerreise ein Gang durch ein ganzes Gebäude darstellt. Dashboards messen hauptsächlich Türen – eine Anfrage kommt herein, eine Antwort geht heraus, das Diagramm wird aktualisiert und alle beruhigen sich.

Die Nutzer sind jedoch nicht da, um sich eine Tür anzusehen. Sie möchten ein Ziel erreichen: den Startbildschirm öffnen, eine Zahlung senden, einen Auftrag platzieren, eine Auszugsdarstellung herunterladen oder eine echte Aufgabe erledigen, ohne das Gefühl zu haben, dass das Produkt zögert. Diese Aufgabe entspricht selten nur einem einzigen Anruf im Hintergrundsystem. Das Öffnen eines Dashboards in einer typischen Single-Page-App sieht eher wie die unten dargestellte Abfolge aus, bei der das Programm zunächst Profil-, Berechtigungs-, Konto-, Angebot-, Benachrichtigungs- und Limitinformationen anfordert, bevor etwas Nützliches angezeigt wird.

User Clicks "Open Dashboard"
        |
        v
+----------------------+
| Frontend Shell       |
+----------------------+
        |
        +------> GET /profile
        |
        +------> GET /permissions
        |
        +------> GET /accounts
        |
        +------> GET /offers
        |
        +------> GET /notifications
        |
        +------> GET /limits
        |
        v
Screen Finally Becomes Useful

Jeder dieser sechs Endpunkte kann für sich genommen gesund erscheinen, während der Gesamtprozess insgesamt gestört wirkt. Ein Service kann seine SLA erfüllen und dennoch zu einem langsamen Produkt beitragen. Ein Team kann seine eigenen Ziele erreichen und dennoch zu einer schlechten Benutzererfahrung beitragen. Wenn die Anfragen nacheinander gestartet werden, addieren sich ihre Latenzen; wenn sie parallel ausgeführt werden, bestimmt die langsamste Anfrage das Tempo, und die Verbindungsbeschränkungen des Browsers pro Ursprung oder Abhängigkeiten zwischen den Anfragen können Arbeit serialisieren, von der man annahm, sie sei gleichzeitig ablaufend.

Deshalb ist „unsere API ist schnell“ erst der Beginn einer Untersuchung, keine Antwort. Die folgenden Fragen sind unangenehm, aber notwendig: Schnell im Vergleich zu was, schnell für wen und wo wird die Geschwindigkeit gemessen? Innerhalb der Grenzen Ihres Services oder ab dem Moment, in dem der Benutzer geklickt hat?

Die verbleibenden fünf Prozent sind kein Rundungsfehler

Der Name „P95“ lässt den schlechten Wert klein erscheinen. Fünf Prozent wirken wie ein akzeptabler Betriebskostenanteil bei großem Umfang – etwas, was ein gelassenes Gremium problemlos verkraften kann.

Doch setzt man echte Mengen ein, ändert sich das Bild: Ein System, das täglich eine Million Anfragen mit einem P95-Schwellenwert abwickelt, hat jeden Tag fünfzigtausend Anfragen darüber hinaus. Jede dieser Anfragen ist ein Moment, in dem jemand erneut klicken, den Inhalt aktualisieren oder den Prozess abbrechen und das Produkt dafür verantwortlich machen könnte.

Sogar diese Darstellung ist zu vereinfacht, denn langsame Anfragen sind nicht gleichmäßig auf die Nutzer verteilt:

  • Ein unglücklicher Benutzer kann immer wieder auf den langsamen Weg geraten.
  • Ein einzelner Unternehmenskunde kann an einen überlasteten Rechenabschnitt gebunden sein.
  • Eine Region kann in ihrer Leistung nachlassen, während der Gesamtwert weiterhin akzeptabel bleibt.
  • Ein wertvoller Arbeitsablauf kann langsam sein, während günstige, häufige Anfragen den Prozentsatz gut aussehen lassen.

Das Dashboard zeigt eine Verteilung. Der betroffene Benutzer erlebt etwas Persönliches. Aus Sicht des Systems waren nur ein Bruchteil der Anfragen langsam; aus Sicht des Benutzers hat das Produkt gezielt seine Zeit verschwendet.

Deshalb verdienen Benutzerbeschwerden, als Daten betrachtet zu werden. Die Leute berichten nicht in Prozentwerten, sondern in Geschichten: „Es hangt jedes Mal ein, nachdem ich auf Senden geklickt habe.“ „Die Seite erscheint, aber die Zahlen kommen spät.“ „Ich muss zweimal neu laden.“ „Es funktioniert, aber es wirkt träge.“ Keine dieser Beschwerden ist vage – jede ist ein Leistungsbericht, der in einfachen Worten verfasst wurde. Das gemeinsame Problem besteht darin, ihn in die falschen Metriken umzurechnen und zu dem Schluss zu kommen, dass nichts falsch ist.

Zwei Dienste, ein P95 – sehr unterschiedliches Risiko

Ein einzelner Prozentsil sagt nichts über die Form der um ihn herum liegenden Verteilung aus. Betrachten wir zwei Dienste mit einem ähnlichen P95-Wert. Der erste ist unbedeutend: Die meisten Anfragen sind mittelmäßig, die langsamen sind nur etwas langsamer, und es gibt keinen plötzlichen Anstieg der Latenz. Der zweite ist meist schnell, fällt aber gelegentlich in den Bereich mehrerer Sekunden ab.

SERVICE A

Most Requests        120ms - 260ms
Slow Requests        300ms - 380ms
Disaster Requests    Rare

SERVICE B

Most Requests        40ms - 80ms
Slow Requests        300ms
Disaster Requests    8s - 12s

Sie können beide in etwa denselben P95-Wert angeben. Nur Dienst B weist eine „Falltür“ auf, die sich vollständig jenseits des 95. Prozentsils befindet – dort kann der Wert sie nicht erkennen. Nur durch die Betrachtung von P99, P99,9 und dem Maximum oder besser noch durch einen Latenz-Histogramm oder -Heatmap wird dieser Unterschied sichtbar.

Ein Prozentsil verdeckt außerdem, wo sich die Probleme konzentrieren. Es verrät Ihnen nicht, ob die langsamen Anfragen einem bestimmten Mieter, einer Endpunkt-Instanz, einer Datenbankpartition, einer abhängigen Komponente, einer Region, einem Wiederholungsversuchsweg, einem überlasteten Threadpool oder einer Funktion zugeordnet werden können, die nur während wichtiger Demonstrationen genutzt wird. Die Aufschlüsselung der Latenz nach diesen Kriterien verwandelt eine Zahl in eine Diagnose.

Die praktische Regel: Ein Prozentsildashboard sollte ein Gespräch anstoßen, niemals beenden. Die Frage lautet nicht nur „Was ist unser P95?“, sondern auch „Welche Art von Problem steckt hinter dieser Zahl?“

Die größten Latenzprobleme entstehen zwischen Teams

Einige Verzögerungsprobleme sind fast angenehm zu beheben, weil ihre Ursache offensichtlich ist. Eine langsame Abfrage erhält einen Index. Ein fehlender Cache wird vorsichtig hinzugefügt. Ein zu großer Datenträgerumfang wird gekürzt. Eine ineffiziente Schleife wird umgeschrieben. Jeder versteht das.

Die schwierigen Fälle entstehen in den Lücken zwischen den Teams. Die Backend-Abteilung meldet, dass die API funktionsfähig ist. Das Frontend gibt an, auf das Backend zu warten. Die Plattform teilt mit, dass das Gateway in Ordnung ist. Das Datenbankteam berichtet, dass die Datenbank funktioniert. Das Produktmanagement gibt an, dass die Nutzer weiterhin unzufrieden sind. Jede Abteilung sagt die Wahrheit – genau das macht die Situation so frustrierend.

In solchen Fällen ist das System nicht an einer einzigen offensichtlichen Stelle langsam. Es ist in den Verbindungsstellen langsam:

  • bei der Übertragung zwischen Diensten;
  • beim erneuten Versuch, der die Dauer einer Anfrage verdoppelt;
  • bei einer zweiten Anfrage, die eigentlich nie gestartet werden sollte;
  • bei einer Berechtigungsprüfung, die nachdem mit aufwändiger Arbeit bereits begonnen wurde, durchgeführt wird;
  • bei einem Cache-Miss, das unbemerkt bleibt, weil die Endpunktreaktion den Alarmgrenzwert trotzdem nicht überschreitet;
  • bei einem Drittanbieterdienst, den alle als „externe Abhängigkeit“ bezeichnen – das klingt weniger unangemessen als zuzugeben, dass er Teil der Benutzererfahrung ist;
  • in einer Warteschlange, noch bevor Ihr Codeablauf überhaupt startet;
  • bei der Serialisierung, nachdem die Geschäftslogik abgeschlossen ist;
  • im Frontend, weil die API Daten zurückgibt, die für die Datenbank und nicht für den Bildschirm konzipiert sind, wodurch der Client diese neu zusammensetzen muss.
  • P95 hat niemanden durch falsche Werte getäuscht – es hat durch seine Isolation getäuscht. Eine isolierte Metrik ermöglicht es jedem Team, seine Unschuld zu beweisen, während der Benutzer weiter wartet.

    Die Zeit in einer Warteschlange zählt weiterhin zur Latenz

    Es gibt ein subtileres Messproblem: Timer auf Anwendungs-Ebene starten oft zu spät. Wenn Ihr Handler oder Controller mit der Ausführung beginnt, hat die Anfrage möglicherweise bereits Zeit damit verbracht, am Load Balancer zu warten, auf eine Verbindung zu warten, auf einen Worker oder Thread zu warten oder hinter anderen Aufgaben zurückzustehen – weil der Prozess zwar technisch aktiv war, aber faktisch überlastet war. In Node.js zeigt sich derselbe Effekt als Verzögerung im Event-Loop: Ein stark belasteter Loop bedeutet, dass Callbacks, einschließlich Ihres Anfrage-Handlers, später starten als der Socket die Daten erhalten hat.

    Dann startet Ihr Timer, Ihr Code wird schnell ausgeführt und Ihre Metrik erfasst eine schnelle Anfrage. Dem Benutzer ist es egal, wo die Grenze Ihres Stoppuhrens lag. Das Warten vor dem Start des Timers zählt ebenfalls. Ebenso zählen das Warten während von Wiederholungsversuchen, das Warten darauf, dass der Browser den nächsten erforderlichen Aufruf sendet, und das Warten darauf, dass eine deaktivierte Schaltfläche wieder klickbar wird.

    Dies ist die Falle der Messgrenzen. Teams messen das, was ihr Service beobachten kann, und betrachten anschließend alles, was außerhalb dieses Sichtfelds liegt, implizit als nicht existent. Latenz, die man nicht sehen kann, ist keine Einbildung; es handelt sich dabei um Latenz, für die niemand verantwortlich ist. Unverantwortete Latenz ist in der Regel die teuerste Art, denn niemand trägt die Verantwortung, sie zu verringern. Anfragedauer von Load-Balancern, Wartezeiten im Connection-Pool, Verzögerungen im Event-Loop sowie Zeitmessungen auf der Browser-Seite sind die üblichen Methoden, um diese Intervalle wieder sichtbar zu machen.

    Jeder Schritt der Anfrage benennen

    Was diese Auseinandersetzungen in der Regel beendet, ist weder ein weiteres Dashboard, ein neuer Anbieter noch ein höherer Budgetrahmen für die Überwachbarkeit. Es geht vielmehr darum, den langsamen Teil unsichtbar zu machen, indem jedem Schritt der Anfrage ein ehrlicher Name gegeben wird.

    Der untenstehende Auszug ist ein Spring Boot-Handler in Java, doch das Muster lässt sich direkt auf Express, Fastify oder NestJS übertragen. Für GET /home wird eine LatencyTrace gestartet, die mit dem eingehenden X-Request-Id-Header verknüpft wird. Jeder bedeutungsvolle Schritt – das Laden des Benutzers, das Abfragen von Berechtigungen, der Aufruf von Konten- und Angebotsservices sowie die Kartierung der Antwort – wird in trace.time(...) mit einer beschreibenden Beschriftung eingebettet. Im finally-Block gibt trace.logIfTotalCrosses(Duration.ofMillis(700)) die Aufschlüsselung nur dann aus, wenn die gesamte Anfrage länger als 700 ms dauert; dadurch bleibt das Protokoll bei normaler Trafficmenge unbeschrieben und enthält detaillierte Informationen bei langsamerem Traffic. Die genaue Implementierung ist weniger wichtig als die Gewohnheit, die sie fördert.

    @GetMapping("/home")
    public HomeResponse home(@RequestHeader("X-Request-Id") String requestId) {
        LatencyTrace trace = LatencyTrace.start("GET /home", requestId);
    try {
            User user = trace.time("db.user", () ->
                userRepository.findActiveUser()
            );
            Permissions permissions = trace.time("auth.permissions", () ->
                permissionClient.fetch(user.id())
            );
            List<Account> accounts = trace.time("accounts.http", () ->
                accountClient.fetchAccounts(user.id())
            );
            List<Offer> offers = trace.time("offers.http", () ->
                offerClient.fetchEligibleOffers(user.id())
            );
            return trace.time("response.mapping", () ->
                HomeResponse.from(user, permissions, accounts, offers)
            );
        } finally {
            trace.logIfTotalCrosses(Duration.ofMillis(700));
        }
    }
    

    Zwei Aspekte sind erwähnenswert. Erstens garantiert finally, dass der Fehlerverlauf aufgezeichnet wird, selbst wenn einer der Schritte eine Ausnahme wirft – genau dann, wenn man das am meisten benötigt. Zweitens werden die Schritte hier nacheinander ausgeführt, sodass die Gesamtzeit ungefähr ihrer Summe entspricht; wenn man unabhängige Aufrufe wie solche für Konten und Angebote parallelisiert, sollte die Zeitverfolgung dennoch jede einzelne Aufrufzeit erfassen, damit man erkennen kann, welcher von ihnen das Tempo bestimmt.

    Vergleichen Sie, was ein gewöhnliches Latenzprotokoll liefert, mit dem Ergebnis dieses Ansatzes. Die gewöhnliche Version zeigt nur eine einzige Zeile mit der Gesamtzeit:

    GET /home took 1180ms
    

    Es ist genau, aber fast nutzlos, denn es sagt nichts darüber aus, wohin die 1.180 ms geflossen sind. Die version mit Schrittinformationen beantwortet diese Frage sofort:

    GET /home took 1180ms
    
    db.user              42ms
    auth.permissions     81ms
    accounts.http        156ms
    offers.http          823ms
    response.mapping     28ms
    

    Durch diese Darstellung vor der Gruppe ändert sich der Ton der Diskussion. Niemand muss raten oder seinen eigenen Service verteidigen, und „Es funktioniert bei mir“ ist dann keine sinnvolle Beitragsform mehr. Die Anfragen bezüglich der Funktionalität machen etwa 70 Prozent aus, wodurch die Unterhaltung von Meinungen zu Fakten übergeht. Das ist der eigentliche Nutzen guter Instrumentierung: nicht mehr viele Diagramme, sondern weniger Argumente.

    In einer Node.js-Codebasis kann man denselben Effekt erzielen, indem man performance.now() um jeden wartenden Aufruf herum verwendet und einen strukturierten Logger einsetzt, oder systematischer mit OpenTelemetry-Spans – diese tragen die gleichen Schrittnamen über Servicegrenzen hinweg, sodass die Aufschlüsselung erhalten bleibt, wenn der langsame Schritt sich in einem anderen Service befindet. Für eine ausführlichere Behandlung der Kombination von Logs, Metriken und Spuren siehe building observable Node.js APIs.

    Warum saubere Benchmarks ein unübersichtliches System klären können

    An dieser Stelle würde ein Leistungsbericht normalerweise eine Benchmark-Tabelle präsentieren. Es gibt einen guten Grund, dies hier nicht zu tun. Benchmarks sind wertvoll, wenn sie ehrlich sind – doch ein erfundener Wert für dieses Problem wäre lediglich eine weitere irreführende Zahl in formeller Verpackung.

    Das Problem liegt nicht darin, dass Ingenieure keine kontrollierten Tests durchführen können. Es liegt vielmehr daran, dass solche Tests tendenziell genau die Probleme beseitigen, die den Nutzern schaden:

    • Ein Benchmark testet nur einen Endpunkt; eine echte Benutzeraktion löst viele aus.
    • Ein Benchmark wird mit vorgeheizten Caches ausgeführt; Nutzer tauchen erst nach Deploys, Löschvorgängen und Nutzung von „kalten“ Codepfaden auf.
    • Ein Benchmark misst erfolgreiche Antworten; Nutzer müssen sich mit teilweisen Fehlern, Wiederholungsversuchen, Zeitüberschreitungen und deaktivierten Bildschirmen abfinden.
    • Ein Benchmark geht von stabilen Abhängigkeiten aus; Produktionsabhängigkeiten funktionieren meist einwandfrei – bis zu dem Tag, an dem das nicht mehr der Fall ist.
    • Ein Benchmark isoliert einen Dienst; was die Nutzer nutzen, setzt sich aus vielen Diensten sowie Browsern, Netzwerken, Warteschlangen, API-Verträgen und Entscheidungen zusammen, die von Teams getroffen werden, die möglicherweise nicht mehr miteinander kommunizieren.

    Ein Benchmark beschreibt, wie schnell ein kontrollierter Ablauf sein kann. Die Produktleistung hingegen zeigt an, wie zuverlässig das gesamte System ist. Das sind unterschiedliche Messgrößen, und die eine ersetzt die andere nicht.

    Schneller Code garantiert kein schnelles Produkt

    Viele Backend-Entwickler finden lokale Optimierungen äußerst befriedigend. Man reduziert die Laufzeit einer Funktion von 20 ms auf 8 ms, entfernt eine Speicherallokation, strukturiert einen Schleifenaufruf um oder optimiert eine Abfrage – der Unterschied wirkt dann ansprechend. Gleichzeitig wartet das Produkt weiterhin auf einen entfernten Dienst, der bei jedem zehnten Aufruf 900 ms benötigt. Der Bildschirm bleibt weiterhin zu lange unbrauchbar, die Schaltflächen frieren weiterhin ein und in den Supportanfragen steht weiterhin „langsam“.

    Das ist so, als würde man Messing polieren, während der Boden unter den Füßen nachgibt. Lokale Lösungen sind attraktiv, weil sie vollständig unter Ihrer Kontrolle liegen. Die Gesamtlatenz ist komplizierter, da ihre Verbesserung andere Teams, veraltete Verträge, Produktentscheidungen sowie eine Architektur erfordert, die niemand gerne wieder überarbeiten möchte. Die Nutzer belohnen jedoch nicht die lokale Eleganz, sondern abgeschlossene Aktionen. Eine schnelle Funktion ist schön, ein schneller Endpunkt noch besser – doch erst eine schnelle Gesamtlösung zählt wirklich. Bevor man irgendeinen einzelnen Bestandteil optimiert, hilft es, zu überprüfen, ob er auf dem kritischen Pfad eines Prozesses liegt, der für jemanden von Bedeutung ist.

    Wenn die langsamste Abhängigkeit eine Entscheidung ist

    Nicht jedes Latenzproblem ist technischer Natur. Manche entstehen durch Entscheidungen, die sich in der Architektur festgesetzt haben:

    • Die Frontend-Anwendung ruft sechs APIs auf, obwohl eine zusammengefasste Antwort ausreichen würde.
  • Jeder Bildschirm fragt den Berechtigungsdienst erneut an, anstatt ein bereits vorhandenes Ergebnis zu verwenden.
  • Die Wiederholungsversuche finden synchron innerhalb des für den Benutzer sichtbaren Anfragepfads statt.
  • Ein Angebots-Widget blockiert die Darstellung des gesamten Startbildschirms.
  • Analytics-Ereignisse müssen ausgelöst werden, bevor dem Benutzer die Fortsetzung gestattet wird.
  • Die API gibt datenbankähnliche Objekte zurück und überlässt es dem Frontend, daraus Bedeutung abzuleiten.
  • Ein nachgelagerter Dienst wird als „kritisch“ bezeichnet, weil niemand bereit war zuzugeben, dass seine Funktion später geladen werden könnte.
  • Der langsamste Teil eines Systems ist oft eine Produktwahl, die nie gemessen wurde, ein Besitzproblem oder ein längst abgehaltener Treffen, dessen Ergebnis zu dauerhafter Latenz führte. Für viele dieser Probleme gibt es bekannte Lösungen, wie beispielsweise ein Backend-for-Frontend, das Anfragen bündelt, träge oder verzögerte Ladevorgänge von nicht essentiellen Komponenten, das Verschieben von Wiederholungsversuchen und Analysen außerhalb des kritischen Pfades sowie Antwortstrukturen, die speziell auf den Bildschirm zugeschnitten sind. Jede dieser Lösungen erfordert jedoch, dass jemand die Befugnis hat, eine Entscheidung zu ändern.

    Deshalb wird die Leistungsoptimierung politisch, sobald die einfachen Lösungen umgesetzt sind. Die verbleibende Latenz wird zu einer Frage der Verantwortung. Wer darf eine Abhängigkeit als verpflichtend deklarieren? Wer ist für den gesamten Ablauf verantwortlich? Wer darf eine Anfrage entfernen? Wer ist schuld, wenn alle Dienste funktionieren, aber die Benutzererfahrung weiterhin schlecht ist? P95 kann keine dieser Fragen beantworten – und dazu war es auch nie bestimmt.

    Grüne Dashboards können Teams weniger neugierig machen

    Eine weniger angenehme Beobachtung: Ein grünes Dashboard kann dazu führen, dass ein Team aufhört, Fragen zu stellen. Nicht immer, aber häufig genug, um von Bedeutung zu sein. Wenn ein Diagramm rot wird, untersucht jeder es. Wenn das Diagramm grün ist und ein Nutzer sich beschwert, neigt die Diskussion dazu, die Beschwerde abzutun. Vielleicht war das Netzwerk schlecht. Vielleicht war der Browser langsam. Vielleicht handelte es sich um einen Einzelfall, oder der Support hat übertrieben, oder die Frontend-Plattform hat etwas Seltsames getan, oder der Kunde hat zu früh geklickt, oder die Beschwerde ist emotional und nicht technischer Natur.

    So verlieren Teams das Vertrauen – nicht durch mangelnde Daten, sondern dadurch, dass sie Daten als Grund nutzen, nicht zuzuhören. Der Nutzer besitzt Ihr Dashboard nicht; er besitzt Ihr Produkt. Wenn es für ihn langsam wirkt, ist das ein Beweis. Es ist kein vollständiger oder perfekter Beweis, aber es ist ein Beweis.

    Eine gesunde Ingenieurkultur betrachtet das Diagramm als einen Zeugen und den Benutzer als einen weiteren, wobei die Produktion der Ort ist, der untersucht werden muss. Eine Kultur, die Diagramme als Wahrheit und Benutzer als Störgeräusche ansieht, liefert langsame Produkte aus, obwohl sie glaubt, diese seien schnell.

    Fangen Sie bei dem an, was der Benutzer erreichen wollte

    Leistungsdiskussionen drehen sich in der Regel um Komponenten: Welcher Dienst, welches Endpunkt, welche Abfrage ist langsam? Das sind gute Fragen, aber selten die beste erste Frage. Besser ist: Was wollte der Benutzer erreichen? Sobald Sie diese Frage beantwortet haben, ergibt sich in der Regel von selbst die richtige Messung:

    • Für das Öffnen eines Dashboards: Messen Sie die Zeit bis zum tatsächlichen Einsatzbereitsein des Dashboards.
    • Für das Absenden eines Formulars: Messen Sie die Zeit vom Absenden bis zur sichtbaren Bestätigung.
    • Für das Herunterladen eines Berichts: Messen Sie die Zeit vom Klick bis zur Verfügbarkeit der Datei.
  • Zur Berechnung der Zahlung wird die Strecke vom Anfang bis zum endgültigen abgeschlossenen Zustand gemessen.
  • Das klingt offensichtlich, doch viele Systeme sind nicht auf diese Weise instrumentiert. Sie werden entlang der Teamgrenzen gemessen, weil diese leicht zu instrumentieren sind und übersichtliche Dashboards ergeben. Prozessschritte sind schwieriger zu messen und liefern weniger schmeichelhafte Ergebnisse – gerade aus diesen weniger schmeichelhaften Ergebnissen ergeben sich Verbesserungen des Produkts. Auf der Client-Seite ermöglichen Browser-APIs wie die Performance- und User-Timing-Schnittstellen es, explizit „User hat geklickt“ sowie „Bildschirm ist nutzbar“ zu markieren, und das Verknüpfen dieser Markierungen mit einer Request-ID verbindet die menschliche Zeitmessung wieder mit den Server-Protokollen. Falls der Verzögerungsfaktor im Rendering und nicht im Netzwerk liegt, behandelt der Artikel zu der Diagnose von React-Leistungsproblemen jenseits der API-Antwortzeit diesen Aspekt des Problems.

    Betrachten Sie Metriken mit respektvoller Vorsicht

    Nichts davon ist ein Argument dafür, den P95-Wert von Ihren Dashboards zu entfernen. Er gehört dorthin und ist wichtig – er sollte jedoch nicht allein vertraut werden. Ein guter Prozentsatzwert sollte vielmehr Neugier wecken und auf die Anfragen hinweisen, die sich jenseits der Schwellenwertgrenze befinden:

    • Wer gehört genau zu den fünf Prozent mit langsamen Antworten, und sind es immer dieselben Personen?
    • Zu welchen Benutzerprozessen gehören diese Anfragen?
    • Ist eine Abhängigkeit für den größten Teil der Probleme verantwortlich?
    • Ist eine bestimmte Region beeinträchtigt, obwohl der globale Überblick in Ordnung erscheint?
    • Lidet ein bestimmter Nutzer kontinuierlich am meisten?
    • Verwandeln Wiederholungsversuche heimlich Fehler in lange Wartezeiten?
    • Wie viel Zeit verbringen Anfragen im Warteschlangenmodus, bevor der Timer der Anwendung überhaupt startet?
  • Gibt es einen Frontend-Zustand, der den Bildschirm nach einer bereits eingegangenen Antwort vom Backend blockiert hält?
  • Ist ein Service auf dem Papier funktionsfähig, schadet er aber in der Praxis dem Produkt?
  • Die richtige Haltung gegenüber einer Metrik ist weder Verehrung noch Ablehnung. Es handelt sich um respektvolles Misstrauen. Eine Metrik ist ein Zeuge – und Zeugen können wichtige Dinge auslassen.

    Kernpunkte

    Das gefährliche Szenario ist nicht eine eindeutig falsche Metrik; die würde leicht entdeckt werden. Es handelt sich vielmehr um eine Metrik, die eine kleine Wahrheit so selbstsicher darstellt, dass alle aufhören, nach der größeren zu suchen. Beim nächsten Mal, wenn jemand sagt, P95 sei in Ordnung, streiten Sie nicht mit der Zahl – fragen Sie stattdessen, was dabei ausgeschlossen wurde. Enthielt die Messung die Wartezeit in der Warteschlange, Wiederholungsversuche, das Warten im Frontend, den Aufruf bei Drittanbietern, das erneute Klicken des Benutzers, weil nichts zu geschehen schien, sowie den Moment, in dem der Bildschirm tatsächlich für die vom Kunden zu erledigende Aufgabe nutzbar wurde?

    • P95 übertrifft die Durchschnittswerte, verdeckt aber die Form der Verteilung, die Schwere der Ausreißer sowie die betroffenen Personen; kombinieren Sie es mit höheren Perzentilen, Histogrammen sowie Aufschlüsselungen nach Mieter oder Region.
    • Unterscheiden Sie zwischen Server-, Abhängigkeits- und Benutzeruhren und stellen Sie sicher, dass für jede Uhr eine Verantwortliche festgelegt ist.
    • Eine Benutzerreise besteht in der Regel aus vielen Anrufen; messen Sie die gesamte Reise, nicht nur die Endpunkte.
    • Anwendungs-Timer übersehen oft Wartezeiten in Schlangen, Verbindungsverzögerungen sowie Verzögerungen im Event-Loop; messen Sie diese ausdrücklich.
    • Durch das Zeitmessen jeder einzelnen Schritt eines Anfrages und das Protokollieren von Ausreißern bei Überschreitung eines Schwellenwerts ersetzt man Anschuldigungen durch Beweise.
    • Ein Teil der Latenz entsteht durch Produkt- und Verantwortlichkeitsentscheidungen, und um sie zu beheben, benötigt man jemanden mit Befugnis, diese zu ändern.
    • Betrachten Sie Benutzerbeschwerden als Leistungsberichte und Dashboards als einen von mehreren Belegquellen.

    Zusätzliche Literatur