UNPKG

@tehreet/conduit

Version:

LLM API gateway with intelligent routing, robust process management, and health monitoring

276 lines 9.05 kB
"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