Commit Graph
3 Commits
Author SHA1 Message Date
SravanandClaude Opus 4.8 0129489746 fix(db): pool safety timeouts to stop login hangs (connection exhaustion)
Production outage: logins (and eventually all requests) hung forever. Root
cause: the pg Pool was max:10 with NO connectionTimeoutMillis, so once all 10
connections were busy/stuck, every new query waited indefinitely for a free
client — the "login spinner that never ends". The reject path stayed fast
(one brief query); the success path needs several writes and blocked on
acquiring a connection. Server-side only (reproduced in a desktop browser).

Add fail-fast + self-healing pool config (all env-tunable):
- connectionTimeoutMillis (10s): callers error instead of hanging on a
  saturated pool.
- statement_timeout (60s): kill a runaway query so it can't pin a connection.
- idle_in_transaction_session_timeout (60s): reclaim a client left idle in a
  hung transaction (the classic slow pool leak).
- max 10 -> 20 for headroom; pool 'error' handler so a broken idle client
  can't crash the process.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-11-03 21:00:18 +05:30
SravanandClaude Opus 4.8 ad48829337 Retire the SQLite backend — Postgres is the only engine
The dual backend (SQLite via db.js + Postgres via schema.pg.sql) was a
maintenance foot-gun: a schema change could land on the SQLite path only and
silently 500 every read on prod (it just did, with #18/#13). Production has run
on Postgres for weeks, so SQLite is retired: ONE schema source of truth
(db/schema.pg.sql), no drift possible.

- dbx.js: default DB_BACKEND=pg; an unknown backend now fails loudly at require
  time instead of silently selecting a stale engine.
- Deleted server/db.js, server/db/sqlite.js, server/db/migrate-sqlite-to-pg.js,
  server/scripts/migrate-bizgaze-only.js (all SQLite-only, none in the runtime
  path — the running server loads db/pg.js).
- Tests (e2e, db-smoke) target Postgres now and fail-fast (skip) unless
  DATABASE_URL points at a disposable test DB — never SQLite, never prod.
- Removed the dead DB_PATH env + fixed misleading SQLite comments in the
  Dockerfile / docker-compose (kept the /data volume: it holds
  uploads/recordings/transcripts/downloads, not just the old data.db).
- CLAUDE.md: stack + repo-layout + run-locally updated for Postgres-only.

Runtime is unaffected (prod already sets DB_BACKEND=pg and pg is a prod dep);
this only removes the unused SQLite path.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-12 08:20:26 +05:30
SravanandClaude Opus 4.8 e67a783bdc feat(db): Postgres backend + dialect-portable queries + data migration (Phase 5)
- db/pg.js: the pg backend (prepare/exec/tx/init) — ?→$N translation, BIGINT parsed
  as Number (matches sqlite; else expires_at<Date.now() compares string<number),
  transactions on one pooled client, init() applies schema.pg.sql. Same interface as
  db/sqlite.js, so repos are unchanged.
- repos.js: the ~7 SQLite-only queries rewritten to run on BOTH engines —
  audit.add @named→positional; email lookups COLLATE NOCASE→LOWER()=LOWER();
  INSERT OR IGNORE→ON CONFLICT DO NOTHING (addMember/poll vote/favorite);
  mergeInto's UPDATE OR IGNORE→UPDATE…WHERE NOT EXISTS/NOT IN and INSERT OR
  REPLACE→ON CONFLICT DO UPDATE. Re-validated on sqlite: db-smoke still 22/22.
- server.js: boot now `await db.init()` before listening (pg creates tables; sqlite
  no-op), so the first request can't hit a missing table.
- db/migrate-sqlite-to-pg.js: one-shot row copy in FK order (bulk insert, TRUNCATE
  first so re-runnable). audit_log id left to PG's identity.
- package.json: add pg ^8.13.1.

Next: validate DB_BACKEND=pg smoke against a real Postgres on the server, then merge.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-24 22:28:04 +05:30