Xircuit Blog

Produkt-Updates, Entwickler-Logs und Neuigkeiten aus der Entwicklung von Xircuit.

Observability: Sentry in jedem der sieben Worker cover image

Observability: Sentry in jedem der sieben Worker


Observability: Sentry in jedem der sieben Worker

Ein Rechenzentrum als Sinnbild für Betrieb Foto: Rawpixel (CC0 1.0), via Openverse

Der Web-Server und die API meldeten sich schon seit Wochen, wenn etwas schieflief. Ein Sentry-Event, ein Stack-Trace, im besten Fall die Zeile, in der es knallte — bei allem, was ein Nutzer direkt anfasst, war die Sichtbarkeit gut. Was mir in dieser Woche auffiel: der Teil der Plattform, der niemanden gegenübersteht, war praktisch blind. Die Hintergrunddienste liefen, taten ihre Arbeit, und wenn sie stolperten, erfuhr ich es bestenfalls Tage später über eine Nebenwirkung — eine Abrechnung, die nicht ankam, ein Sync, der ausblieb.

Genau das ist die unangenehme Klasse von Fehlern: nicht die, die laut abstürzen, sondern die, die still passieren. Ein Worker, der eine Ausnahme schluckt und im nächsten Intervall einfach weitermacht, sieht von außen gesund aus. Diese Woche ging es deshalb nicht um ein neues Feature, sondern darum, jedem einzelnen der sieben Hintergrund-Worker eine Stimme zu geben — und zwar so, dass sie sich meldet, bevor jemand anderes das Problem bemerkt.

Warum die Worker der blinde Fleck waren

Die sieben Worker sind der Motorraum der Plattform. Sie synchronisieren Gesundheitsdaten aus den angebundenen Quellen, laufen durch Abrechnungszyklen, führen Datenschutz-Läufe aus, pflegen abgeleitete Daten nach. Jeder von ihnen ist ein eigener Prozess, oft auf einem eigenen Zeitplan, und keiner hat ein UI, in dem sich ein Fehler zeigen würde.

Bislang war meine einzige Fehlersicht bei diesen Diensten das, was die OpenTelemetry-Exporte aus Aspire hergaben — nützlich zur Laufzeit, aber kein Werkzeug, um zwei Tage später zu rekonstruieren, warum ein bestimmter Lauf um drei Uhr morgens abgebrochen ist. Ein Log, das niemand liest, ist kein Monitoring. Ich wollte den Punkt erreichen, an dem eine Ausnahme in einem Worker denselben Weg nimmt wie eine Ausnahme im Web: erfasst, zugeordnet, mit Kontext, und an einer Stelle, an der ich sie tatsächlich sehe.

Ein gemeinsamer Baustein statt sieben Copy-Paste-Blöcke

Die naheliegende Versuchung wäre gewesen, in jeden Worker den gleichen Sentry-Setup-Code zu kopieren. Sieben Mal fast dasselbe, mit dem üblichen Ergebnis: Nach drei Monaten weichen sie subtil voneinander ab, weil jemand einen Fix nur an einer Stelle nachzieht. Stattdessen liegt die gesamte Verdrahtung jetzt hinter einem einzigen Aufruf — AddXircuitWorkerSentry. Jeder Worker holt sich damit dieselbe Konfiguration: dieselben Standard-Tags, dasselbe Environment-Handling, dieselbe Art, den Kontext an ein Event zu hängen.

Der Vorteil ist nicht nur weniger Code. Er ist, dass Observability damit eine Eigenschaft der Plattform wird und keine Eigenschaft des jeweiligen Worker-Autors. Wenn nächste Woche ein achter Worker dazukommt, ist die Frage nicht mehr „habe ich das Monitoring nicht vergessen", sondern eine Zeile in der Startup-Konfiguration. Und wenn ich das Verhalten für alle ändern will — ein zusätzliches Tag, eine andere Sampling-Rate — passiert das an genau einem Ort.

Ein DSN pro Worker, nicht einer für alle

Die interessantere Entscheidung war, wie fein ich die Fehler trennen wollte. Ein einzelner Sentry-DSN für „die Worker" wäre einfacher gewesen, hätte aber die eigentliche Frage nie beantwortet: welcher Dienst hat das Problem. In einem gemeinsamen Topf verschwimmen ein Abrechnungsfehler und ein Sync-Timeout zu einer diffusen Masse aus Events, die man erst mühsam wieder auseinandersortiert.

