Die Entwicklung von DataBridge: 20 eigenständige 4D-Systeme mit einem zentralen MySQL-Backend synchronisieren
Manche Softwareprojekte ziehen alle Blicke auf sich. Dieses gehört nicht dazu. Es arbeitet im Hintergrund, wenn alle anderen schlafen, und fällt erst auf, wenn es ausfällt. Genau deshalb ist es so wichtig.
Wir entwickeln unsere DataBridge-Schicht derzeit weiter: eine Synchronisierungs-Engine, die rund 20 unabhängige 4D-Installationen mit einem zentralen MySQL-Backend für die Website und zugehörige Onlinedienste verbindet. Die Herausforderung besteht nicht einfach darin, Daten von A nach B zu übertragen. Entscheidend ist, dies sicher, wiederholbar und vorhersehbar im laufenden Produktionsbetrieb zu tun: während Anwender arbeiten, Datensätze sich ändern, Verbindungen abbrechen können und kein einzelner Rechner Unordnung in das Gesamtsystem bringen darf.
Im Mittelpunkt steht eine Klasse, die DBStructure erweitert. Sie koordiniert die Synchronisierung wie ein Dirigent ein Orchester. Sie weiß, ob die Synchronisierung aktiv ist, wo der entfernte Endpunkt liegt, ob Trigger eingeschaltet sind, ob bereits ein Synchronisierungszyklus läuft, welcher HTTP-Fehler zuletzt auftrat und welche Datensätze geändert wurden. Außerdem verwaltet sie eine Sammlung von Event-Listenern, damit geöffnete Fenster der 4D-Anwendung auf eingehende Änderungen reagieren können. Sie ist damit nicht nur eine Transportschicht, sondern auch die Koordinationsschicht zwischen Datenbank, Netzwerk und Benutzeroberfläche.
Die Architektur folgt einem einfachen, konsequent umgesetzten Modell. Lokale Änderungen werden zunächst durch Trigger erfasst. Wird ein Datensatz angelegt, geändert oder gelöscht, versucht der Trigger nicht sofort, ihn mit dem entfernten Server zu synchronisieren. Stattdessen legt er einen Eintrag in einer lokalen RecordSync-Warteschlange an. Diese Entscheidung entkoppelt die Datenbankbearbeitung von der Netzwerkkommunikation: Wer einen Datensatz speichert, muss weder auf einen entfernten Server noch auf einen HTTP-Aufruf oder die Behebung eines vorübergehenden Verbindungsproblems warten. Das lokale System bleibt bedienbar. Die Synchronisierung läuft asynchron im Hintergrund, statt die Arbeit im Vordergrund zu blockieren.
Wenn die Synchronisierungsschleife im Hintergrund aktiv wird, prüft sie zuerst, ob der entfernte Endpunkt erreichbar ist. Funktioniert die Verbindung, führt DataBridge zwei getrennte Phasen aus. Zunächst überträgt sie die lokal vorgemerkten Änderungen nach außen. Anschließend ruft sie Änderungen der Gegenseite ab. Diese Abläufe sind bewusst getrennt: Der eine übermittelt die auf dem lokalen 4D-Rechner beabsichtigten Änderungen, der andere übernimmt den Zustand des zentralen Backends. Diese klare Trennung macht den Code leichter verständlich und das Verhalten bei Fehlern wesentlich besser kontrollierbar.
Für die Übertragung vom lokalen zum entfernten System liest DataBridge die Einträge aus RecordSync in ihrer Sequenzreihenfolge, erstellt daraus Nutzdaten und versendet sie stapelweise. Jeder Datensatz enthält den Tabellennamen, den Änderungsstatus, Angaben zum Primärschlüssel und gegebenenfalls die serialisierten Daten der Entität selbst. Für Löschungen genügen Identität und Status. Neue und geänderte Datensätze benötigen einen vollständigen Snapshot des Objekts. Sobald ein Stapel erfolgreich bestätigt wurde, werden die zugehörigen Warteschlangeneinträge entfernt. Schlägt die Übertragung fehl, bleiben sie bewusst erhalten, damit der nächste Zyklus sie erneut senden kann. Das ist keine spektakuläre Technik, sorgt aber für verlässliche Systeme.
Die Verarbeitung eingehender Änderungen ist ebenso sorgfältig ausgelegt. DataBridge fragt den Server, wie viele Änderungen seit der zuletzt bekannten Sequenznummer vorliegen, ruft sie blockweise ab und wendet sie einzeln im lokalen 4D-Datastore an. Jede Änderung wird als Erstellung, Änderung oder Löschung eingeordnet. Neue Datensätze werden angelegt, sofern sie noch nicht existieren. Geänderte Datensätze werden in die passende Entität geladen. Gelöschte Datensätze werden entfernt, soweit dies möglich ist. Nach der Verarbeitung wird die lokal gespeicherte Sequenznummer weitergesetzt, damit der Rechner seine genaue Position im Änderungsstrom kennt.
Das klingt einfach, bis man auf den ältesten Gegner jeder Synchronisierungs-Engine trifft: Rekursion. Wird eine entfernte Änderung lokal geschrieben und löst dieser Schreibvorgang erneut eine ausgehende Änderung aus, kann eine endlose Rückkopplung entstehen. Deshalb markiert DataBridge Datensätze, die gerade aus der entfernten Synchronisierung übernommen werden, mit einem triggerKey. Springt der zugehörige lokale Trigger an, erkennt er, dass die Änderung von DataBridge selbst stammt, und ignoriert sie. Dieser eine Mechanismus verhindert, dass die Engine endlos mit sich selbst kommuniziert.
Ein weiteres unscheinbares, aber wesentliches Detail sind Datensatzsperren. In einer echten Anwendung kann ein Benutzer einen Datensatz bereits geöffnet haben, wenn eine entfernte Aktualisierung eintrifft. Statt das Speichern zu erzwingen, Daten zu überschreiben oder in einen Sperrkonflikt zu geraten, versucht DataBridge zunächst, die Entität zu sperren. Gelingt das nicht, protokolliert sie die Situation und verschiebt den Eintrag auf einen späteren Zyklus. Das ist eine bewusste Entscheidung: lieber sicher aufschieben, als Probleme durch gleichzeitige Zugriffe zu ignorieren. Verteilte Systeme verzeihen unangebrachten Optimismus nicht.
Auch die Nachvollziehbarkeit des Betriebs erhält viel Aufmerksamkeit. Die Synchronisierungsschicht zeigt den Fortschritt in einem Palettenfenster an, zählt verarbeitete Datensätze, speichert HTTP-Fehler und schreibt jedes relevante Ereignis in eine Synchronisierungsprotokolldatei. Entwickler von Hintergrunddiensten kennen das: Wenn um 03:12 Uhr etwas schiefgeht, entscheidet meist die Qualität der Protokollierung darüber, ob die Behebung fünf Minuten oder die Untersuchung vier Stunden dauert.
Über die normale inkrementelle Synchronisierung hinaus enthält DataBridge auch Werkzeuge für den Betrieb. Sie kann vollständige Tabellen an das entfernte Backend übertragen, entfernte Tabellen nach 4D zurückladen, Tabellen auf der Gegenseite anlegen und lokale mit entfernten Strukturen vergleichen. Das hilft nicht nur im täglichen Betrieb, sondern auch bei Einrichtung, Migration, Diagnose und kontrollierter Wiederherstellung. Eine Synchronisierungs-Engine ist deutlich wertvoller, wenn sie dabei hilft, ein System zu verstehen, statt lediglich Daten zu bewegen.
Für Entwickler liegt dieses Projekt an einer interessanten Schnittstelle zwischen klassischer Unternehmenssoftware und der Entwicklung verteilter Systeme. Auf der einen Seite steht 4D: ausgereift, lokal, transaktional und bewährt. Auf der anderen Seite steht eine zentrale Webplattform mit MySQL, die über viele Rechner und Benutzer hinweg konsistente Daten benötigt. DataBridge verbindet diese beiden Welten. Dabei gleicht sie nicht nur Daten ab, sondern auch Zeitabläufe, Zustände, Fehler, Wiederholungsversuche, Reihenfolgen und Verlässlichkeit.
Es bleibt noch einiges zu tun. Synchronisierungs-Engines sind nie wirklich fertig; sie werden nach und nach verfeinert, robuster gemacht und um weitere Sonderfälle ergänzt. Doch jede Verbesserung bringt das System dem näher, was Entwickler von Infrastruktur erwarten: so viel Zuverlässigkeit, dass man sich nicht mehr mit ihr beschäftigen muss. Das größte Kompliment für eine solche Komponente ist am Ende kein Applaus, sondern Ruhe.