Das Problem mit der Datenbank“¦
Für Anwendungscode ist es inzwischen die normale Vorgehensweise: Codeänderungen werden regelmäßig in einem Nightly Build gebaut, getestet und häufig auch direkt deployt. Das nennt man Continuous Integration (CI). Ziel ist, dass die neue Version der Anwendung in jeder beliebigen Umgebung lauffähig ist. Das ist sie aber nur dann, wenn der Zustand der Datenbank zur Anwendungsversion passt. Wie geht man aber mit Datenbankänderungen um, ohne den Grundgedanken von CI zu verletzen? Und was ist an der Datenbank eigentlich anders als an normalem Programmcode?
Das besondere an Datenbanktabellen ist, dass die in ihnen gespeicherten Daten beim Deployment einer neuen Version erhalten bleiben müssen. Während normaler Programmcode einfach vollständig neu deployt wird, verlangt die Datenbank eine Behandlung, die den Ist-Zustand des Zielsystems berücksichtigt und unter Erhalt der bestehenden Daten nur die notwendigen Ü„nderungen zum Erreichen des Soll-Zustands durchführt. Mit anderen Worten: die Skripte, die ausgeführt werden müssen, hängen vom Zielsystem ab. Mit der Definition des Sollzustands ist es nicht getan, um ans Ziel zu kommen, brauchen wir auch das Delta zwischen Ist und Soll. Und das kann für jedes Zielsystem ein anderes sein.
Die herkömmliche Vorgehensweise
Normalerweise begegnet man dem Problem einfach damit, dass die Migrationsskripte für jedes konkrete Deployment per Hand geschrieben und vor dem Deployment ausgeführt werden. Häufig greift man dabei auf Toolunterstützung, z. B. durch die bekannten Produkte Liquibase oder Flyway zurück. Damit ist es dann möglich, Skripte für verschiedene Zielumgebungen zu verwalten, und nachzuhalten, welche Skripte auf welchen Umgebungen schon gelaufen sind. Abgesehen davon, dass man die Skripte für jede Ü„nderung manuell erstellen muss, hat das Vorgehen noch einen wichtigen Nachteil: der Zustand der Datenbank ergibt sich nur als Summe aller Migrationsskripte. Es gibt keine Dateien in unserem Sourcecode-Repository, die den Zustand der Datenbanktabellen zu einer beliebigen Softwareversion darstellen würden.
Unser Lösungsansatz: CI für die Datenbank
An dieser Stelle tritt unser Framework Orcas auf den Plan. Zunächst ermöglicht es uns, den Zustand von Tabellen in einer Oracle-Datenbank mit einer SQL-ähnlichen Syntax zu beschreiben, wie in folgendem Beispiel.
create table orders
(
ordr_id number(15) not null,
version number(15) default „0“ not null,
bpar_id number(15) not null,
orderdate date not null,
tracking_number varchar2(20) not null,
constraint ordr_pk primary key (ordr_id),
constraint ordr_uc unique (tracking_number),
constraint ordr_bpar_fk foreign key (bpar_id) references business_partners (bpar_id)
);
Diese Syntax beschreibt einen Zustand, nämlich den gewünschten Sollzustand der Tabelle. Den Rest erledigt nun Orcas für uns indem es das Delta des Ist-Zustands in der Datenbank zu diesem Soll-Zustand ermittelt und dafür ein Migrationsskript generiert. Dieses Migrationsskript überführt die Datenbank unter Erhaltung aller Daten vom Ist-Zustand in den Soll-Zustand. Orcas kann die Skripte direkt ausführen oder auch für ein späteres Deployment im Dateisystem bereitstellen. Damit schließt es die Lücke, die uns noch von CI für die Datenbank trennte. Unser Sourcecode-Repository enthält nun unsere Orcas-Skripte, die wir genau wie normalen Programmcode einer Versionsverwaltung unterziehen können. Um auf einem Zielsystem eine bestimmte Version unserer Software zu installieren, muss nur ein Aufruf von Orcas mit der entsprechenden Version der Tabellenskripte in den Deployment-Prozess integriert werden. Einen Preis müssen wir für diese Vorteile zahlen. Da Orcas auf das Data Dictionary der Oracle Datenbank zugreift um den Ist-Zustand zu ermitteln, sind wir auf die Benutzung von Oracle RDMS eingeschränkt.
Orcas ist bei Github als Open Source veröffentlicht. Jeder ist eingeladen, es zu verwenden und sich an seiner Weiterentwicklung zu beteiligen.