Deshalb bekommt jeder Worker seinen eigenen DSN. Ein Fehler im Datenschutz-Lauf landet in einem anderen Projekt als ein Fehler im Abrechnungs-Worker, und beide sind auf einen Blick auseinanderzuhalten. Release-Health, Alert-Regeln und die Frage „ist dieser Dienst gerade auffällig" lassen sich damit pro Dienst beantworten statt pauschal. Die Umgebungen — Entwicklung, Test, Produktion — werden innerhalb eines Projekts über ein Environment-Tag getrennt, nicht über weitere Projekte; das hält die Zahl der DSNs bei der Zahl der Dienste und lässt die Views trotzdem sauber.

Überwachungsdiagramme auf mehreren Bildschirmen Foto: Lorenzo Cafaro · StockSnap (CC0 1.0), via Openverse

Der Trick mit dem frühen DSN

Ein Detail hat mehr Kopfzerbrechen gemacht, als es von außen aussieht: wann der DSN verfügbar ist. In Produktion leben die Geheimnisse im Key Vault und werden dort von einem Initialisierungs-Schritt hineingeschrieben. Das bedeutet aber, dass ein Worker, der seinen DSN ausschließlich aus dem Key Vault zieht, in genau dem Zeitfenster blind ist, in dem am ehesten etwas kaputtgeht — beim Hochfahren, bevor die Initialisierung durch ist.

Boot-Fehler sind aber die, die man am wenigsten verlieren will. Ein Worker, der beim Start über eine fehlende Konfiguration oder eine nicht erreichbare Abhängigkeit fällt, meldet sich sonst nie, weil er nie so weit kommt, den DSN zu laden. Die Lösung ist zweistufig: Der DSN kommt zuerst aus der Umgebung — früh genug, dass Sentry schon steht, bevor irgendetwas anderes initialisiert wird — und später aus dem Key Vault, sobald die reguläre Konfiguration greift. So ist der frühe Start abgedeckt und der laufende Betrieb sauber über die zentrale Geheimnisverwaltung versorgt. Kein Fenster, in dem ein Fehler unsichtbar wäre.

Damit kein Fehler mehr still bleibt

Das eigentliche Ziel hinter der ganzen Mechanik ist einfach zu formulieren: Kein Worker-Fehler soll mehr stumm sein. Wenn ein Sync-Job auf eine unerwartete Antwort einer externen Quelle trifft, wenn ein Abrechnungslauf über einen Randfall stolpert, wenn ein Datenschutz-Lauf einen Datensatz nicht verarbeiten kann — dann gibt es jetzt ein Event, einem konkreten Dienst zugeordnet, mit dem Kontext, um zu verstehen, was passiert ist.

Das verschiebt die Arbeitsweise spürbar. Vorher war der übliche Ablauf: Jemand bemerkt eine fehlende Nebenwirkung, ich suche rückwärts, welcher Dienst zuständig ist, und grabe mich durch Logs. Jetzt ist die Reihenfolge umgekehrt — der Dienst meldet sich selbst, und zwar in dem Moment, in dem der Fehler auftritt, nicht wenn seine Folgen sichtbar werden. Das ist der ganze Sinn von Observability an dieser Stelle: die Zeit zwischen „etwas ist schiefgelaufen" und „ich weiß davon" auf nahe null zu drücken.

Die Kehrseite: eine Stille-Welle gegen das Rauschen

Ein Monitoring, das alles meldet, ist auf seine eigene Art nutzlos. Kaum waren die sieben Worker angeschlossen, zeigte sich der erwartbare Effekt: Ein Teil der Events waren keine echten Probleme, sondern bekannte, harmlose Ausnahmen — Dinge, die im normalen Betrieb vorkommen und keine Handlung erfordern. Wenn diese den Kanal füllen, gewöhnt man sich an, Sentry-Meldungen wegzuklicken, und genau dann übersieht man das eine Event, das zählt.

Deshalb kam nachträglich eine Stille-Welle dazu: eine Filterschicht, die diese bekannten, unkritischen Ausnahmen herausnimmt, bevor sie zum Event werden. Der Anspruch dabei ist heikel und ich behandle ihn bewusst konservativ — es soll leiser werden, nicht taub. Gefiltert wird nur, was ich als harmlos verstanden und eingeordnet habe; alles andere kommt durch. Ein Signal, das im Rauschen untergeht, ist so wenig wert wie gar kein Signal, und diese letzte Schicht ist der Unterschied zwischen einem Monitoring, auf das man schaut, und einem, das man ignoriert.

Was kommt als Nächstes?

Nächste Woche kommt eine Prod-E2E-Suite, die mit Playwright echte Personas gegen die Live-Plattform durchspielt. Die Worker melden sich jetzt, wenn etwas schiefgeht — der nächste Schritt ist, systematisch nachzusehen, ob im Normalbetrieb überhaupt alles funktioniert, und zwar an der echten Produktion, nicht nur auf meinem Rechner.


Kommentare

Noch keine Kommentare. Teilen Sie als Erste(r) Ihre Gedanken!

Kommentar hinterlassen