In meinem letzten Artikel sprach ich davon, Fehler in Softwaresystemen zu akzeptieren. Dieser Meinung ist auch Michael T. Nygard, wenn er es mit dem folgenden Satz auf die Spitze treibt:

Normal mode of operation is partial failure.

Also verfolgen wir doch bei der Erstellung von Software doch diesen Ansatz und gehen davon aus, dass der normale Betriebsmodus ein teilweiser Ausfall des Gesamtsystems bedeutet. Doch wie kommen wir zu dieser Annahme?

Verfügbarkeit

Bei dem Betrieb von Softwaresystemen dreht sich doch alles um die Verfügbarkeit. Man spricht von Availability bzw. oft sogar von High Availability wenn Anforderungen an Softwaresysteme gestellt werden und bemisst sie in einer prozentualen Wertangabe. Gefordert werden dann oft Werte von 99% oder sogar mehr, denn schon eine Nicht-Verfügbarkeit von einem Prozent bedeutet bei Betrachtung eines Gesamtsystems einen totalen Ausfall von 3,65 Tagen. Und wie du dir sicher denken kannst, bedeutet jeder Ausfall letztendlich Kosten; für die einen mehr, für die anderen weniger.

Wie sieht es nun aber aus, wenn mehrere Einzelsysteme am Gesamtsystem beteiligt sind? Angenommen wir haben ein Gesamtsystem, dass sich aus 5 einzelnen Sub-Systemen zusammensetzt. Jedes der Sub-Systeme garantiert eine Verfügbarkeit von 99,9%. Das Gesamtsystem letztendlich kann diese Verfügbarkeit nicht mehr sicherstellen, sondern erreicht nur noch einen Wert von 99,9%^5 = 99,5%. Damit ergibt sich eine voraussichtliche Ausfallzeit von 1 Tag 19 Stunden und 48 Minuten für das Gesamtsystem.

Wie berechnet sich Verfügbarkeit? Hierzu betrachten wir zunächst die Gesamtzeit, die sich einerseits zusammensetzt aus der mittleren Zeit die vergeht, bis ein Fehler auftritt; man spricht dann von Mean-Time-To-Failure (MTTF) oder manchmal auch von Mean-Time-Between-Failures (MTBF). Andererseits ergibt sich die Gesamtzeit aus dem Komplementär von MTTF, nämlich aus der Zeit die man benötigt, um den ordentlichen Betriebszustand wiederherzustellen. Diese Größe nennt sich dann Mean-Time-To-Recovery (MTTR) oder auch Mean-Time-To-Repair. Die Verfügbarkeit ist letztendlich der Quotient aus der Mean-Time-To-Failure und der Gesamtzeit:

Availability := MTTF / (MTTF + MTTR)

Der klassische Ansatz

Angenommen ein System fällt einmal im Monat aus (MTTF ~26,42h) und man benötigt vom Fehlereintritt bis zur Wiederhestellung des Betriebszustands im Schnitt 4 Stunden, dann hätte das System eine Verfügbarkeit von knapp 87%. Um eine besonders hohe Verfügbarkeit zu erreichen hat man in der Vergangenheit stets versucht, die Zeit bis zum Fehlereintritt (MTTF) zu maximieren. Folglich hätte eine verbesserte Variante des Systems nur noch mit einem Ausfall in 3 Monaten zu kämpfen (MTTF=87,25h, MTTR=4h) und die Verfügbarkeit läge dann bereits bei knapp 96%. Um eine Verfügbarkeit von 99% zu erreichen, wäre bei diesem System nicht einmal ein Ausfall pro Jahr vertretbar. Über Redundanz und andere Ansätze lässt sich die MTTF für das Gesamtsystem und damit die Verfügbarkeit noch weiter erhöhen.

Weiter oben bereits erwähnt habe ich jedoch die Problematik, die entsteht, wenn mehr als nur eine Systemkomponente existiert, also wenn sich das Gesamtsystem zusammensetzt aus mehreren Einzelsystemen, bei denen jedes eine andere Teilaufgabe erledigt. Dies Komposition eines Gesamtsystems aus einzelnen, verteilten Sub-Systemen und die Abhängigkeiten der Systeme untereinander ist jedoch genau das, was uns heute immer mehr begegnet. Gerade bei einer Ausrichtung hin zu Microservice Architekturen wird man bezüglich der Verfügbarkeit mit klassischen Ansätzen schnell an seine Grenzen stoßen.

Natürlich erhöht sich in der Softwareentwicklung die MTTF in der Regel mit jedem Bugfix, jedoch erhöht sich im Gegenzug auch immer wieder die Wahrscheinlichkeit für einen Fehler mit jedem zusätzlichen Feature. Es ist auch (nahezu?) unmöglich bzw. extrem kostspielig geworden in den heutigen komplexen Systemen alle möglichen Fehlerquellen zu identifizieren und sämtliche Fehler zu vermeiden. Hinzu kommt, dass während heutige Systemlandschaften immer größer werden, immer weiter verteilt sind und immer komplexere Abhängigkeitsstrukturen haben, auch die Verfügbarkeitsanforderungen immer weiter steigen.

Der Resilience-Ansatz

Erinnern wir uns an meinen letzten Artikel “Shit happens …” und dem damit verbundenen Aufruf umzudenken und Fehler in Systemen zu akzeptieren. Wenn wir also davon ausgehen, dass es immer irgendwo einen Fehler geben wird und wir den Eintritt auch nicht verhinden können, dann sind wir auch nicht in der Lage, die Verfügbarkeit zu erhöhen indem wir die MTTF maximieren. Im Gegenzug müssen wir die MTTF als gegeben hinnehmen und demnach die MTTR minimieren, um eine hohe Verfügbarkeit zu erreichen. Beim Resilient Software Design geht es also darum, ein System zu erschaffen, das mit allen möglichen erwarteten und vor allem auch unerwarteten Fehlern umgehen kann. Im besten Fall gelingt dies so, dass der Endanwender letztendlich nichts von den Fehlern mitbekommt.

Ausblick: In den kommenden Artikeln möchte ich mich mit den Grundprinzipien von Resilient Software Design beschäftigen, Anti-Patterns beleuchten und einen Überblick über Stability-Patterns geben.


 

Literatur:

[1] U. Friedrichsen, ‘Damit es auch morgen noch Läuft – Eine kurze Einführung in Resilient Software Design’, Javamagazin, no. 5.2015, pp. 33-39, 2015.

[2] M. Nygard, Release it!. Raleigh, N.C.: Pragmatic Bookshelf, 2007.

[3] Z. Taylor and S. Ranganathan, Designing high availability systems. Hoboken, New Jersey: John Wiley & Sons Inc., 2014.

Grafik: