fix(restore): re-init knex pool after DROP/CREATE DATABASE

`db.destroy()` during restore tore down the in-process connection
pool to release PG sessions so DROP DATABASE could succeed. After
CREATE DATABASE + psql restore, the old code did
`require('../database/db')` expecting a fresh instance — but Node
caches require results, so it got the SAME destroyed instance back.
Every subsequent query in the process failed with "Unable to acquire
a connection" until the container was manually restarted, even
though the restore technically succeeded.

Net effect for admins: login showed "An error occurred", customer /
invoice / quote pages were blank, no surface hinted at the dead pool.

Cure: db.js now wraps the live knex instance in a Proxy that forwards
to a mutable internal reference, with a `reinitPool()` function that
destroys the old instance + builds a fresh one + probes with `SELECT 1`
so any reconnect failure surfaces immediately. The thousands of
existing `const { db } = require(...)` imports work unchanged — they
capture the Proxy once, and every call goes through to the current pool.

restoreService calls reinitPool() after CREATE DATABASE and before
migrate.latest(), so the rest of the request + every subsequent admin
action runs against the fresh pool. Container restart no longer
needed after restore.
This commit is contained in:
Luca
2026-05-31 22:43:40 +02:00
parent c435263744
commit 48e9c9c79a
+20 -5
View File
@@ -954,12 +954,27 @@ END $$;`
this.log('info', 'Sequence resync completed');
}
// Re-initialize database connection
const { db: newDb } = require('../database/db');
// Run migrations to ensure schema is up to date
// Re-initialize the in-process knex pool. The DROP/CREATE
// DATABASE pair above destroyed our connections and the recreated
// database has a different pg_database OID — any pooled
// connection from before would either be dead or pointed at a
// ghost. Without explicit reinit, every query in the process
// after restore returns `Error: Unable to acquire a connection`
// until the container is manually restarted (and admin sees
// "An error occurred" on the login screen even after the restore
// technically succeeded). reinitPool destroys + rebuilds the
// pool and probes the new one with `SELECT 1` so failures here
// surface immediately instead of polluting the next request.
const { reinitPool } = require('../database/db');
this.log('info', 'Re-initializing knex pool against the restored database...');
await reinitPool();
this.log('info', 'Knex pool re-initialized');
// Run migrations to ensure schema is up to date. Use the
// module-level `db` export, which is the Proxy that now points
// at the freshly-initialized pool.
this.log('info', 'Running database migrations...');
await newDb.migrate.latest();
await db.migrate.latest();
return { success: true };