mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-15 06:52:04 +00:00
fix(db): read the clock as UTC in raw SQL and prove it on a Berlin database
Every raw-SQL clock read against a timestamp column without a zone now writes NOW() AT TIME ZONE 'UTC', so passkey and login challenges, rate limits, provider pauses, coach usage stamps, tombstone and measurement windows no longer pass through the session time zone even where the per-connection pin cannot apply. The regression-window bounds stay naive UTC timestamps instead of being turned into timestamptz. A structural guard keeps bare NOW() / CURRENT_TIMESTAMP out of raw SQL outside a short list of timestamptz targets. The integration test sets the database default to Europe/Berlin and drives challenge claims, session expiry, the native sign-in handoff freshness check and the rate-limit window through the app's client, plus the challenge claim through an unpinned client. Self-hosting docs describe the pin for an existing or shared Postgres.
This commit is contained in:
1 parent
cefc8c15c1
commit
f65d25b0d0
22 files changed
+469
-66
No files matched your search
@@ -77,6 +77,26 @@ process (`pnpm dev` against a host Postgres) or a **custom compose** that
|
||||
does not derive it — in that case uncomment and set `DATABASE_URL`
|
||||
explicitly in `.env` (see the commented example there).
|
||||
|
||||
### Using an existing or shared Postgres
|
||||
|
||||
HealthLog can live in a database on a Postgres instance you already run.
|
||||
It stores every time as UTC and pins each connection it opens to UTC
|
||||
(`TimeZone=UTC`, sent with the libpq `options` startup parameter), so the
|
||||
server's or the database's own `timezone` setting does not matter: an
|
||||
instance set to `Europe/Berlin` works the same as one set to UTC. The app
|
||||
checks the setting at startup and logs a warning naming the zone if a
|
||||
connection still reports something else.
|
||||
|
||||
Behind PgBouncer with `DATABASE_SESSION_OPTIONS_DISABLED=1` (see
|
||||
[Scaling](./scaling.md#postgres-connection-pool-sizing)) the app pins the
|
||||
zone with `SET TIME ZONE 'UTC'` on each new connection instead; PgBouncer
|
||||
keeps that setting per client in transaction pooling too. If the startup
|
||||
warning still appears, set the database default as well:
|
||||
|
||||
```sql
|
||||
ALTER DATABASE healthlog SET timezone TO 'UTC';
|
||||
```
|
||||
|
||||
## 3. Bring the stack up
|
||||
|
||||
```bash
|
||||
|
||||
@@ -188,13 +188,16 @@ already`. Either raise `max_connections` on the Postgres side, lower
|
||||
`DATABASE_POOL_MAX`, or front the database with PgBouncer in
|
||||
transaction-pooling mode.
|
||||
|
||||
The app sets its statement timeout and `work_mem` on every connection
|
||||
through the libpq `options` startup parameter. PgBouncer refuses that
|
||||
The app sets its statement timeout, `work_mem` and `TimeZone=UTC` on every
|
||||
connection through the libpq `options` startup parameter. PgBouncer refuses that
|
||||
parameter (`unsupported startup parameter: options`) unless it is listed
|
||||
in `ignore_startup_parameters`. Either list it there, or set
|
||||
`DATABASE_SESSION_OPTIONS_DISABLED=1` so the app sends no session
|
||||
settings; set the timeout and `work_mem` on the database role instead
|
||||
(`ALTER ROLE healthlog SET work_mem = '16MB'`) if you still want them.
|
||||
The time zone is the exception: with the options off the app pins it with
|
||||
`SET TIME ZONE 'UTC'` on each new connection, which PgBouncer tracks per
|
||||
client, and warns at startup if a connection still reports another zone.
|
||||
The measurement maintenance job (Admin, System status) then applies its own
|
||||
settings with `SET` after connecting. Its concurrent index rebuild needs a
|
||||
session, so run it through a session-mode pool or a direct connection.
|
||||
|
||||
Reference in new issue
Block a user