From 3639dcc92aef898fd9ede92a8e8aa8a87ea421e9 Mon Sep 17 00:00:00 2001 From: Mononaut Date: Fri, 10 Nov 2023 07:45:02 +0000 Subject: [PATCH 1/3] Recover from stale PID file --- backend/src/database.ts | 32 +++++++++++++++++++++++++------- 1 file changed, 25 insertions(+), 7 deletions(-) diff --git a/backend/src/database.ts b/backend/src/database.ts index c27f28d23..655dad47c 100644 --- a/backend/src/database.ts +++ b/backend/src/database.ts @@ -4,6 +4,7 @@ import config from './config'; import { createPool, Pool, PoolConnection } from 'mysql2/promise'; import logger from './logger'; import { FieldPacket, OkPacket, PoolOptions, ResultSetHeader, RowDataPacket } from 'mysql2/typings/mysql'; +import { execSync } from 'child_process'; class DB { constructor() { @@ -105,18 +106,34 @@ import { FieldPacket, OkPacket, PoolOptions, ResultSetHeader, RowDataPacket } fr public getPidLock(): boolean { const filePath = path.join(config.DATABASE.PID_DIR || __dirname, `/mempool-${config.DATABASE.DATABASE}.pid`); + this.enforcePidLock(filePath); + fs.writeFileSync(filePath, `${process.pid}`); + return true; + } + + private enforcePidLock(filePath: string): void { if (fs.existsSync(filePath)) { - const pid = fs.readFileSync(filePath).toString(); - if (pid !== `${process.pid}`) { - const msg = `Already running on PID ${pid} (or pid file '${filePath}' is stale)`; + const pid = parseInt(fs.readFileSync(filePath, 'utf-8')); + if (pid === process.pid) { + logger.warn('PID file already exists for this process'); + return; + } + + let cmd; + try { + cmd = execSync(`ps -p ${pid} -o args=`); + } catch (e) { + logger.warn(`Stale PID file at ${filePath}, but no process running on that PID ${pid}`); + return; + } + + if (cmd && cmd.toString()?.includes('node')) { + const msg = `Another mempool nodejs process is already running on PID ${pid}`; logger.err(msg); throw new Error(msg); } else { - return true; + logger.warn(`Stale PID file at ${filePath}, but the PID ${pid} does not belong to a running mempool instance`); } - } else { - fs.writeFileSync(filePath, `${process.pid}`); - return true; } } @@ -124,6 +141,7 @@ import { FieldPacket, OkPacket, PoolOptions, ResultSetHeader, RowDataPacket } fr const filePath = path.join(config.DATABASE.PID_DIR || __dirname, `/mempool-${config.DATABASE.DATABASE}.pid`); if (fs.existsSync(filePath)) { const pid = fs.readFileSync(filePath).toString(); + // only release our own pid file if (pid === `${process.pid}`) { fs.unlinkSync(filePath); } From 8c4b488251cc2aae70d142a78715b33e2ad3431c Mon Sep 17 00:00:00 2001 From: Mononaut Date: Mon, 13 Nov 2023 07:33:53 +0000 Subject: [PATCH 2/3] handle SIGHUP exit code --- backend/src/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/src/index.ts b/backend/src/index.ts index e7e1afa3d..59c92fbf3 100644 --- a/backend/src/index.ts +++ b/backend/src/index.ts @@ -92,7 +92,7 @@ class Server { logger.notice(`Starting Mempool Server${worker ? ' (worker)' : ''}... (${backendInfo.getShortCommitHash()})`); // Register cleanup listeners for exit events - ['exit', 'SIGINT', 'SIGTERM', 'SIGUSR1', 'SIGUSR2', 'uncaughtException', 'unhandledRejection'].forEach(event => { + ['exit', 'SIGHUP', 'SIGINT', 'SIGTERM', 'SIGUSR1', 'SIGUSR2', 'uncaughtException', 'unhandledRejection'].forEach(event => { process.on(event, () => { this.onExit(event); }); }); From c6a92083a8621e50a7b5410ea9909d4e4233da0a Mon Sep 17 00:00:00 2001 From: Mononaut Date: Mon, 13 Nov 2023 07:53:13 +0000 Subject: [PATCH 3/3] Fix pid parsing on release lock --- backend/src/database.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/backend/src/database.ts b/backend/src/database.ts index 655dad47c..0638aa319 100644 --- a/backend/src/database.ts +++ b/backend/src/database.ts @@ -140,9 +140,9 @@ import { execSync } from 'child_process'; public releasePidLock(): void { const filePath = path.join(config.DATABASE.PID_DIR || __dirname, `/mempool-${config.DATABASE.DATABASE}.pid`); if (fs.existsSync(filePath)) { - const pid = fs.readFileSync(filePath).toString(); + const pid = parseInt(fs.readFileSync(filePath, 'utf-8')); // only release our own pid file - if (pid === `${process.pid}`) { + if (pid === process.pid) { fs.unlinkSync(filePath); } }