11 February 2018
Vor etwas mehr als einem Jahr habe in zwei Beiträgen erläutert, wie wir in Zukunft die Datenflüsse rund um unsere GDI gestalten wollen. Im vergangenen Jahr haben wir das nun mit externer Hilfe umgesetzt resp. einen ersten Wurf mit den - für uns - wichtigsten Funktionalitäten. Der Name für unser neues Lieblingsspielzeug ist GRETL: Gradle ETL.
Rekapitulation: Seit Anbeginn der (GIS-)Zeit dreht sich bei uns viel oder fast alles um die Datenbank («Datenbank-zentrisches Amt»). Fast alles wurde von irgendwo her in die Datenbank importiert, vieles wurde auch darin berechnet und sämtliche Daten wurden in irgendeiner Form auch wieder exportiert. So weit, so gut. Die Schnittstellen wie die Daten in die Datenbank reinkommen oder wieder rauskommen oder wie die Berechnung durchgeführt wird, ist unendlich heterogen gelöst. Zuerst good old PHP4, später dann Python-Skripte («Yeah, wir sind modern.») und auch mal Java hier und dort. Import, Datenumbau und Berechnung im Skript wild durcheinander. Und immer wieder Probleme mit Fremddatenbanken (Oracle, SQL-Server), da keine passenden Treiber im Haus. Wir haben festgestellt, dass sich die einzelnen Schritte eines solchen Prozesses leicht identifizieren lassen und auch kapseln lassen und diese eben auch immer etwa gleich aussehen. Die einzelnen Schritte werden als Gradle-Tasks erstellt und anschliessend zu einem Job aneinandergereit. Gradle haben wir gewählt, weil es bereits viel von unseren Anforderungen out-of-the-box oder durch Plugins abdecken kann:
Herunterladen, Entzippen und Kopieren von Daten
Logging
Abhängigkeiten zwischen den einzelnen Tasks (tasks ordering)
Via SSH Befehle auf externen Servern ausführen.
Deployments auf AWS etc.
Dokumentation mit Asciidoctor
etc. etc.
Für die fehlenden zusätzlichen Tasks ist Gradle relativ einfach mit Java (oder Groovy) erweiterbar.
Der Quellcode befindet sich im dazugehörigen Github-Repository. Die ganze Geschichte ist momentan noch klein wenig unübersichtlich strukturiert. Neben den ganzen Funktionalitäten, gibt es im Repository noch viel anderen Code: CI/CD-Pipeline inkl. Deployment in einer OpenShift-Umgebung als Docker-Container und viel Code für die Abhängigkeiten der Integrationstests etc. Der eigentliche Quellcode liegt im Unterordner gretl
. Eine umfangreiche User-Dokumentation im Ordner docs/user
inkl. einem minimalen Beispiel.
Zum jetzigen Zeitpunkt gibt es folgende zusätzlichen Tasks:
SQLExecutor: Dieser Task führt beliebige parametrisierbare SQL-Befehle in einer Datenbank aus.
Db2Db: Kopiert Tabellen aus einer Datenbank in einer andere. Einfache, parametrisierbare Datenumbauten sind möglich. Dank JDBC wird (theoretisch) jede Datenbank unterstützt für die ein JDBC-Treiber verfügbar ist.
CsvImport / CsvExport: Import und Export von CSV-Dateien in resp. aus einer Datenbank.
ShpImport / ShpExport: Import und Export von Shapedateien. (Traurig aber wahr: Sogar noch vor GeoPackage realisiert…)
CsvValidator / ShpValidator: Prüft den Inhalt der CSV- resp. Shapedateien gegenüber einer vorgegebenen Struktur. Die umgesetzte Lösung ist schlicht genial. Dazu in einem anderen Beitrag mehr.
Ili2pg: Für den Umgang mit INTERLIS wurden mit ili2pg verschiedene Tasks gebaut (Import, Export, Replace, Schema-Import)
Ilivalidator: Zum Prüfen für die INTERLIS-Transferdateien wurde ein ilivalidator-Task programmiert.
Mit diesen funktional atomaren Tasks (und den in Gradle vorhandenen Funktionalitäten) kommt man bereits jetzt ziemlich weit.
Builden und installieren funktioniert wie man es von Gradle kennt:
./gradlew clean build install
Das erstellte Produkt ist ein Gradle-Plugin, das im lokalen Maven-Respository installiert wird und anschliessend in den Jobs (= build.gradle) angesprochen werden kann.
Herausfordernd ist die Testerei: Will man möglichst Nahe an der Realität testen, kommt man nicht um eine PostgreSQL-/PostGIS-Datenbank herum. Viel JDBC-mässiger Code lässt sich zwar auch mit einer Derby-Datenbank testen, aber wenn es um «Geo» geht, muss PostgreSQL/PostGIS her. Momentan ist das so gelöst, dass der Entwickler manuell eine dockerisierte PostgreSQL/PostGIS-Datenbank hochfährt und anschliessend die Tests einer bestimmten Kategorie durchführen kann. Denkbar wäre auch, dass man in Gradle verschiedene TestSets einführt und so eine bessere Steuerbarkeit der Tests herbeiführt. Anstatt die dockerisierte Datenbank manuell hochzufahren, fände ich z.B. TestContainers eine sehr elegante Lösung. «Passt scho.»
Wir werden bei uns in der Produktion nicht das pure Plugin verwenden, sondern möchten eine GRETL-Runtime als Docker-Image in Betrieb nehmen. Im Image sind sämtliche benötigten Abhängigkeiten (im Sinne von Bibliotheken etc.). Gradles Dependency Management ist ja schön und gut, im produktiven Betrieb will man aber nicht zu viel Feenstaub, sondern exakt wissen welche Version welcher Bibliothek in Betrieb ist oder zumindest sicher sein, dass heute nicht eine andere in Betrieb ist als gestern. Ebenso werden die benötigten INTERLIS-Modellablagen mit ili2c geklont, um komplett offline agieren zu können.
Ganz abgefahren wird es bei der Orchestrierung der Jobs. Wir gehen davon aus, dass wir circa 100 bis 200 einzelne Jobs haben werden. Der grösste Teil sind Datenumbauten in der Datenbank resp. von einer Erfassungsdatenbank in eine Publikationsdatenbank. Bei dieser Anzahl von Jobs will man in keiner Software selber gross konfigurieren. Sei es erstmalig oder bei Änderungen, die sämtliche Jobs betreffen. Als Fundament für diese Orchestrierung setzen wir Jenkins ein. Für die Konfiguration der Jobs wird es einen «Seeder-Job» geben, der sämtliche anderen Jobs aufgrund eines Jenkinsfiles in Jenkins definiert. Diese Jobs können anschliessend cronjob-mässig automatisch ausgeführt werden oder auch on-demand durch einen Benutzer. Und wenn das alles so klappt wie gewünscht, habe ich ein paar graue Haare mehr und mache drei Kreuze an die Decke… Jedenfalls ist es «Spatial Is Not Special» ziemlich auf die Spitze getrieben.
Der Appetit kommt wie so oft mit dem Essen. Folgende zusätzlichen Tasks oder Funktionalitäten stehen auf dem Wunschzettel:
Unterstützung von GeoPackage
Bei den Export-Prozessen sollen auch SQL-Queries als Input möglich sein und nicht bloss Tabellen oder Views.
Die Geometrie (in einer noch zu bestimmenden Form) soll beim CSV-Export unterstützt werden.
Unterstützung für JasperResports
Upload-Funktionalität in die Aggregationsinfrastruktur der Kantone
Vielleicht ein wenig unorthodox: Ein Kettle-PDI-Ausführ-Task.
???