커넥션 재시도 작업
This commit is contained in:
parent
cafc88c37c
commit
d850e8154c
|
|
@ -5,11 +5,13 @@ import { logger } from '../utils/logger';
|
||||||
|
|
||||||
// Prisma 7 requires a driver adapter for direct database connections.
|
// Prisma 7 requires a driver adapter for direct database connections.
|
||||||
const resolveDbSchema = (): string => {
|
const resolveDbSchema = (): string => {
|
||||||
const raw = process.env.DATABASE_URL || '';
|
const raw = (process.env.DATABASE_URL || '').trim().replace(/^['"]|['"]$/g, '');
|
||||||
try {
|
try {
|
||||||
const u = new URL(raw);
|
const u = new URL(raw);
|
||||||
// Prisma-style: ?schema=kord_live
|
// Prisma-style: ?schema=kord_live
|
||||||
return u.searchParams.get('schema') || 'public';
|
const schema = u.searchParams.get('schema') || 'public';
|
||||||
|
// Guard against invalid/unsafe identifiers in connection options.
|
||||||
|
return /^[a-zA-Z_][a-zA-Z0-9_]*$/.test(schema) ? schema : 'public';
|
||||||
} catch {
|
} catch {
|
||||||
return 'public';
|
return 'public';
|
||||||
}
|
}
|
||||||
|
|
@ -18,6 +20,8 @@ const resolveDbSchema = (): string => {
|
||||||
const dbSchema = resolveDbSchema();
|
const dbSchema = resolveDbSchema();
|
||||||
const pool = new Pool({
|
const pool = new Pool({
|
||||||
connectionString: process.env.DATABASE_URL,
|
connectionString: process.env.DATABASE_URL,
|
||||||
|
// Fail fast when DB is temporarily unreachable; connectDB will retry.
|
||||||
|
connectionTimeoutMillis: 5_000,
|
||||||
// pg(Pool) doesn't understand Prisma's `?schema=...` param.
|
// pg(Pool) doesn't understand Prisma's `?schema=...` param.
|
||||||
// Force PostgreSQL search_path so both Prisma adapter and raw queries
|
// Force PostgreSQL search_path so both Prisma adapter and raw queries
|
||||||
// operate on the intended schema (e.g. kord_live).
|
// operate on the intended schema (e.g. kord_live).
|
||||||
|
|
@ -30,16 +34,36 @@ export const prisma = new PrismaClient({
|
||||||
log: ['warn', 'error'],
|
log: ['warn', 'error'],
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const sleep = (ms: number) => new Promise<void>(resolve => setTimeout(resolve, ms));
|
||||||
|
|
||||||
export const connectDB = async () => {
|
export const connectDB = async () => {
|
||||||
try {
|
const maxAttempts = 6;
|
||||||
// Adapter-based client connects when first used,
|
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
|
||||||
// but we can test the pool connection here.
|
try {
|
||||||
const client = await pool.connect();
|
// Adapter-based client connects when first used,
|
||||||
const { rows } = await client.query('SHOW search_path;');
|
// but we can test the pool connection here.
|
||||||
client.release();
|
const client = await pool.connect();
|
||||||
logger.info(`Connected to PostgreSQL successfully via Driver Adapter. (search_path=${rows?.[0]?.search_path ?? 'unknown'})`);
|
const { rows } = await client.query('SHOW search_path;');
|
||||||
} catch (error) {
|
client.release();
|
||||||
logger.error('Failed to connect to PostgreSQL:', error);
|
|
||||||
process.exit(1);
|
let hostPort = 'unknown';
|
||||||
|
try {
|
||||||
|
const u = new URL((process.env.DATABASE_URL || '').trim().replace(/^['"]|['"]$/g, ''));
|
||||||
|
hostPort = `${u.hostname}:${u.port || '5432'}`;
|
||||||
|
} catch {
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.info(
|
||||||
|
`Connected to PostgreSQL successfully via Driver Adapter. (targetSchema=${dbSchema}, search_path=${rows?.[0]?.search_path ?? 'unknown'}, host=${hostPort})`,
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
} catch (error) {
|
||||||
|
const isLast = attempt === maxAttempts;
|
||||||
|
logger.error(`Failed to connect to PostgreSQL (attempt ${attempt}/${maxAttempts}):`, error);
|
||||||
|
if (isLast) process.exit(1);
|
||||||
|
// Backoff: 0.5s, 1s, 2s, 4s, 8s...
|
||||||
|
await sleep(500 * 2 ** (attempt - 1));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue