Artikel

Warum eine Datenbank für drei Prozesse reicht

Bot, Dashboard und Verwaltung hatten am Ende drei getrennte Datenbestände — historisch gewachsen, nie beabsichtigt. Wie ich sie zusammengeführt habe und woran man merkt, dass etwas auseinanderläuft.

BlackZackBlackzack

959 Wörter · 5 Min. Lesezeit

  • datenbank
  • architektur
  • betrieb

Als ich meinen Aufbau für den Umzug vorbereitet habe, ist mir etwas aufgefallen, das ich nicht wahrhaben wollte: Meine drei Anwendungen sprachen nicht mehr mit derselben Datenbank.

Sie sollten. Der ganze Aufbau beruht darauf. Tatsächlich hatte jede eine eigene Datei, und sie waren über Monate auseinandergelaufen.

Wie so etwas passiert

Nicht durch eine Entscheidung, sondern durch eine Kette kleiner Schritte.

Der Pfad zur Datenbank stand in einer Umgebungsvariablen. Beim Anlegen einer neuen Umgebung wurde er relativ angegeben. Relativ heißt: relativ zum Arbeitsverzeichnis des jeweiligen Prozesses — und die sind verschieden.

Ergebnis: Drei Anwendungen, drei Arbeitsverzeichnisse, drei Dateien. Jede hat brav ihr Schema angelegt und darin gearbeitet.

Warum es nicht sofort auffiel: Die meisten Zugriffe sind lesend, und die Anwendungen lesen überwiegend Dinge, die sie selbst geschrieben haben. Das Dashboard zeigt Einstellungen an, die über das Dashboard gesetzt wurden. Erst wenn eine Änderung aus einer Anwendung in einer anderen wirken soll, merkt man es — und genau das passiert seltener, als man denkt.

Bei mir war der Auslöser eine Einstellung, die ich im Dashboard geändert habe und die der Bot nicht übernommen hat. Ich habe zwei Stunden im Zwischenspeicher gesucht, bevor ich auf die Idee kam, die Dateigrößen zu vergleichen.

Die Zusammenführung

Das Vorgehen war weniger dramatisch als befürchtet, weil sich die Bestände wenig überschnitten.

Erstens: feststellen, welche Datei welche Wahrheit hat. Pro Tabelle: Wo stehen die aktuellen Daten? Bei Servereinstellungen war es die Datei des Bots, bei Sitzungen die des Dashboards, bei Lizenzen die der Verwaltung.

Zweitens: eine Zieldatei festlegen — die mit dem größten und wichtigsten Bestand.

Drittens: fehlende Zeilen übertragen, Tabelle für Tabelle, mit Zählung vorher und nachher. Bei Überschneidungen entscheidet der jüngere Zeitstempel.

Viertens: alle drei Anwendungen auf einen absoluten Pfad umstellen. Das war die eigentliche Behebung. Ein relativer Pfad in einer Konfiguration ist eine Einladung für genau diesen Fehler.

Fünftens: die alten Dateien nicht löschen, sondern umbenennen und wegräumen. Falls eine Tabelle vergessen wurde, ist sie noch da.

Warnung

Der gefährlichste Moment ist Schritt drei. Wenn zwei Bestände denselben Datensatz mit verschiedenen Werten haben, muss man entscheiden — und die Entscheidung ist inhaltlich, nicht technisch. Ich habe für jede betroffene Tabelle einzeln festgelegt, welche Seite gewinnt, und das Ergebnis vorher stichprobenartig angesehen. Automatisch zusammenzuführen wäre schneller gewesen und hätte stillschweigend Daten überschrieben.

Woran man merkt, dass etwas auseinanderläuft

Nach diesem Fall habe ich mir Prüfungen gebaut, die genau das erkennen.

Beim Start meldet jede Anwendung, mit welcher Datei sie arbeitet — mit absolutem Pfad und Größe. Zwei verschiedene Pfade in den Protokollen fallen sofort auf.

Eine Kennung in der Datenbank. Eine Tabelle mit einer einzigen Zeile, die eine zufällige Kennung enthält, einmal beim Anlegen erzeugt. Jede Anwendung meldet sie beim Start. Drei verschiedene Kennungen heißen: drei Datenbanken.

CREATE TABLE datenbank_kennung (
  id      INTEGER PRIMARY KEY CHECK (id = 1),
  kennung TEXT NOT NULL,
  erzeugt TEXT NOT NULL
);

