24 July 2022
Unser ilivalidator-web-service hat einige Neuerungen spendiert bekommen, unter anderem:
Konfiguration
Es ist möglich eigene Konfigurationen (aka toml- resp. ini-Dateien und zusätzliche INTERLIS-Modelle) zu verwenden. Der Webservice berücksichtigt zwei Verzeichnisse (config/toml/ und config/ili) während der Prüfung einer INTERLIS-Transferdatei. Es werden standardmässig einige Solothurner Konfigurationen reinkopiert, was jedoch unterbunden werden kann. Das config-Verzeichnis und die beiden Unterverzeichnisse werden im Webservice via URL http://localhost:8080/config exponiert. Damit wird für die Benutzer die Prüfung transparenter, da sie eine allfällige zusätzliche Konfiguration sehen:
REST-API
Es gibt neben dem GUI neu eine REST-API für eine M2M-Kommunikation. Da es sich um long running tasks handelt, kann kein synchroner Prozessablauf gewählt werden, d.h. man kann nicht eine Datei hochladen und die Verbindung zum Server bleibt offen, bis die Prüfung durch ist. Im GUI wird aus diesem Grund auf das Websocket-Protokoll gesetzt. Bei der REST-API verwenden wir jedoch «nur» HTTP.
Wenn man eine Datei hochlädt, erhält man als Antwort den Statuscode 202 ACCEPTED
und den Header Operation-Location
, der auf eine Ressource zeigt.
curl -i -X POST -F file=@254900.itf http://localhost:8080/rest/jobs
liefert:
HTTP/1.1 202
Operation-Location: http://localhost:8080/rest/jobs/4d4aa583-6575-4200-a39c-621a5190d36d
Content-Length: 0
Date: Sat, 23 Jul 2022 16:40:50 GMT
Die Ressource /rest/jobs/4d4aa583-6575-4200-a39c-621a5190d36d
liefert mir Informationen über den Stand der Validierung meiner Datei:
curl -i -X GET http://localhost:8080/rest/jobs/4d4aa583-6575-4200-a39c-621a5190d36d
liefert:
HTTP/1.1 200
Retry-After: 30
Content-Type: application/json
Transfer-Encoding: chunked
Date: Sat, 23 Jul 2022 16:43:12 GMT
{"createdAt":"2022-07-23T16:42:15.68317767","updatedAt":"2022-07-23T16:42:15.68317767","status":"PROCESSING"}
Es gibt einen Retry-Header
, der Clients anweist, 30 Sekunden zu warten bis zur nächsten Abfrage. Ist die Validierung abgeschlossen, enthält die Antwort Links zu den Logdateien:
HTTP/1.1 200
Content-Type: application/json
Transfer-Encoding: chunked
Date: Sat, 23 Jul 2022 16:43:29 GMT
{"createdAt":"2022-07-23T16:42:15.68317767","updatedAt":"2022-07-23T16:43:16.011457796","status":"SUCCEEDED","logFileLocation":"http://localhost:8080/logs/ilivalidator_8148789347157812698/254900.itf.log","xtfLogFileLocation":"http://localhost:8080/logs/ilivalidator_8148789347157812698/254900.itf.log.xtf"}
Die Joborchestrierung ist mit JobRunr umgesetzt. Eine Bibliothek für verteiltes Background Processing. Die Persistierung wird über eine Datenbank gemacht. JobRunr hat viele Features und Einstellungsmöglichkeiten, ist aber trotzdem sehr einfach in der Handhabung. Ein interessantes Feature ist der Umgang mit nicht beendeten Jobs, z.B. bei einem Server-Absturz. Diese werden nach einem Restart nochmals ausgeführt. Es geht also nichts verloren. Verteile Prozessierung bedeutet in diesem Fall über die Grenzen der JVM und über die Grenzen von Servern hinweg, solange die «Arbeitstiere» Zugriff auf die gemeinsame Datenbank haben. Im absolut einfachsten Fall gibt es aber nur einen Webservice, dieser fungiert als Schnittstellenserver als auch als Validierungsserver und verwendet eine In-Memory-Datenbank.
ilivalidator Cluster
Was kann man mit der neuen REST-API und deren Umsetzung mit JobRunr machen? Zum Beispiel einen ilivalidator Cluster mit 15 Worker. Dazu reicht eine einfache docker-compose-Konfiguration:
version: '3'
services:
frontend:
image: sogis/ilivalidator-web-service:2
environment:
TZ: Europe/Zurich
ports:
- 8080:8080
- 8000:8000
volumes:
- type: bind
source: /tmp/docbase
target: /docbase
- type: bind
source: /tmp/work
target: /work
worker:
image: sogis/ilivalidator-web-service:2
deploy:
replicas: 15
environment:
TZ: Europe/Zurich
JOBRUNR_DASHBOARD_ENABLED: "false"
REST_API_ENABLED: "false"
UNPACK_CONFIG_FILES: "false"
CLEANER_ENABLED: "false"
volumes:
- type: bind
source: /tmp/docbase
target: /docbase
- type: bind
source: /tmp/work
target: /work
Es wird ein (1) Frontend-Schnittstellen-Service gestartet, welcher die Uploads entgegennimmt. Es wird ein worker-Service mit 15 Replicas gestartet, die als JobRunr-Backgroundserver arbeiten. Beide Services verwenden das gleiche Dockerimage. Beim worker-Service werden einige Funktionalitäten ausgeschaltet, da diese nicht benötigt werden. Zudem ist der worker-Service von aussen nicht erreichbar. Sauberer wäre natürlich, wenn man Ressource-Limiten (RAM, CPU) setzt. Dies ist im Nicht-Swarm-Mode mit der Version 3 für die CPU nicht möglich und darum wird darauf verzichtet. Das sollte für unseren Test egal sein.
Nun benötigt man genügend Server-Ressourcen, d.h. man mietet sich bei Digitalocean einen 40vCPU-Server mit 160GB RAM. Man sollte ihn einfach nicht vergessen zu löschen, da das Teil monatlich mit $1260.00 zu Buche schlägt.
Als Testdaten verwende ich die AV-Daten des Kantons Bern, die ich via geodienste.ch heruntergeladen habe. Es handelt sich um 339 Gemeinden. Ein simples Jbang-Skript lädt mir die Daten via REST-API zum Server hoch. Nun geht die Post ab. Nachfolgend ein Screenshot des JobRunr-Dashboards und eine docker stats
-Ausgabe:
Sämtliche 339 Gemeinden konnten innerhalb von circa 17 Minuten geprüft werden. Limitierender Faktor war zu guter Letzt die Gemeinde Bern, welche 8 Minuten benötigt, wobei 2 davon als einzige Gemeinde. Diese sollte also möglichst zu Beginn hochgeladen werden.
Anschliessend versuchte ich 29 Worker zu verwenden, was aber nicht erfolgreich funktionierte. Ein Drittel der Dockercontainer wurden wieder runtergefahren. Der Grund war mir auf die Schnelle nicht klar (Hinweis: Ressourcen setzen!). Eventuell wird unter gewissen Umständen der I/O zum Problem, wenn 30+ Replicas grossen Traffic verursachen.
Im Grunde genommen kann man den ilivalidator-web-service so einfachst beliebig horizontal skalieren. Aus Gründen sollten die Worker nicht auf dem gleichen Server laufen und Ressourcen (limits
und reservations
oder äquivalente Einstellungen) gesetzt werden.