refactor(db): consolidate the two Postgres pools
db/client.ts was creating its own pg Pool with its own lazy initialization, while src/lib/db.ts already had an identical pool and a withTx helper. Two pools per process means double the Postgres connections, double the env-var parsing, and double the BEGIN/COMMIT plumbing to maintain. After this change: - db/client.ts imports getPool/withTx from src/lib/db.ts - withBrand and withPlatformAdmin wrap withTx (single transaction helper) - One pg.Pool per Node process No behavior change for callers — withTx is byte-identical to the deleted runInTransaction. The shared getPool uses a 30s connection timeout (was 10s in the deleted copy); 30s is what src/lib/db.ts has always shipped to production via its callers, so this aligns the Drizzle layer with the rest of the app.
This commit is contained in:
+12
-49
@@ -23,38 +23,22 @@
|
||||
*/
|
||||
|
||||
import "server-only";
|
||||
import { Pool, type PoolClient } from "pg";
|
||||
import { drizzle, type NodePgDatabase } from "drizzle-orm/node-postgres";
|
||||
import * as schema from "./schema";
|
||||
import { getPool, withTx } from "@/lib/db";
|
||||
|
||||
type Schema = typeof schema;
|
||||
export type Db = NodePgDatabase<Schema>;
|
||||
|
||||
let _pool: Pool | null = null;
|
||||
|
||||
function getPool(): Pool {
|
||||
if (_pool) return _pool;
|
||||
const connectionString = process.env.DATABASE_URL;
|
||||
if (!connectionString) {
|
||||
throw new Error(
|
||||
"DATABASE_URL is not set. Add it to .env.local (see .env.example).",
|
||||
);
|
||||
}
|
||||
_pool = new Pool({
|
||||
connectionString,
|
||||
max: parseInt(process.env.PG_POOL_MAX ?? "10", 10),
|
||||
idleTimeoutMillis: parseInt(process.env.PG_POOL_IDLE_MS ?? "30000", 10),
|
||||
connectionTimeoutMillis: parseInt(
|
||||
process.env.PG_POOL_CONN_TIMEOUT_MS ?? "10000",
|
||||
10,
|
||||
),
|
||||
allowExitOnIdle: false,
|
||||
});
|
||||
_pool.on("error", (err) => {
|
||||
console.error("[db] idle client error", err);
|
||||
});
|
||||
return _pool;
|
||||
}
|
||||
/**
|
||||
* The Drizzle layer reuses the shared `pg` `Pool` from `@/lib/db` so the
|
||||
* app connects to Postgres through a single pool. Connection settings
|
||||
* (`PG_POOL_MAX`, `PG_POOL_IDLE_MS`, `PG_POOL_CONN_TIMEOUT_MS`,
|
||||
* `DATABASE_URL`) live in one place. See `src/lib/db.ts` for the config.
|
||||
*
|
||||
* The `withBrand` and `withPlatformAdmin` helpers reuse `withTx` from
|
||||
* `@/lib/db` so the BEGIN/COMMIT/ROLLBACK handling has a single home.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Run `fn` with a Drizzle client. No brand context is set — the caller
|
||||
@@ -83,7 +67,7 @@ export async function withBrand<T>(
|
||||
brandId: string,
|
||||
fn: (db: Db) => Promise<T>,
|
||||
): Promise<T> {
|
||||
return runInTransaction(async (client) => {
|
||||
return withTx(async (client) => {
|
||||
// set_config(setting, value, is_local) — is_local=true makes it
|
||||
// transaction-local so it auto-resets at COMMIT/ROLLBACK and never
|
||||
// leaks across pooled connections.
|
||||
@@ -103,7 +87,7 @@ export async function withBrand<T>(
|
||||
export async function withPlatformAdmin<T>(
|
||||
fn: (db: Db) => Promise<T>,
|
||||
): Promise<T> {
|
||||
return runInTransaction(async (client) => {
|
||||
return withTx(async (client) => {
|
||||
await client.query("SELECT set_config('app.current_brand_id', '', true)");
|
||||
await client.query("SELECT set_config('app.platform_admin', 'true', true)");
|
||||
const db = drizzle(client, { schema });
|
||||
@@ -111,25 +95,4 @@ export async function withPlatformAdmin<T>(
|
||||
});
|
||||
}
|
||||
|
||||
async function runInTransaction<T>(
|
||||
fn: (client: PoolClient) => Promise<T>,
|
||||
): Promise<T> {
|
||||
const client = await getPool().connect();
|
||||
try {
|
||||
await client.query("BEGIN");
|
||||
const result = await fn(client);
|
||||
await client.query("COMMIT");
|
||||
return result;
|
||||
} catch (err) {
|
||||
try {
|
||||
await client.query("ROLLBACK");
|
||||
} catch {
|
||||
// ignore secondary rollback failure
|
||||
}
|
||||
throw err;
|
||||
} finally {
|
||||
client.release();
|
||||
}
|
||||
}
|
||||
|
||||
export { schema };
|
||||
|
||||
Reference in New Issue
Block a user