Post Mortem: Ausfall des Bikemarkts am Morgen des 5. November 2012

Heute morgen in der S-Bahn erreichte mich die Nachricht unseres automatischen Server- und Dienste-Monitorings, dass der Bikemarkt nicht erreichbar ist. Außerdem gab es zum gleichen Zeitpunkt Meldungen über zu hohe Serverlast beim Bikemarkt.

Ich prüfte die Meldungen und stellte fest, dass der Bikemarkt tatsächlich nicht erreichbar war. Ich loggte mich auf dem Webserver per SSH ein (mit Prompt für iOS) und schaute mir die wichtigsten Daten an. Der MySQL-Server kam mit der Bearbeitung von Anfragen aus der Webapplikation nicht hinterher, was dazu führte, dass die Webapplikation in Schockstarre verfiel und sich ebenfalls nicht mehr bewegen wollte.

Mittlerweile vom iPhone auf’s iPad gewechselt, schaute ich mir die laufenden Abfragen im MySQL-Server an (SHOW FULL PROCESSLIST) an und sah erst mal nichts. Es waren zwar eine Menge Abfragen zu sehen, die aber auf dem ersten Blick alle normal aussahen und auch keine schlimmen Locks verursachten usw.

Ich hatte einen wichtigen Termin heute morgen, so dass ich erst mal sehr schlecht gelaunt alles so ließ wie es war.

Gegen 10 Uhr war ich dann – endlich – im Büro und schaute mir die Sache genauer an. Als erste Maßnahme blockierte ich den Zugriff auf den Webserver für alle IP-Adressen außer meiner eigenen. Ziel war es den Fehler möglichst eng einzukreisen anstatt die Nadel im Heuhaufen zu suchen. Was mir dann sofort auffiel: Alle Seiten des Bikemarkts funktionierten nun wie sie sollten, mit Ausnahme der Startseite. Gut. Auf der Startseite passiert nicht viel, es sollte also leicht sein, den Fehler zu finden.

Mir fiel beim erneuten Durchsehen der MySQL-Abfragen auf, dass sich immer wieder ein Muster wiederholte:

SELECTFROMWHERE … ORDER BYLIMIT 16,7216

Der letzte Teil mit dem LIMIT verstörte mich arg, denn er sollte hier nicht auftauchen. Mir schwante Schlimmes…

Und tatsächlich: Ich hatte die Mutter aller Anfängerfehler gemacht. 2012. Nach über 20 Jahren Erfahrung in der Softwareentwicklung.

Gegeben sei ein Schleifenkonstrukt, welches solange durchlaufen, bis eine der angegebenen Abbruchbedingungen erfüllt ist:

<?php
do {
 
    $iterations ++;
 
    // hier geschehen diverse Arbeitsschritte
 
} while ($countCurrent < $countDesired 
    || $iterations >= 5);

Bei jedem Durchlauf der Schleife wird zum Einen der Iterationszähler um eins erhöht und zum anderen ein paar Daten gesammelt. Zum Schluss wird geprüft, ob genügend Daten zusammengekommen sind. Um sicherzustellen, dass diese Schleife nicht zu oft durchlaufen wird (was ja Rechenzeit kostet), dient der Iterationszähler. Sobald er einen bestimmten Wert erreicht hat, wird die Schleife abgebrochen.

Wann beendet sich also diese Schleife?

Wenn genügend Daten zusammengekommen sind oder wenn der Iterationszähler größer oder gleich fünf ist? Nein.

In diesem Fall beendet sich die Schleife, wenn genügend Daten zusammengekommen sind oder Iterationszähler kleiner fünf ist!

Das funktioniert gut, wenn in den ersten vier Durchläufen genügend Daten zusammenkommen. Sobald aber fünf oder mehr Durchläufe benötigt werden, wird diese Schleife niemals beendet. Und genau das ist hier passiert.

Was lernen wir daraus?

  • Ein falscher Vergleichsoperator kann fatale Folgen haben.
  • Die fatalen Folgen treten unter Umständen erst viel später auf (die entsprechende Stelle funktionierte jetzt mehrere Tage problemlos).
  • Die Abbruchbedingungen von „gefährlichen“ Schleifen müssen klarer formuliert werden.

Der schnellste Bugfix wäre in diesem Fall:

<?php
do {
 
    $iterations ++;
 
    // hier geschehen diverse Arbeitsschritte
 
} while ($countCurrent < $countDesired 
    || $iterations < 5);

Verständlicher ist es aber so:

<?php
do {
 
    $iterations ++;
 
    if ($iterations >= 5) {
        break;
    }
 
    // hier geschehen diverse Arbeitsschritte
 
} while ($countCurrent < $countDesired);

2 Gedanken zu „Post Mortem: Ausfall des Bikemarkts am Morgen des 5. November 2012

  1. Wir nutzen ganz klassisch Nagios für die Server- und Diensteüberwachung. Warnungen kommen per Mail.

    Für längerfristige Auswertungen setzen wir auch Munin ein, dafür haben wir uns eine Menge Plugins passend für unsere Applikationen selbst geschrieben.

Schreibe einen Kommentar

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