Ein Testwert. Die Verwaltung schreibt regelmäßig einen Zeitstempel, den der Bot liest und in seinem Zustand meldet. Weicht er stark ab, stimmt etwas nicht.

Die zweite Prüfung ist die wirksamste und kostet fast nichts. Ich habe sie seitdem in jedes System eingebaut, in dem mehrere Prozesse auf einen gemeinsamen Bestand zugreifen.

Der schreibende Zugriff mit einer Datei-Datenbank

Bei der Gelegenheit habe ich mir das Sperrverhalten genauer angesehen, weil beim Umzug in Container mehrere Prozesse auf dieselbe Datei zugreifen werden.

Die Einstellungen, die das entschärfen, sind dieselben wie beim Wechsel auf diese Datenbank: das Write-Ahead-Log, damit Leser Schreiber nicht blockieren, eine großzügige Wartezeit statt sofortigem Abbruch, und ein reduzierter Synchronisationsmodus.

Was zusätzlich zählt: Schreibende Transaktionen kurz halten. Eine Transaktion, die eine Sekunde offen ist, blockiert eine Sekunde lang alle anderen Schreiber. Bei mir war das an einer Stelle der Fall — ein Vorgang, der innerhalb einer Transaktion einen externen Aufruf machte. Das ist ein Fehler, der nur unter Last auffällt.

Wie ich den Zustand heute im Blick behalte

Nach diesem Fall gibt es bei mir eine Startprüfung, die drei Sachen meldet — und sie hat sich als eine der nützlichsten Kleinigkeiten überhaupt erwiesen.

[Start] Datenbank: /daten/blackzack.db (2,1 MB)
[Start] Kennung: 9f4c1e2a
[Start] Schemastand: 47 Tabellen

Der Pfad, absolut. Zwei verschiedene Pfade in den Protokollen zweier Prozesse fallen sofort auf.

Die Kennung aus der Datenbank. Der eigentliche Beweis: Gleiche Kennung heißt gleiche Datenbank, unabhängig davon, wie der Pfad aussieht.

Der Schemastand. Verrät, ob ein Prozess mit einer älteren Struktur arbeitet.

Diese drei Zeilen kosten nichts und beantworten die Frage, mit der jede Fehlersuche in einem verteilten Aufbau anfangen sollte: Reden wir eigentlich über dasselbe?

Was ich beim Zusammenführen über Datenqualität gelernt habe

Beim Vergleich der drei Bestände sind Dinge aufgefallen, die mit dem eigentlichen Problem nichts zu tun hatten.

Karteileichen. Einträge zu Servern, auf denen der Bot seit Jahren nicht mehr ist. Niemand räumt so etwas auf, wenn es keinen Anlass gibt.

Widersprüchliche Voreinstellungen. Dieselbe Einstellung hatte in zwei Beständen unterschiedliche Standardwerte, weil sie zu verschiedenen Zeitpunkten angelegt worden waren.

Zeitstempel in zwei Formaten. Ein Teil der Datensätze hatte Zeitzonenangaben, ein anderer nicht — je nachdem, welcher Prozess sie geschrieben hatte.

Der dritte Punkt ist der, der später Ärger gemacht hätte. Ein Vergleich zweier Zeitstempel in verschiedenen Formaten liefert falsche Ergebnisse, ohne dass irgendwo ein Fehler auftritt.

Seitdem gibt es bei mir eine Regel: Zeit wird an einer Stelle erzeugt, in einem Format, mit Zeitzone. Bei mir setzt die Datenbank die Zeitstempel, wo es geht — dann kann kein Prozess eine eigene Vorstellung davon haben, wie spät es ist.

Was ich mitnehme

Relative Pfade in Konfigurationen sind eine Falle. Sie funktionieren, solange alles aus demselben Verzeichnis läuft, und brechen still, sobald das nicht mehr gilt.

Stille Fehler sind die teuersten. Nichts war kaputt. Nichts hat gemeldet. Die Anwendungen liefen, jede für sich korrekt — sie waren sich nur nicht mehr einig, worüber sie reden.

Prüfungen für Selbstverständlichkeiten lohnen sich. Dass drei Anwendungen dieselbe Datenbank benutzen, war so selbstverständlich, dass ich es nie geprüft habe. Genau solche Annahmen sind die, die man einmal absichern sollte.