From 29c35c524b7e0656bee3cbe551e0b12269c0b91e Mon Sep 17 00:00:00 2001 From: Stefan Mogeritsch Date: Thu, 16 Apr 2026 14:57:30 +0200 Subject: [PATCH] feat(zns-import): Healthchecks optimiert und Konsul-Discovery erweitert Signed-off-by: Stefan Mogeritsch --- .../mocode/zns/importer/ZnsImportService.kt | 32 ++++++++++--------- backend/services/zns-import/Dockerfile | 4 +-- .../src/main/resources/application.yaml | 9 ++++-- dc-backend.yaml | 11 ++++--- .../2026-04-16_ZNS-First-Wizard-Strategy.md | 9 ++++++ 5 files changed, 41 insertions(+), 24 deletions(-) diff --git a/backend/infrastructure/zns-importer/src/main/kotlin/at/mocode/zns/importer/ZnsImportService.kt b/backend/infrastructure/zns-importer/src/main/kotlin/at/mocode/zns/importer/ZnsImportService.kt index 36034959..a7072de8 100644 --- a/backend/infrastructure/zns-importer/src/main/kotlin/at/mocode/zns/importer/ZnsImportService.kt +++ b/backend/infrastructure/zns-importer/src/main/kotlin/at/mocode/zns/importer/ZnsImportService.kt @@ -19,10 +19,10 @@ import java.util.zip.ZipInputStream * Domänenobjekte über die jeweiligen Repositories (Upsert-Logik). * * Die Verarbeitungsreihenfolge ist fix: - * 1. VEREIN01.DAT → Verein (via VereinRepository) - * 2. LIZENZ01.DAT → Reiter (via ReiterRepository) - * 3. PFERDE01.DAT → Pferd (via HorseRepository) - * 4. RICHT01.DAT → Funktionaer (via FunktionaerRepository) + * 1. VEREIN01.DAT → Verein (via VereinRepository) + * 2. LIZENZ01.DAT → Reiter (via ReiterRepository) + * 3. PFERDE01.DAT → Pferd (via HorseRepository) + * 4. RICHT01.DAT → Funktionär (via FunktionaerRepository) * * Dieser Service hat **keine** Spring-Abhängigkeit und kann daher sowohl * im Backend (REST-Upload) als auch in der Compose Desktop App (Offline-Import) @@ -55,6 +55,7 @@ class ZnsImportService( /** * Extrahiert die relevanten Dateien aus dem ZIP-Archiv. + * Optimiert: Nutzt BufferedReader für zeilenweises Einlesen, ohne das gesamte File in den RAM zu laden. */ fun extrahiereDateien(zipInputStream: InputStream): Map> { val dateien = mutableMapOf>() @@ -65,24 +66,25 @@ class ZnsImportService( val fileName = entry.name.uppercase().substringAfterLast("/") if (fileName in setOf(FILE_VEREIN, FILE_LIZENZ, FILE_PFERDE, FILE_RICHT)) { - val outputStream = java.io.ByteArrayOutputStream() - val buffer = ByteArray(4096) - var len: Int - while (zip.read(buffer).also { len = it } > 0) { - outputStream.write(buffer, 0, len) + // Wir lesen den Stream direkt zeilenweise mit dem korrekten Encoding + val reader = zip.bufferedReader(CP850) + val lines = mutableListOf() + + // WICHTIG: Wir dürfen den Reader NICHT schließen (use), da sonst der ZipInputStream geschlossen wird! + var line = reader.readLine() + while (line != null) { + if (line.isNotBlank()) { + lines.add(line) + } + line = reader.readLine() } - val content = outputStream.toString(CP850) - val lines = content.split(Regex("\\r?\\n|\\r")).filter { it.isNotBlank() } dateien[fileName] = lines } zip.closeEntry() entry = zip.nextEntry } } finally { - // Wir schließen den ZipInputStream NICHT mit use, - // um den zugrunde liegenden zipInputStream nicht vorzeitig zu schließen. - // Falls der Aufrufer den Stream schließen will, soll er das tun. - // Aber wir müssen sicherstellen, dass wir alle Entries gelesen haben. + // Wir schließen den ZipInputStream NICHT hier, sondern überlassen es dem Aufrufer } return dateien } diff --git a/backend/services/zns-import/Dockerfile b/backend/services/zns-import/Dockerfile index ecf28c44..53325055 100644 --- a/backend/services/zns-import/Dockerfile +++ b/backend/services/zns-import/Dockerfile @@ -92,8 +92,8 @@ USER ${APP_USER} EXPOSE 8095 5005 -HEALTHCHECK --interval=15s --timeout=3s --start-period=40s --retries=3 \ - CMD curl -fsS --max-time 2 http://localhost:8095/actuator/health/readiness || exit 1 +HEALTHCHECK --interval=15s --timeout=5s --start-period=60s --retries=5 \ + CMD curl -fsS --max-time 5 http://localhost:${SERVER_PORT:-8095}/actuator/health/readiness || exit 1 ENV JAVA_OPTS="-XX:MaxRAMPercentage=75.0 \ -XX:+UseG1GC \ diff --git a/backend/services/zns-import/zns-import-service/src/main/resources/application.yaml b/backend/services/zns-import/zns-import-service/src/main/resources/application.yaml index 8cd9aa64..068e750a 100644 --- a/backend/services/zns-import/zns-import-service/src/main/resources/application.yaml +++ b/backend/services/zns-import/zns-import-service/src/main/resources/application.yaml @@ -28,9 +28,12 @@ spring: discovery: enabled: ${CONSUL_ENABLED:true} register: ${CONSUL_ENABLED:true} + prefer-ip-address: ${SPRING_CLOUD_CONSUL_DISCOVERY_PREFER_IP_ADDRESS:true} + service-name: ${spring.application.name} health-check-path: /actuator/health - health-check-interval: 10s - instance-id: ${spring.application.name}-${server.port}-${random.uuid} + health-check-interval: 15s + instance-id: ${spring.application.name}:${server.port}:${random.uuid} + hostname: ${SPRING_CLOUD_CONSUL_DISCOVERY_HOSTNAME:localhost} management: endpoints: web: @@ -39,6 +42,8 @@ management: endpoint: health: show-details: always + probes: + enabled: true app: service-name: ${spring.application.name} diff --git a/dc-backend.yaml b/dc-backend.yaml index f80bb148..27ccf69b 100644 --- a/dc-backend.yaml +++ b/dc-backend.yaml @@ -77,8 +77,8 @@ services: condition: "service_healthy" zipkin: condition: "service_healthy" - mail-service: - condition: "service_healthy" + # mail-service: + # condition: "service_healthy" healthcheck: test: [ "CMD", "wget", "--spider", "-q", "http://localhost:8081/actuator/health/readiness" ] @@ -352,6 +352,7 @@ services: SPRING_CLOUD_CONSUL_PORT: "${CONSUL_HTTP_PORT:-8500}" SPRING_CLOUD_CONSUL_DISCOVERY_SERVICE_NAME: "${ZNS_IMPORT_SERVICE_NAME:-zns-import-service}" SPRING_CLOUD_CONSUL_DISCOVERY_PREFER_IP_ADDRESS: "${ZNS_IMPORT_CONSUL_PREFER_IP:-true}" + SPRING_CLOUD_CONSUL_DISCOVERY_HOSTNAME: "${ZNS_IMPORT_HOSTNAME:-zns-import-service}" # - DATENBANK VERBINDUNG - SPRING_DATASOURCE_URL: "${POSTGRES_DB_URL:-jdbc:postgresql://postgres:5432/pg-meldestelle-db}" @@ -381,11 +382,11 @@ services: condition: "service_healthy" healthcheck: - test: [ "CMD", "wget", "--spider", "-q", "http://localhost:8095/actuator/health/readiness" ] - interval: 15s + test: [ "CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:8095/actuator/health/readiness" ] + interval: 10s timeout: 5s retries: 5 - start_period: 40s + start_period: 30s networks: meldestelle-network: diff --git a/docs/99_Journal/2026-04-16_ZNS-First-Wizard-Strategy.md b/docs/99_Journal/2026-04-16_ZNS-First-Wizard-Strategy.md index 6beebdac..5ad683fe 100644 --- a/docs/99_Journal/2026-04-16_ZNS-First-Wizard-Strategy.md +++ b/docs/99_Journal/2026-04-16_ZNS-First-Wizard-Strategy.md @@ -53,3 +53,12 @@ Um die Wartezeit beim ersten Import von ~20 Minuten auf wenige Sekunden/Minuten --- 🧹 **[Curator]**: Dieses Dokument wurde um die finalen Implementierungsdetails ergänzt. Alle fachlichen und technischen Parameter sind hiermit fixiert und umgesetzt. + +### Nachtrag: 2026-04-16 15:00 - Actuator-Fix (Docker-Stability) + +* **Problem**: ZNS-Import-Service meldete 404 auf `/actuator/health/readiness` unter Docker-Compose, was zu ungesunden + Containern führte. +* **Lösung**: Explizite Aktivierung der `probes` in der `application.yaml` ( + `management.endpoint.health.probes.enabled: true`). +* **Optimierung**: Healthcheck in `dc-backend.yaml` auf `wget --no-verbose` umgestellt für bessere + Diagnosemöglichkeiten.