feat: shared pgboss singleton for job queue ops
This commit is contained in:
84
server/src/storage/pgboss.ts
Normal file
84
server/src/storage/pgboss.ts
Normal file
@@ -0,0 +1,84 @@
|
||||
/**
|
||||
* Shared PgBoss singleton for job queue operations
|
||||
*
|
||||
* This module provides a single PgBoss instance that can be safely
|
||||
* imported by both the main server process and routes without creating
|
||||
* duplicate connection pools.
|
||||
*
|
||||
* IMPORTANT: This is separate from pgboss-worker.ts which runs in a
|
||||
* forked child process and handles job processing.
|
||||
*/
|
||||
import PgBoss from 'pg-boss';
|
||||
import logger from '../logger';
|
||||
import dotenv from 'dotenv';
|
||||
|
||||
dotenv.config();
|
||||
|
||||
if (!process.env.DB_USER || !process.env.DB_PASSWORD || !process.env.DB_HOST || !process.env.DB_PORT || !process.env.DB_NAME) {
|
||||
throw new Error('One or more required environment variables are missing.');
|
||||
}
|
||||
|
||||
const pgBossConnectionString = `postgres://${process.env.DB_USER}:${encodeURIComponent(process.env.DB_PASSWORD)}@${process.env.DB_HOST}:${process.env.DB_PORT}/${process.env.DB_NAME}`;
|
||||
|
||||
/**
|
||||
* Shared PgBoss instance for submitting jobs (NOT processing)
|
||||
* This instance is only used to send jobs to queues, not to work on them
|
||||
*/
|
||||
export const pgBossClient = new PgBoss({
|
||||
connectionString: pgBossConnectionString,
|
||||
max: 3, // Small pool since we only send jobs
|
||||
ssl: {
|
||||
require: true,
|
||||
rejectUnauthorized: false,
|
||||
},
|
||||
});
|
||||
|
||||
let isStarted = false;
|
||||
|
||||
/**
|
||||
* Initialize the PgBoss client for job submission
|
||||
* Should be called once during server startup
|
||||
*/
|
||||
export async function startPgBossClient(): Promise<void> {
|
||||
if (isStarted) {
|
||||
logger.log('warn', 'PgBoss client already started, skipping...');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await pgBossClient.start();
|
||||
isStarted = true;
|
||||
logger.log('info', 'PgBoss client started successfully (job submission only)');
|
||||
} catch (error: any) {
|
||||
logger.log('error', `Failed to start PgBoss client: ${error.message}`);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop the PgBoss client gracefully
|
||||
*/
|
||||
export async function stopPgBossClient(): Promise<void> {
|
||||
if (!isStarted) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await pgBossClient.stop();
|
||||
isStarted = false;
|
||||
logger.log('info', 'PgBoss client stopped successfully');
|
||||
} catch (error: any) {
|
||||
logger.log('error', `Failed to stop PgBoss client: ${error.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
// Handle graceful shutdown
|
||||
process.on('SIGTERM', async () => {
|
||||
await stopPgBossClient();
|
||||
});
|
||||
|
||||
process.on('SIGINT', async () => {
|
||||
await stopPgBossClient();
|
||||
});
|
||||
|
||||
export default pgBossClient;
|
||||
Reference in New Issue
Block a user