Automatische Datenmigration
TopLogic bietet die Möglichkeit beim Wechsel von Frameworkverionen und beim Einspielen von neuen Anwendungsversionen eine automatische Datenmigration durchzuführen. Beim Wechsel einer Frameworkversion liefert die neue Frameworkversion entsprechende Migrationsbeschreibungen mit. Dieselbe Mechanik kann aber auch bei der Anwendungsentwicklung verwendet werden, um eine Migration von Bestandsdaten beim Einspielen einer neuen Anwendungsversion durchzuführen.
Eine solche automatische Datenmigration kann aus mehreren aufeinanderfolgenden Schritten bestehen, die auf den verschiedenen Ebenen der Persistenzschicht operieren:
- SQL direkt auf der Datenbank
- Replay der Historie
- versionierte Änderungen auf
KnowledgeBase
-Ebene - versionierte Modell-Updates
Schema-Version
Eine Migration wird beim Anwendungsstart automatisch angestoßen, wenn die Schema-Version der deployten Anwendung größer ist als die Schema-Version, die in der Datenbank vorgefunden wird. Die Schema-Version besteht dabei nicht aus einer einfachen Versions-Nummer, sondern aus der Menge von Migrationsbeschreibungen, die bereits in in den Datenbestand der Anwendung eingespielt sind. Über dieses System lässt sich die Schema-Version modular aus einer Framework-Version und einer oder mehreren Applikations-Schema-Versionen zusammensetzen.
Technisch ist die Schema-Version in der Tabelle TL_PROPERTIES in Schlüssel gespeichert, die mit dem Präfix databaseVersion.
beginnen. Für jedes Framework- bzw. Applikationsmodul ist hier gespeichert, welche Migrationsbeschreibung zuletzt in den Datenstand der Anwendung eingespielt wurde. In einer Anwendung, welche nur aus einem Modul my-app
besteht, für das als letztes eine Migration my-migration
durchgeführt wurde, könnte sich die Schema-Version wie folgt zusammensetzen:
SELECT * FROM TL_PROPERTIES
node |
propKey |
propValue |
---|---|---|
__global__ |
databaseVersion.tl-model-search |
Upgrade_layout |
__global__ |
databaseVersion.tl |
Multiple_forms_inapp |
__global__ |
databaseVersion.tl-element |
Move_Roles_from_Singletons_to_Modules |
__global__ |
databaseVersion.my-app |
My_migration |
Jedes Modul einer Anwendung kann seinen Beitrag zur Schema-Version der Anwendung liefert. Die Schema-Version eines Moduls ist dabei gleichbedeutend mit der letzten Migrationsbeschreibung, die für dieses Modul in die Datenbank eingespielt wurde.
Im obigen Beispiel liegen für die Framework-Module tl-model-search
, tl
, und tl-element
entsprechende Versionen vor, welche mit der Framework-Version mitgeliefert wurden, auf der die Anwendung basiert. Die Anwendung selbst besteht nur aus einem Modul my-app
und hat die Schema-Version my-migration
. In einer initial aufgesetzten Anwendung, für die noch keine Migration durchgeführt wurde, ist kein entsprechender Eintrag in der Schema-Version vorhanden.
Migrationsbeschreibung
Migrationen werden in XML-Dateien beschrieben. Diese enden mit .migration.xml
. Um ein Migrations-Datei zu erstellen, muss das Ant-Target create_migration_script_template
aus der build.xml
des jeweiligen Moduls für das die Migration ausgeführt werden soll, aufgerufen werden. Das Target verlangt die Eingabe des Names der Migrationsbeschreibung. Das Skript berechnet automatisch die Abhängigkeiten und legt die neue Migrations-Datei im Ordner WEB-INF/kbase/migration/[module-name]/
an. Migrationen können zwar modular definiert werden, ihre Reihenfolge muss aber global definiert sein. Dafür muss jede Migrationsbeschreibung alle ihre Basisversionen enthalten, auf der sie beruht. Bei der Erstellung des Migrationstemplates wird diese Basisversion in die Abhängigkeiten der neuen Migrationsbeschreibung eingetragen.
Damit ein Anwendungsmodul überhaupt einen Beitrag zur Schema-Version liefern kann, muss dies einmalig in der Anwendungs-Konfiguration bekannt gemacht werden:
<config service-class="com.top_logic.knowledge.service.migration.MigrationService">
<instance>
<modules>
<module name="[module-name]>" />
</modules>
</instance>
</config>
Ein entsprechender Eintrag wird beim Aufsetzen einer neuen Anwendung automatisch durch das Anwendungstemplate erzeugt. Mit dieser Einstellung werden beim Anwendungsstart dann Migrationsbeschreibungen im Ordner WEB-INF/kbase/migration/[module-name]/
gesucht und für dieses Modul eingespielt.
Eine Migrationdatei hat folgende Struktur:
<migration>
<version name="[migration-name]"
module="[module-name]"
/>
<dependencies>
<dependency name="[migration-name-of-dependency]"
module="[module-name-of-dependency]"
/>
...
</dependencies>
<processors>
...
</processors>
<migration>
...
</migration>
<post-processors>
...
</post-processor>
</migration>
Die Abschnitte version
und dependencies
werden automatisch bei der Erstellung befüllt und dürfen nicht angepasst werden. Unter dependencies
sind die letzten Versionen der abhängigen Module aufgeführt. Bevor diese Migration ausgeführt werden kann, müssen alle Migrationen der abhängigen Module bis zu der aufgeführten Version durchgeführt worden sein. Die weiteren Abschnitte werden im folgenden erklärt.
SQL-Migration
Die Prozessoren im Abschnitt processors
werden zu Begin einer Migration ausgeführt.
<processors>
<processor class="[migration-processor-class]"/>
...
</processors>
Die migration-processor-class
muss eine Implementierung von com.top_logic.knowledge.service.migration.MigrationProcessor
sein. Der Processor arbeitet in seiner Schnittstelle direkt auf einer Verbindung zur Datenbank und kann darüber beliebige SQL-Befehle ausführen. Damit ist es möglich, unversionierte Änderungen an den Anwendungsdaten vorzunehmen. Werden mehrere Migrationen auf einmal ausgeführt, so werden erst alle Prozessoren ausgeführt, bevor die Replay-Migration beginnt.
Es gibt eine Reihe von vordefinierten Prozessoren, die über eigene Tag-Namen definiert werden. Beispiele hierfür sind:
- Aktualisieren der Tabellen-Definition. Wird eine Datenbank-Tabelle manuell angelegt oder gelöscht, so kann mit
diese neue Konfiguration der Anwendung bekannt gemacht werden.<store-type-configuration />
- Soll nur ein SQL-Skript ausgeführt werden, so kann dies mit dem SQL-Prozessor geschehen. Hierfür wird ein Skript im Dialekt der Datenbank erwartet. Mit dem Prozessor
wird z.B. im Ordner "WEB-INF/kbase/migration/scripts" das auszuführende SQL-Skript erwartet. Ist die Ziel-Datenbank z.B. "Oracle" so wird die Datei "migration4711.oracle.sql" ausgeführt, auf einer "MySQL" die Datei "migration4711.mysql.sql" und auf einer "MSSQL"-Datenbank "migration4711.mssql.sql".<sql file-name-prefix="webinf://kbase/migration/scripts/migration4711" />
-
Für unversionierte Modelländerungen (z.B. Anlegen eines Fachobjektes oder einer Eigenschaft eines Fachobjektes, so als ob sie schon immer existieren würde), gibt es eine Menge vorgefertigter Prozessoren die unter Unversionierte Modellanpassung beschrieben sind. Der Vorteil an diesen Prozessoren ist, dass der Entwickler sie Datenbank-unabhängig einsetzen kann.
Replay
Ist der Unter-Abschnitt migration
gefüllt, wird ein Replay der Anwendungshistorie durchgeführt. Hierbei werden aus der Persistenz-Schicht die Änderungen in Change-Set's extrahiert, umgeschrieben und wieder eingespielt.
<migration>
<steps>
<step name="[name-of-migration-step]">
<rewriter class="[rewriter-class]"/>
...
</step>
</steps>
</migration>
Die Replay-Migration besteht aus potentiell mehreren Gruppen von benannten Migrationsschritten (steps
). Ein Migrationsschritt besteht aus der Ausführung einer Reihe von Change-Set-Umschreibungen (rewriter
). Die rewriter-class muss die Schnittstelle com.top_logic.knowledge.event.convert.EventRewriter
implementieren. Ein Rewriter bekommen ein ChangeSet
, das aus der Persistenz-Schicht extrahiert wurde, übergeben. Dieses kann modifiziert oder entfernt werden. Weiterhin können auch neue ChangeSet
s erstellt werden. Diese modifizierten oder neu erstellten ChangeSet
s werden dann in die Ziel-Datenbank eingespielt.
Es existieren eine Reihe von vordefinierten Rewritern, die über eigene Tag-Namen definiert werden können. Beispiele hierfür sind:
- Ändern der Annotationen eines
TLModelPart
: Hierbei werden die Annotationen einesTLModelPart
in der Datenbank durch den konfigurierten Wert ersetzt. Z.B im ModuleDemoTypes
im TypDemoTypes.A
das Attributedate2
:
Man beachte hierbei das zusätzliche<set-part-annotation module="DemoTypes" type="DemoTypes.A" attribute="date2"> <annotations> <!-- Annotation des Attributes wie es z.B. in der Datenbank oder einer `*.model.xml` steht. --> <annotations> <format format="d. MMMMM y"/> </annotations> </annotations> </set-part-annotation>
annotations
-Tag
- Ändern von Teilen der Konfiguration eines
TLModelPart
: Hierbei wird nicht die gesamte Annotation ersetzt sondern nur spezielle Teile. Z.B. in allen Konfigurationen<table name="OldTable"/>
durch<table name="NewTable"/>
ersetzen:<attribute-value-rewrite types="MetaElement" source-attribute="annotations" target-attribute="annotations" > <config-as-string> <regex-replace> <pattern><![CDATA[<table name="OldTable"/>]]></pattern> <replacement><![CDATA[<table name="NewTable"/>]]></replacement> </regex-replace> </config-as-string> </attribute-value-rewrite>
- Transformation eines
String
- oderConfigurationItem
-wertigen Attributes: Ist der Wert eines Datenbank-Attributes XML-wertig, so kann dieser mithilfe eines XSL Transformation geändert werden. Z.B. im Datenbank-Type "A" das Attribut "attr" Mithilfe des XSLT-Script "xslt-script.xls":<rewriter class="com.top_logic.knowledge.service.db2.migration.rewriters.AttributeRewriter" types="A" > <algorithm class="com.top_logic.knowledge.service.db2.migration.rewriters.XsltValueTransform" attribute="attr" transform="WEB-INF/.../xslt-script.xls" /> </rewriter>
- Entfernen der Werte der Attribute: Hier können Datenbank-Attribute, deren Namen einem gegebenen Regulären Ausdruck genügen, entfernt werden. Dies ist z.B. hilfreich um die Daten zu entfernten Attributen ebenfalls zu entfernen. Z.B. im Datenbank Typ "A" die Werte der Attribute deren Namen mit "Legacy" enden:
<rewriter class="com.top_logic.knowledge.service.db2.migration.rewriters.AttributeRewriter" types="A" > <algorithm class="com.top_logic.knowledge.service.db2.migration.rewriters.AttributeNameFilter" exclude-pattern=".*Legacy" /> </rewriter>
- Umbenennen von Datenbank-Typen. Z.B. den Datenbank Typ "A" in "B" umbenennen:
<type-renaming types="A" target-type="B"/>
Versionierte Änderungen
Nach einer potentiellen Replay-Migration können versionierte Änderungen auf dem Datenstand direkt auf Ebene der Persistenz-Schicht durchgeführt werden. Solche Änderungen passieren noch bevor die Anwendung und damit das dynamische Typsystem startet. Daher stehen für diesen Teil einer Migration keine Anwendungsfunktionalitäten außer dem Zugriff auf die versionierende Persistenzschicht (Knowledge-Base) zur Verfügung.
Entsprechende Prozessoren werden in der Sektion post-processors
konfiguriert. Eine post-processor-class
muss die Schnittstelle com.top_logic.knowledge.service.migration.MigrationPostProcessor
implementieren.
<post-processors>
<post-processor class="[post-procesor-class]"/>
</post-processors>
Ein solcher Post-Processor kann direkt Operationen auf der KnowledgeBase
durchführen. Dabei kann anders als bei der SQL-Migration oder dem Rewrite keine Änderung der Anwendungshistorie vorgenommen werden. Stattdessen können einmalig versionierte Änderungen am Anwendungsdatenbestand durchgeführt werden.