Plattform statt Skripte: Datenmigration ohne Python-First-Mindset

Datentransformationsprojekte gehören zu den komplexesten IT-Projekten – speziell dann, wenn die Quellsysteme in eine Systemlandschaft eingebettet sind, die bereits seit 30 Jahren im produktiven Einsatz ist. Oft wird dabei der Begriff „historisch gewachsen“ verwendet und mit einer negativen Konnotation belegt.
Tatsächlich bedeutet „historisch gewachsen“ meistens, dass IT-Systeme über eine längere Zeit zunehmend an die Geschäftsprozesse mit allen Sonderfällen und Ausnahmen angepasst wurden und sich die Komplexität der technischen Umsetzung an die oft hohe Komplexität der Operationen eines Betriebs annähert.
Folglich sind IT-Transformationsprojekte eine willkommene Gelegenheit, Altlasten loszuwerden und Prozesse zu verschlanken. Damit gehen jedoch oft lange Verhandlungen mit diversen Stakeholdern einher, die unterschiedliche Interessen verfolgen. Das erfordert ein besonders ausgeprägtes Maß an Agilität, da der Scope besonders volatil ist.
Loslassen ist schwer
Ein Legacy-System abzulösen ist auf mehreren Ebenen eine große Herausforderung. Ein derartiges System abzuschalten und auf den Zugriff darauf für immer zu verzichten ist eine sehr folgenschwere Entscheidung, die mit einem immensen Risiko einhergeht, speziell, wenn Finanz- oder andere Geschäftstransaktionen damit abgewickelt werden. Daher ist es naheliegend, dieses System laufen zu lassen (zumindest in einer Art Schattenmodus) und nur schrittweise abzulösen.
Für den Teilbereich der Datenmigration stellt das aber eine extreme Herausforderung dar, da plötzlich eine bidirektionale Synchronisation benötigt wird. Um das ressourcenschonend zu bewerkstelligen, wird zusätzlich eine Strategie zur Deltaerkennung (Stichwort „Change Data Capture“) benötigt. Out-of-the-Box-Lösungen wie Oracle GoldenGate oder Fivetran funktionieren zuverlässig, können aber extrem kostspielig werden, wenn eine große Menge an Daten auf Änderungen geprüft werden muss. Daher wird oft empfohlen, diese Technologien nur punktuell einzusetzen, nicht aber, um ein gesamtes Ökosystem bidirektional synchron zu halten.
Für unseren Anwendungsfall musste also eine Lösung her, die ähnlich flexibel, aber deutlich kostengünstiger umzusetzen war.
Delta statt Vollabgleich
Eine der wichtigsten nicht-funktionalen Anforderungen im Projekt war, dass Datenabgleiche vor den bestehenden Datenverarbeitungen (Berechnen von Aggregaten, Verteilen von Daten an Downstream-Systeme etc.) erfolgen und unsere ETL-Prozesse Teil dieser Sequenz werden.
Daher war klar, dass es eine Deltaerkennung brauchte, um die tägliche Verarbeitung innerhalb der knappen Zeitslots, die uns zur Verfügung gestellt wurden, zu bewerkstelligen. In einer Discovery-Phase betrachteten wir mehrere mögliche Lösungen. Wie so oft war die einfachste die beste: Wir verwendeten eine Kombination aus verschiedenen internen Timestamps, um die zu übertragenden Daten zu ermitteln.
Diese Information war aber nur in einem System durchgängig vorhanden, sodass wir für die andere Richtung auf andere Berechnungsmethoden zurückgreifen mussten. Apache Spark (in unserem Fall mit Java) bietet für die Deltaerkennung zahlreiche Performanceoptimierungen an, auf die man als Entwickler:in zurückgreifen kann. Da nur tatsächlich geänderte Daten verarbeitet werden, blieben Laufzeit und Kosten auch bei größeren Datenmengen überschaubar.
Das Fundament und die Plattform
Die vielen beweglichen Teile in Datenmigrationsprojekten laden zum Aufbau einer Self-Service-Plattform ein. Dabei konnten wir auf unsere Erfahrung beim Bau von [Developer-Plattformen](https://posedio.com/platform-engineering) zurückgreifen, denen teilweise andere Technologien, aber die gleichen Paradigmen zugrunde liegen. Domänenexpert:innen sollten Migrationsregeln möglichst autonom übermitteln und in Workflows integrieren können. Gleichzeitig ist es bei generischen Lösungen besonders wichtig, eine breite Kompatibilität zu verschiedenen Datenbanksystemen sowie ein erprobtes Testframework zu haben, um flexibel und robust zu sein.
Aufgrund dieser Anforderungen an Abstraktion, Kompatibilität und Testbarkeit haben wir uns für einen modernen, Java-basierten Stack entschieden. Während Python weiterhin die Programmiersprache der Data Science ist (und es noch länger bleiben wird), ist Java die Programmiersprache der Enterprises, Abstraktionen und Plattformen. Knapp 400 ETL-Pipelines wurden so umgesetzt und via Apache Airflow in einer serverless Dataproc-Umgebung orchestriert.
Besonders viel Bedeutung haben wir dabei unserer Softwarearchitektur beigemessen, um mit Best Practices, wie beispielsweise Composition over Inheritance und Domain-driven Design der Module, die Erstellung neuer Pipelines so einfach wie möglich zu gestalten und so einem volatilen Projektscope entgegenzuwirken.
In Zeiten KI-gestützter Entwicklung bekommt die Ökonomie von sauberen Schnittstellen und Abstraktionen oft einen geringeren Stellenwert. In diesem Projekt haben wir uns trotzdem bewusst dazu entschieden, genügend Zeit darauf zu verwenden, die Komplexität aller sich wiederholenden Teile unserer ETL-Pipelines intelligent zu kapseln. So konnten wir möglichst viel Zeit darauf verwenden, die Domänen der Datenmigration zu beleuchten, die Unterschiede in der Funktionsweise beider Systeme gemeinsam mit dem Kunden aufzuzeigen und domänenspezifische Lösungen dafür zu entwickeln.
Das Ergebnis waren Pipeline-Konfigurationen, die ausschließlich domänenspezifische Validierung und Transformationslogik enthielten. DB-Connection-Optimierung, Caching, AuthZ/AuthN, das Mounten von Cloudspeicher als Hadoop-Filesystem, Batching und viele andere Funktionen waren als Teil unserer Plattform für alle Entwickler:innen automatisch inklusive.
Komplexe Teilbereiche unserer Lösung, wie das Synchronhalten von dynamisch allozierten Sequence-Values über Systeme hinweg und die Deltaerkennung, konnten wir mit bewährten Tools wie JUnit und Testcontainers in isolierten Bereichen testen und Migrationsszenarien vor der tatsächlichen Durchführung praxisnah simulieren.
Der Betrieb
Da die beiden beteiligten Systeme Prozesse verschieden abbildeten und unterschiedliche Validierungsregeln hatten, konnten Fehler nicht vollständig ausgeschlossen werden. Daher war es umso wichtiger, dass Anomalien schnell erkannt und gemeldet wurden, sodass Probleme behoben werden konnten, bevor der Betrieb beeinträchtigt wurde.
Neben einem ausgeklügelten CI/CD-Konzept, das schnelles Deployen von Hotfixes erlaubte, waren Notifizierungen über Microsoft Teams und andere Kanäle innerhalb weniger Stunden vom Requirement bis zur Integration umgesetzt. Obwohl die implementierte Lösung auf modernen Cloudtechnologien basierte, war eine Einbettung der ETL-Pipelines und ihrer Artefakte in eine Legacy-Enterprise-Umgebung gut möglich. Am Ende mussten verschiedenste Protokolle unterstützt werden: Pipelinetrigger via E-Mail, FTP(S)-Integrationen, Operationen mit Cloud-Storage-Output und Trigger via Datenbanktabellen, die von den Legacy-Systemen verlangt wurden.
Für die Orchestrierung wurde übrigens Python verwendet, da Airflow Python-DAGs verlangt. Dafür wären komplexe objektorientierte Strukturen ohnehin hinderlich gewesen, weshalb sich Python hier sehr gut eignete.
War es das wert?
Auf jeden Fall. Für dieses Projekt war die Verwendung von Java als Programmiersprache essenziell, um durch einen hohen Abstraktionsgrad die Handhabung einer großen Zahl von Pipelines mit geteilter Funktionalität (als Middlewares implementiert) zu ermöglichen. ⟨Am Ende ließ sich eine simple Pipeline in ca. 5 Minuten vom Requirement bis zum produktiven Einsatz umsetzen und für komplexere Verarbeitung leicht erweitern.⟩ Das Paradigma, das der Entwicklung der Plattform zugrunde lag, war immer sehr stark an klassische objektorientierte Entwicklung angelehnt und war für den Einsatz von Java prädestiniert.
Für interaktivere Workflows, speziell wenn explorative Datenanalysen mit Data Insights, wie hier beschrieben, im Vordergrund stehen - etwa in Verbindung mit Jupyter oder Jupyter-ähnlichen Notebooks - bleibt Python aber weiterhin die erste Wahl.



