UNPKG

snow-flow

Version:

Snow-Flow v3.2.0: Complete ServiceNow Enterprise Suite with 180+ MCP Tools. ATF Testing, Knowledge Management, Service Catalog, Change Management with CAB scheduling, Virtual Agent chatbots with NLU, Performance Analytics KPIs, Flow Designer automation, A

268 lines 9.14 kB
"use strict"; /** * Reliable Memory Manager * Direct in-memory storage without database dependencies * Solves hanging issues with better-sqlite3 */ 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.reliableMemory = exports.ReliableMemoryManager = void 0; const fs = __importStar(require("fs")); const path = __importStar(require("path")); const logger_js_1 = require("../../utils/logger.js"); class ReliableMemoryManager { constructor() { this.memory = new Map(); this.MAX_MEMORY_MB = 100; // 100MB max memory usage this.persistTimer = null; this.logger = new logger_js_1.Logger('ReliableMemoryManager'); // Persistence file for recovery const memoryDir = path.join(process.cwd(), '.snow-flow', 'memory'); if (!fs.existsSync(memoryDir)) { fs.mkdirSync(memoryDir, { recursive: true }); } this.PERSIST_FILE = path.join(memoryDir, 'memory-snapshot.json'); // Load previous memory if exists this.loadFromDisk(); // Auto-persist every 30 seconds this.startAutoPersist(); } static getInstance() { if (!ReliableMemoryManager.instance) { ReliableMemoryManager.instance = new ReliableMemoryManager(); } return ReliableMemoryManager.instance; } /** * Store value in memory with size checking */ async store(key, value, expiresInMs) { const serialized = JSON.stringify(value); const sizeBytes = Buffer.byteLength(serialized); // Check total memory usage const currentUsage = this.getMemoryUsageBytes(); const newUsage = currentUsage + sizeBytes; const maxBytes = this.MAX_MEMORY_MB * 1024 * 1024; if (newUsage > maxBytes) { // Try to free expired entries first this.cleanupExpired(); // Check again const afterCleanup = this.getMemoryUsageBytes() + sizeBytes; if (afterCleanup > maxBytes) { throw new Error(`Memory limit exceeded. Current: ${(currentUsage / 1024 / 1024).toFixed(2)}MB, ` + `Requested: ${(sizeBytes / 1024 / 1024).toFixed(2)}MB, ` + `Max: ${this.MAX_MEMORY_MB}MB`); } } const entry = { key, value, timestamp: new Date(), sizeBytes, expiresAt: expiresInMs ? new Date(Date.now() + expiresInMs) : undefined }; this.memory.set(key, entry); this.logger.debug(`Stored key '${key}' (${(sizeBytes / 1024).toFixed(2)}KB)`); } /** * Retrieve value from memory */ async retrieve(key) { const entry = this.memory.get(key); if (!entry) { return null; } // Check if expired if (entry.expiresAt && entry.expiresAt < new Date()) { this.memory.delete(key); this.logger.debug(`Key '${key}' expired and removed`); return null; } return entry.value; } /** * Delete a key from memory */ async delete(key) { const existed = this.memory.has(key); this.memory.delete(key); return existed; } /** * List all keys with optional pattern matching */ async list(pattern) { this.cleanupExpired(); const keys = Array.from(this.memory.keys()); if (pattern) { const regex = new RegExp(pattern); return keys.filter(key => regex.test(key)); } return keys; } /** * Clear all memory */ async clear() { const count = this.memory.size; this.memory.clear(); this.logger.info(`Cleared ${count} entries from memory`); } /** * Get memory usage statistics */ getStats() { const totalSizeBytes = this.getMemoryUsageBytes(); const totalSizeMB = totalSizeBytes / 1024 / 1024; return { entries: this.memory.size, totalSizeBytes, totalSizeMB, maxSizeMB: this.MAX_MEMORY_MB, utilizationPercent: (totalSizeMB / this.MAX_MEMORY_MB) * 100 }; } /** * Get total memory usage in bytes */ getMemoryUsageBytes() { let total = 0; for (const entry of this.memory.values()) { total += entry.sizeBytes; } return total; } /** * Clean up expired entries */ cleanupExpired() { const now = new Date(); let removed = 0; for (const [key, entry] of this.memory.entries()) { if (entry.expiresAt && entry.expiresAt < now) { this.memory.delete(key); removed++; } } if (removed > 0) { this.logger.debug(`Cleaned up ${removed} expired entries`); } } /** * Persist memory to disk for recovery */ async persistToDisk() { try { const data = { version: '1.0', timestamp: new Date().toISOString(), entries: Array.from(this.memory.entries()).map(([key, entry]) => ({ key, value: entry.value, timestamp: entry.timestamp, expiresAt: entry.expiresAt, sizeBytes: entry.sizeBytes })) }; await fs.promises.writeFile(this.PERSIST_FILE, JSON.stringify(data, null, 2), 'utf-8'); this.logger.debug(`Persisted ${this.memory.size} entries to disk`); } catch (error) { this.logger.error('Failed to persist memory to disk:', error); } } /** * Load memory from disk */ loadFromDisk() { try { if (!fs.existsSync(this.PERSIST_FILE)) { return; } const data = JSON.parse(fs.readFileSync(this.PERSIST_FILE, 'utf-8')); if (data.version !== '1.0') { this.logger.warn('Incompatible memory snapshot version, skipping load'); return; } for (const entry of data.entries) { this.memory.set(entry.key, { key: entry.key, value: entry.value, timestamp: new Date(entry.timestamp), expiresAt: entry.expiresAt ? new Date(entry.expiresAt) : undefined, sizeBytes: entry.sizeBytes }); } this.cleanupExpired(); this.logger.info(`Loaded ${this.memory.size} entries from disk`); } catch (error) { this.logger.error('Failed to load memory from disk:', error); } } /** * Start auto-persist timer */ startAutoPersist() { // Persist every 30 seconds this.persistTimer = setInterval(() => { this.persistToDisk().catch(error => { this.logger.error('Auto-persist failed:', error); }); }, 30000); // Don't block process exit if (this.persistTimer.unref) { this.persistTimer.unref(); } // Persist on exit process.on('beforeExit', () => { this.persistToDisk(); }); } /** * Stop auto-persist timer */ destroy() { if (this.persistTimer) { clearInterval(this.persistTimer); this.persistTimer = null; } this.persistToDisk(); } } exports.ReliableMemoryManager = ReliableMemoryManager; // Export singleton instance exports.reliableMemory = ReliableMemoryManager.getInstance(); //# sourceMappingURL=reliable-memory-manager.js.map