From 7052238db752a37c9d181b13459531c9e08a20f0 Mon Sep 17 00:00:00 2001 From: Harlekin Date: Fri, 4 Sep 2026 10:45:38 +0200 Subject: [PATCH] Lebenszeichen des Workers zeitzonenfest pruefen Der Healthcheck meldete den Worker dauerhaft als krank, obwohl er im Sekundentakt schrieb: "Lebenszeichen 7214 s alt" - und 7214 Sekunden sind genau zwei Stunden, der Abstand zwischen CEST und UTC. `ServiceHeartbeat.at` ist ein timestamp ohne Zeitzone, und Prisma schreibt UTC hinein. node-postgres liest so einen Wert aber als *lokale* Zeit; im Container steht TZ=Europe/Berlin, und damit war jedes frisch geschriebene Lebenszeichen beim Lesen schlagartig zwei Stunden alt. Das Alter wird jetzt in der Datenbank gerechnet, gegen `now() AT TIME ZONE 'UTC'` - also in derselben Form, in der der Wert gespeichert ist. Die Rechnung stimmt damit unabhaengig davon, welche Zeitzone im Container oder in der Datenbank eingestellt ist. Auffallen konnte das nur im Betrieb: lokal laeuft die Datenbank auf UTC, dort ist die Abweichung null. --- worker/healthcheck.js | 30 ++++++++++++++++++++++++------ 1 file changed, 24 insertions(+), 6 deletions(-) diff --git a/worker/healthcheck.js b/worker/healthcheck.js index 8984232..80a67af 100644 --- a/worker/healthcheck.js +++ b/worker/healthcheck.js @@ -1,29 +1,47 @@ // Healthcheck des Dienstes `worker`, wird vom Compose aufgerufen. // Prueft das eigene Lebenszeichen in der Datenbank: ist es frisch und meldet -// `up`, gilt die Gateway-Verbindung als in Ordnung. Siehe docs/11-betrieb.md. +// `up`, gilt die Gateway-Verbindung als in Ordnung. Siehe README, Betrieb. // `pg` wird als CommonJS ausgeliefert, deshalb der Default-Import. import pg from 'pg' const { Client } = pg -const MAX_AGE_MS = 120_000 +const MAX_AGE_S = 120 const client = new Client({ connectionString: process.env.DIRECT_DATABASE_URL ?? process.env.DATABASE_URL }) try { await client.connect() + + // Das Alter wird **in der Datenbank** gerechnet und nicht in JavaScript. + // + // `at` ist ein `timestamp` ohne Zeitzone, und Prisma schreibt UTC hinein. + // node-postgres liest so einen Wert aber als *lokale* Zeit; im Container + // steht TZ=Europe/Berlin, und damit war jedes Lebenszeichen beim Lesen + // schlagartig zwei Stunden alt - der Dienst galt dauerhaft als krank, + // obwohl er im Sekundentakt schrieb. + // + // `now() AT TIME ZONE 'UTC'` liefert die aktuelle UTC-Wanduhrzeit als + // zeitzonenlosen Wert, also genau in der Form, in der `at` gespeichert ist. + // Die Rechnung stimmt damit unabhaengig davon, welche Zeitzone im Container + // oder in der Datenbank eingestellt ist. const { rows } = await client.query( - 'SELECT status, at FROM "ServiceHeartbeat" WHERE service = $1', + `SELECT status, + EXTRACT(EPOCH FROM (now() AT TIME ZONE 'UTC' - at)) AS alter_s + FROM "ServiceHeartbeat" + WHERE service = $1`, ['worker'], ) + const beat = rows[0] if (!beat) { console.error('kein Lebenszeichen') process.exit(1) } - const ageMs = Date.now() - new Date(beat.at).getTime() - if (ageMs > MAX_AGE_MS) { - console.error(`Lebenszeichen ${Math.round(ageMs / 1000)} s alt`) + + const alter = Math.round(Number(beat.alter_s)) + if (alter > MAX_AGE_S) { + console.error(`Lebenszeichen ${alter} s alt`) process.exit(1) } if (beat.status !== 'up') {