@tehreet/conduit
Version:
LLM API gateway with intelligent routing, robust process management, and health monitoring
276 lines • 9.05 kB
JavaScript
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.PidManager = void 0;
const fs_1 = require("fs");
const constants_1 = require("../constants");
const path = __importStar(require("path"));
const os = __importStar(require("os"));
/**
* Robust PID file manager with file locking support
*/
class PidManager {
/**
* Acquire an exclusive lock on the PID file
*/
static acquireLock() {
const startTime = Date.now();
while (Date.now() - startTime < this.lockTimeout) {
try {
// Try to create lock file exclusively
const fd = (0, fs_1.openSync)(this.lockFile, 'wx');
(0, fs_1.closeSync)(fd);
return true;
}
catch (err) {
if (err.code === 'EEXIST') {
// Lock file exists, check if it's stale
try {
const lockStat = require('fs').statSync(this.lockFile);
const lockAge = Date.now() - lockStat.mtimeMs;
// If lock is older than timeout, remove it
if (lockAge > this.lockTimeout) {
try {
(0, fs_1.unlinkSync)(this.lockFile);
}
catch (e) {
// Another process might have removed it
}
}
}
catch (e) {
// Lock file might have been removed
}
// Wait a bit before retrying
// Using a small busy wait since we need synchronous behavior
const waitUntil = Date.now() + 50;
while (Date.now() < waitUntil) {
// Busy wait
}
}
else {
throw err;
}
}
}
return false;
}
/**
* Release the lock on the PID file
*/
static releaseLock() {
try {
(0, fs_1.unlinkSync)(this.lockFile);
}
catch (e) {
// Lock might have been removed already
}
}
/**
* Save PID to file with locking
*/
static savePid(pid) {
// Ensure directory exists
const dir = path.dirname(constants_1.PID_FILE);
if (!(0, fs_1.existsSync)(dir)) {
require('fs').mkdirSync(dir, { recursive: true });
}
if (!this.acquireLock()) {
console.error('Failed to acquire lock for PID file');
return false;
}
try {
(0, fs_1.writeFileSync)(constants_1.PID_FILE, pid.toString());
return true;
}
catch (err) {
console.error('Failed to write PID file:', err);
return false;
}
finally {
this.releaseLock();
}
}
/**
* Read PID from file with locking
*/
static getPid() {
if (!(0, fs_1.existsSync)(constants_1.PID_FILE)) {
return null;
}
if (!this.acquireLock()) {
// If we can't get lock, try to read anyway (non-critical)
try {
const pid = parseInt((0, fs_1.readFileSync)(constants_1.PID_FILE, 'utf-8'));
return isNaN(pid) ? null : pid;
}
catch (e) {
return null;
}
}
try {
const pid = parseInt((0, fs_1.readFileSync)(constants_1.PID_FILE, 'utf-8'));
return isNaN(pid) ? null : pid;
}
catch (e) {
return null;
}
finally {
this.releaseLock();
}
}
/**
* Check if process is running
*/
static isProcessRunning(pid) {
try {
// Send signal 0 to check if process exists
process.kill(pid, 0);
return true;
}
catch (e) {
// ESRCH means process doesn't exist
// EPERM means process exists but we don't have permission
return e.code === 'EPERM';
}
}
/**
* Check if service is running
*/
static isServiceRunning() {
const pid = this.getPid();
if (!pid) {
return false;
}
const running = this.isProcessRunning(pid);
if (!running) {
// Process is dead, clean up PID file
this.cleanupPidFile();
}
return running;
}
/**
* Clean up PID file with locking
*/
static cleanupPidFile() {
if (!(0, fs_1.existsSync)(constants_1.PID_FILE)) {
return true;
}
if (!this.acquireLock()) {
console.error('Failed to acquire lock for PID file cleanup');
return false;
}
try {
(0, fs_1.unlinkSync)(constants_1.PID_FILE);
return true;
}
catch (e) {
return false;
}
finally {
this.releaseLock();
}
}
/**
* Stop service gracefully
*/
static async stopService(timeout = 30000) {
const pid = this.getPid();
if (!pid) {
console.log('No PID file found, service may not be running');
return true;
}
if (!this.isProcessRunning(pid)) {
console.log('Process is not running, cleaning up PID file');
this.cleanupPidFile();
return true;
}
try {
// Send SIGTERM for graceful shutdown
process.kill(pid, 'SIGTERM');
console.log(`Sent SIGTERM to process ${pid}, waiting for graceful shutdown...`);
// Wait for process to exit
const startTime = Date.now();
while (Date.now() - startTime < timeout) {
if (!this.isProcessRunning(pid)) {
console.log('Process stopped gracefully');
this.cleanupPidFile();
return true;
}
await new Promise(resolve => setTimeout(resolve, 100));
}
// If still running, force kill
console.log('Process did not stop gracefully, sending SIGKILL...');
process.kill(pid, 'SIGKILL');
// Wait a bit for the kill to take effect
await new Promise(resolve => setTimeout(resolve, 1000));
if (!this.isProcessRunning(pid)) {
this.cleanupPidFile();
return true;
}
return false;
}
catch (err) {
if (err.code === 'ESRCH') {
// Process already dead
this.cleanupPidFile();
return true;
}
console.error('Failed to stop service:', err);
return false;
}
}
/**
* Get comprehensive service info
*/
static getServiceInfo() {
const pid = this.getPid();
const running = pid ? this.isProcessRunning(pid) : false;
return {
running,
pid,
port: 3456,
endpoint: 'http://127.0.0.1:3456',
pidFile: constants_1.PID_FILE,
lockFile: this.lockFile,
platform: os.platform(),
node: process.version,
};
}
}
exports.PidManager = PidManager;
PidManager.lockFile = constants_1.PID_FILE + '.lock';
PidManager.lockTimeout = 5000; // 5 seconds timeout for acquiring lock
//# sourceMappingURL=pidManager.js.map