← All posts

Unsere Service Discovery erkannte ihren eigenen Ausfall und schaltete sich selbst ab

Authagonal·July 1, 2026
authdistributed-systemsclusteringleader-electionazureaks

Wir hatten einen Cluster mit drei Replikaten, der ständig uneins mit sich selbst war. Hintergrundjobs liefen zwei- und dreimal nacheinander. Die Antwort stand nicht in den Logs, sondern in unserem eigenen Code. Die Peer-Discovery-Routine hatte einen catch-Block, und der Kommentar darin sagte sinngemäß: "multicast fehlgeschlagen, discovery deaktiviert." Unsere Service Discovery erkannte ihren eigenen Ausfall und schaltete sich stillschweigend ab, und genau das tat sie vom ersten Tag an in der Produktion.

Das ist die Geschichte, warum ein Clustering-Protokoll, das auf einem Server im Rack korrekt funktioniert, das falsche Werkzeug ist, sobald man es auf eine gemanagte Plattform bringt, und warum die Lösung darin bestand, es zu löschen statt es zu reparieren.

Wozu der Cluster eigentlich da ist

Ein Auth-Server läuft aus Verfügbarkeitsgründen in mehr als einem Replikat. Die Replikate sind weitgehend unabhängig: Jedes von ihnen kann ein Token validieren oder ein Passwort prüfen. Doch einige Jobs müssen einmal laufen, nicht einmal pro Replikat. Der Data-Retention-Durchlauf, der abgelaufene Datensätze löscht. Der Durchlauf, der Kunden-Webhooks auslöst. Alles, was nach außen greift und einen Seiteneffekt hat. Dafür braucht man zwei Dinge, auf die sich der Cluster einigen muss: wer die Mitglieder sind und welches von ihnen der Leader ist, der die Singleton-Arbeit ausführt.

Unsere ursprüngliche Antwort war die klassische: gossip. Jeder Knoten plaudert mit seinen Peers, die Mitgliedschaft ist eine emergente Eigenschaft dessen, wer erreichbar ist, und die Gruppe wählt einen Leader aus der vereinbarten Menge. Es ist ein wunderschönes Design. Es setzt allerdings voraus, dass die Knoten einander finden können, und der alte Code fand sie per multicast: in das lokale Netzwerksegment hineinrufen und sehen, wer antwortet.

Warum es stillschweigend kaputtging

Gemanagtes Kubernetes überträgt kein multicast. Genauso wenig wie fast jedes Cloud-Netzwerk, das man nicht selbst baut. Der Ruf ging hinaus und nichts kam zurück, jedes Mal. Also "scheiterte" die Discovery, der catch-Block feuerte, und jeder Knoten kam zu dem Schluss, er sei allein auf der Welt.

Bei einem Replikat ist das harmlos. Bei drei ist es Split-Brain per Konstruktion: drei Knoten, jeder überzeugt, das einzige Mitglied zu sein, jeder wählt sich selbst zum Leader, jeder führt die Singleton-Arbeit aus. Der Retention-Durchlauf lief auf allen dreien. Der Webhook-Durchlauf feuerte jeden Kunden-Webhook dreimal. Nichts davon warf einen Fehler, denn aus Sicht jedes einsamen Knotens war alles in Ordnung. Der Bug war kein Absturz; es waren drei Programme, die sich in einer Welt, die sie falsch verstanden, jeweils völlig korrekt verhielten.

Die Lösung war, gar keine Peers mehr zu suchen

Hier ist die Neubetrachtung, die das Ganze auf wenige Zeilen zusammenfallen ließ: Wir versuchten, mit einem geschwätzigen Protokoll eine Tatsache nachzubauen, die die Plattform bereits mit starker Konsistenz für uns speichert. Wir laufen auf einer Cloud, die uns einen stark konsistenten Speicher bereitstellt. Die Leader-Wahl braucht keinen Konsens unter Peers, wenn es bereits einen Ort gibt, auf den sich alle einigen können.

So wurde die Leadership zu einem blob lease. Ein Blob, ein Lease. Wer das Lease hält, ist der Leader. Das Lease hat einen Timeout, also hört ein sterbender Leader auf zu erneuern, und das Lease gibt sich für den nächsten Übernehmer frei. Es gibt keine Peer-Discovery, keine Quorum-Rechnerei, kein multicast und keinen catch-Block, der darauf wartet, es zu deaktivieren. Koordination und Events laufen über eine kleine Tabelle, die als Bus zwischen den Knoten fungiert.

Die Eigenschaft, die ich nicht erwartet hatte zu lieben: Man kann den Storage Explorer öffnen und den Verstand des Clusters sehen. Wer gerade das Lease hält. Was auf dem Bus in der Warteschlange steht. Mitgliedschaft war nicht länger etwas, das man aus dem Verhalten eines Protokolls erschließt, sondern eine Zeile, die man lesen kann. Wenn das, was entscheidet, wer eure destruktiven Jobs ausführt, ein Wert ist, den man anschauen kann, hört Debugging auf, Archäologie zu sein.

Der Teil, in dem wir absichtlich etwas Cleveres gelöscht haben

Als wir schon mal dabei waren, löschten wir einen verteilten Rate-Limiter, der auf einem conflict-free replicated data type aufbaute, einem CRDT, das die Zähler pro Knoten ohne Koordination zu einer globalen Zählung zusammenführte. Es war wirklich elegant. Es löste allerdings auch ein Problem, das wir verschoben hatten. Das globale Rate Limit gehört an den Rand, dorthin, wo die Requests ankommen, und nicht im Cluster nachgebaut mit einer Datenstruktur, die einen Absatz zur Erklärung braucht. Raus damit.

Funktionierenden Code zu löschen fühlt sich wie ein Verlust an, bis man zusammenzählt, was man nicht mehr warten muss. Wir entfernten die gossip-Schicht, die multicast-Annahme, den sich selbst deaktivierenden catch-Block und das CRDT und ersetzten alles durch ein Lease und eine Tabelle. Die Zeilenzahl sank, und die Anzahl der Zustände, in denen das System sein kann, sank mit ihr.

Die Lektion, herausgefiltert

Gossip- und multicast-Mitgliedschaft sind die richtigen Werkzeuge auf Bare Metal oder einem flachen Netzwerk, das man kontrolliert. Sie sind die falschen Werkzeuge auf einer Plattform, deren Netzwerk genau den Mechanismus nicht überträgt, von dem sie abhängen, und der Fehlermodus ist kein lautes Crashen; es ist ein leiser Fallback, der aussieht, als würde er funktionieren. Bevor man ein Muster aus verteilten Systemen auf gemanagte Infrastruktur portiert, sollte man fragen, was es über das Netzwerk annimmt, und fragen, was die Plattform bereits umsonst bereitstellt. Unsere lief bereits mit einem stark konsistenten Speicher. Ein Lease in diesem Speicher war eine einfachere, korrektere Leader-Wahl als das Protokoll, das wir mit uns herumgetragen hatten, und es ist eine, die wir beobachten können.

Wenn ihr Auth betreibt, sollten die Teile, die eure Schlüssel halten und entscheiden, wer eure destruktiven Jobs ausführt, die langweiligsten und am besten inspizierbaren Dinge sein, die ihr besitzt. Wir haben unsere langweilig gemacht. Es war ein Upgrade.