Merge pull request #2709 from fosrl/pool-update

Update pool and disable idp
This commit is contained in:
Owen Schwartz
2026-03-24 16:48:28 -07:00
committed by GitHub
4 changed files with 113 additions and 29 deletions

View File

@@ -1,7 +1,7 @@
import { drizzle as DrizzlePostgres } from "drizzle-orm/node-postgres"; import { drizzle as DrizzlePostgres } from "drizzle-orm/node-postgres";
import { Pool } from "pg";
import { readConfigFile } from "@server/lib/readConfigFile"; import { readConfigFile } from "@server/lib/readConfigFile";
import { withReplicas } from "drizzle-orm/pg-core"; import { withReplicas } from "drizzle-orm/pg-core";
import { createPool } from "./poolConfig";
function createDb() { function createDb() {
const config = readConfigFile(); const config = readConfigFile();
@@ -39,12 +39,17 @@ function createDb() {
// Create connection pools instead of individual connections // Create connection pools instead of individual connections
const poolConfig = config.postgres.pool; const poolConfig = config.postgres.pool;
const primaryPool = new Pool({ const maxConnections = poolConfig?.max_connections || 20;
const idleTimeoutMs = poolConfig?.idle_timeout_ms || 30000;
const connectionTimeoutMs = poolConfig?.connection_timeout_ms || 5000;
const primaryPool = createPool(
connectionString, connectionString,
max: poolConfig?.max_connections || 20, maxConnections,
idleTimeoutMillis: poolConfig?.idle_timeout_ms || 30000, idleTimeoutMs,
connectionTimeoutMillis: poolConfig?.connection_timeout_ms || 5000 connectionTimeoutMs,
}); "primary"
);
const replicas = []; const replicas = [];
@@ -55,14 +60,16 @@ function createDb() {
}) })
); );
} else { } else {
const maxReplicaConnections =
poolConfig?.max_replica_connections || 20;
for (const conn of replicaConnections) { for (const conn of replicaConnections) {
const replicaPool = new Pool({ const replicaPool = createPool(
connectionString: conn.connection_string, conn.connection_string,
max: poolConfig?.max_replica_connections || 20, maxReplicaConnections,
idleTimeoutMillis: poolConfig?.idle_timeout_ms || 30000, idleTimeoutMs,
connectionTimeoutMillis: connectionTimeoutMs,
poolConfig?.connection_timeout_ms || 5000 "replica"
}); );
replicas.push( replicas.push(
DrizzlePostgres(replicaPool, { DrizzlePostgres(replicaPool, {
logger: process.env.QUERY_LOGGING == "true" logger: process.env.QUERY_LOGGING == "true"

View File

@@ -1,9 +1,9 @@
import { drizzle as DrizzlePostgres } from "drizzle-orm/node-postgres"; import { drizzle as DrizzlePostgres } from "drizzle-orm/node-postgres";
import { Pool } from "pg";
import { readConfigFile } from "@server/lib/readConfigFile"; import { readConfigFile } from "@server/lib/readConfigFile";
import { withReplicas } from "drizzle-orm/pg-core"; import { withReplicas } from "drizzle-orm/pg-core";
import { build } from "@server/build"; import { build } from "@server/build";
import { db as mainDb, primaryDb as mainPrimaryDb } from "./driver"; import { db as mainDb, primaryDb as mainPrimaryDb } from "./driver";
import { createPool } from "./poolConfig";
function createLogsDb() { function createLogsDb() {
// Only use separate logs database in SaaS builds // Only use separate logs database in SaaS builds
@@ -42,12 +42,17 @@ function createLogsDb() {
// Create separate connection pool for logs database // Create separate connection pool for logs database
const poolConfig = logsConfig?.pool || config.postgres?.pool; const poolConfig = logsConfig?.pool || config.postgres?.pool;
const primaryPool = new Pool({ const maxConnections = poolConfig?.max_connections || 20;
const idleTimeoutMs = poolConfig?.idle_timeout_ms || 30000;
const connectionTimeoutMs = poolConfig?.connection_timeout_ms || 5000;
const primaryPool = createPool(
connectionString, connectionString,
max: poolConfig?.max_connections || 20, maxConnections,
idleTimeoutMillis: poolConfig?.idle_timeout_ms || 30000, idleTimeoutMs,
connectionTimeoutMillis: poolConfig?.connection_timeout_ms || 5000 connectionTimeoutMs,
}); "logs-primary"
);
const replicas = []; const replicas = [];
@@ -58,14 +63,16 @@ function createLogsDb() {
}) })
); );
} else { } else {
const maxReplicaConnections =
poolConfig?.max_replica_connections || 20;
for (const conn of replicaConnections) { for (const conn of replicaConnections) {
const replicaPool = new Pool({ const replicaPool = createPool(
connectionString: conn.connection_string, conn.connection_string,
max: poolConfig?.max_replica_connections || 20, maxReplicaConnections,
idleTimeoutMillis: poolConfig?.idle_timeout_ms || 30000, idleTimeoutMs,
connectionTimeoutMillis: connectionTimeoutMs,
poolConfig?.connection_timeout_ms || 5000 "logs-replica"
}); );
replicas.push( replicas.push(
DrizzlePostgres(replicaPool, { DrizzlePostgres(replicaPool, {
logger: process.env.QUERY_LOGGING == "true" logger: process.env.QUERY_LOGGING == "true"

View File

@@ -0,0 +1,63 @@
import { Pool, PoolConfig } from "pg";
import logger from "@server/logger";
export function createPoolConfig(
connectionString: string,
maxConnections: number,
idleTimeoutMs: number,
connectionTimeoutMs: number
): PoolConfig {
return {
connectionString,
max: maxConnections,
idleTimeoutMillis: idleTimeoutMs,
connectionTimeoutMillis: connectionTimeoutMs,
// TCP keepalive to prevent silent connection drops by NAT gateways,
// load balancers, and other intermediate network devices (e.g. AWS
// NAT Gateway drops idle TCP connections after ~350s)
keepAlive: true,
keepAliveInitialDelayMillis: 10000, // send first keepalive after 10s of idle
// Allow connections to be released and recreated more aggressively
// to avoid stale connections building up
allowExitOnIdle: false
};
}
export function attachPoolErrorHandlers(pool: Pool, label: string): void {
pool.on("error", (err) => {
// This catches errors on idle clients in the pool. Without this
// handler an unexpected disconnect would crash the process.
logger.error(
`Unexpected error on idle ${label} database client: ${err.message}`
);
});
pool.on("connect", (client) => {
// Set a statement timeout on every new connection so a single slow
// query can't block the pool forever
client.query("SET statement_timeout = '30s'").catch((err: Error) => {
logger.warn(
`Failed to set statement_timeout on ${label} client: ${err.message}`
);
});
});
}
export function createPool(
connectionString: string,
maxConnections: number,
idleTimeoutMs: number,
connectionTimeoutMs: number,
label: string
): Pool {
const pool = new Pool(
createPoolConfig(
connectionString,
maxConnections,
idleTimeoutMs,
connectionTimeoutMs
)
);
attachPoolErrorHandlers(pool, label);
return pool;
}

View File

@@ -275,6 +275,8 @@ export default function Page() {
} }
} }
const disabled = !isPaidUser(tierMatrix.orgOidc);
return ( return (
<> <>
<div className="flex justify-between"> <div className="flex justify-between">
@@ -292,6 +294,9 @@ export default function Page() {
</Button> </Button>
</div> </div>
<PaidFeaturesAlert tiers={tierMatrix.orgOidc} />
<fieldset disabled={disabled} className={disabled ? "opacity-50 pointer-events-none" : ""}>
<SettingsContainer> <SettingsContainer>
<SettingsSection> <SettingsSection>
<SettingsSectionHeader> <SettingsSectionHeader>
@@ -812,9 +817,10 @@ export default function Page() {
</Button> </Button>
<Button <Button
type="submit" type="submit"
disabled={createLoading || !isPaidUser(tierMatrix.orgOidc)} disabled={createLoading || disabled}
loading={createLoading} loading={createLoading}
onClick={() => { onClick={() => {
if (disabled) return;
// log any issues with the form // log any issues with the form
console.log(form.formState.errors); console.log(form.formState.errors);
form.handleSubmit(onSubmit)(); form.handleSubmit(onSubmit)();
@@ -823,6 +829,7 @@ export default function Page() {
{t("idpSubmit")} {t("idpSubmit")}
</Button> </Button>
</div> </div>
</fieldset>
</> </>
); );
} }