Skip to main content
Glama
service-pool.ts10.1 kB
import { EventEmitter } from 'events'; // import { measurePerformance } from "./performance-monitor.js"; /** * Object pool for reusing expensive objects and reducing garbage collection */ export class ObjectPool<T> { private available: T[] = []; private inUse = new Set<T>(); private readonly maxSize: number; private readonly factory: () => T; private readonly reset?: (item: T) => void; private readonly validate?: (item: T) => boolean; constructor( factory: () => T, maxSize = 10, reset?: (item: T) => void, validate?: (item: T) => boolean, ) { this.factory = factory; this.maxSize = maxSize; this.reset = reset; this.validate = validate; // Pre-populate pool for (let i = 0; i < Math.min(3, maxSize); i++) { this.available.push(this.factory()); } } acquire(): T { let item: T; // Try to reuse existing item if (this.available.length > 0) { item = this.available.pop()!; // Validate item if validator provided if (this.validate && !this.validate(item)) { // Item is invalid, create new one item = this.factory(); } } else { // Create new item item = this.factory(); } this.inUse.add(item); return item; } release(item: T): void { if (!this.inUse.has(item)) { return; // Item not from this pool } this.inUse.delete(item); // Reset item if reset function provided if (this.reset) { this.reset(item); } // Return to pool if under max size if (this.available.length < this.maxSize) { this.available.push(item); } // Otherwise let it be garbage collected } getStats(): { available: number; inUse: number; total: number } { return { available: this.available.length, inUse: this.inUse.size, total: this.available.length + this.inUse.size, }; } clear(): void { this.available.length = 0; this.inUse.clear(); } } /** * Optimized service container with resource management */ export class ServiceContainer extends EventEmitter { private services = new Map<string, any>(); private factories = new Map<string, () => any>(); private singletons = new Map<string, any>(); private pools = new Map<string, ObjectPool<any>>(); // Resource tracking private resourceUsage = new Map<string, { created: number; lastAccessed: number; accessCount: number; memoryEstimate: number; }>(); /** * Register a singleton service */ registerSingleton<T>(name: string, factory: () => T): void { this.factories.set(name, factory); this.emit('service-registered', { name, type: 'singleton' }); } /** * Register a transient service (new instance each time) */ registerTransient<T>(name: string, factory: () => T): void { this.services.set(name, factory); this.emit('service-registered', { name, type: 'transient' }); } /** * Register a pooled service for expensive objects */ registerPooled<T>( name: string, factory: () => T, maxPoolSize = 10, reset?: (item: T) => void, validate?: (item: T) => boolean, ): void { const pool = new ObjectPool(factory, maxPoolSize, reset, validate); this.pools.set(name, pool); this.emit('service-registered', { name, type: 'pooled' }); } /** * Get service instance */ get<T>(name: string): T { this.updateResourceUsage(name); // Check for pooled service first if (this.pools.has(name)) { return this.pools.get(name)!.acquire(); } // Check for singleton if (this.factories.has(name)) { if (!this.singletons.has(name)) { const instance = this.factories.get(name)!(); this.singletons.set(name, instance); } return this.singletons.get(name)!; } // Check for transient if (this.services.has(name)) { return this.services.get(name)!(); } throw new Error(`Service '${name}' not found`); } /** * Release pooled service back to pool */ release<T>(name: string, instance: T): void { if (this.pools.has(name)) { this.pools.get(name)!.release(instance); } } /** * Check if service exists */ has(name: string): boolean { return this.factories.has(name) || this.services.has(name) || this.pools.has(name); } private updateResourceUsage(name: string): void { const now = Date.now(); const usage = this.resourceUsage.get(name) || { created: now, lastAccessed: now, accessCount: 0, memoryEstimate: 0, }; usage.lastAccessed = now; usage.accessCount++; this.resourceUsage.set(name, usage); } /** * Get service usage statistics */ getResourceStats(): Map<string, ReturnType<ServiceContainer['resourceUsage']['get']>> { return new Map(this.resourceUsage); } /** * Get pool statistics */ getPoolStats(): Map<string, ReturnType<ObjectPool<any>['getStats']>> { const stats = new Map(); for (const [name, pool] of this.pools.entries()) { stats.set(name, pool.getStats()); } return stats; } /** * Optimize resource usage - cleanup unused services */ optimize(): void { const now = Date.now(); const maxIdleTime = 10 * 60 * 1000; // 10 minutes // Clear unused singletons for (const [name, usage] of this.resourceUsage.entries()) { if (now - usage.lastAccessed > maxIdleTime && usage.accessCount < 5) { this.singletons.delete(name); this.resourceUsage.delete(name); } } // Clear pools for (const pool of this.pools.values()) { const stats = pool.getStats(); if (stats.inUse === 0) { pool.clear(); } } this.emit('optimized', { timestamp: now, clearedSingletons: this.singletons.size, poolStats: Object.fromEntries(this.getPoolStats()), }); } /** * Cleanup all resources */ cleanup(): void { this.singletons.clear(); this.resourceUsage.clear(); for (const pool of this.pools.values()) { pool.clear(); } this.emit('cleanup'); } } /** * Memory-efficient string interning for reducing duplicate strings */ export class StringInterner { private static instance: StringInterner; private internMap = new Map<string, string>(); private readonly maxSize = 10000; static getInstance(): StringInterner { if (!StringInterner.instance) { StringInterner.instance = new StringInterner(); } return StringInterner.instance; } /** * Intern a string to reuse identical strings and save memory */ intern(str: string): string { if (!str || typeof str !== 'string') { return str; } if (this.internMap.has(str)) { return this.internMap.get(str)!; } // Prevent unbounded growth if (this.internMap.size >= this.maxSize) { this.evictOldEntries(); } this.internMap.set(str, str); return str; } private evictOldEntries(): void { // Remove 10% of entries (simple FIFO) const toRemove = Math.floor(this.maxSize * 0.1); const keys = Array.from(this.internMap.keys()); for (let i = 0; i < toRemove && keys.length > 0; i++) { this.internMap.delete(keys[i]); } } getStats(): { size: number; maxSize: number } { return { size: this.internMap.size, maxSize: this.maxSize, }; } clear(): void { this.internMap.clear(); } } /** * Async operation manager with batching and throttling */ export class AsyncOperationManager { private operationQueues = new Map<string, Array<() => Promise<any>>>(); private activeOperations = new Map<string, number>(); private maxConcurrent: number; private batchSize: number; constructor(maxConcurrent = 10, batchSize = 5) { this.maxConcurrent = maxConcurrent; this.batchSize = batchSize; } /** * Add operation to queue with automatic batching */ async enqueue<T>(category: string, operation: () => Promise<T>): Promise<T> { return new Promise((resolve, reject) => { if (!this.operationQueues.has(category)) { this.operationQueues.set(category, []); this.activeOperations.set(category, 0); } const queue = this.operationQueues.get(category)!; queue.push(async () => { try { const result = await operation(); resolve(result); return result; } catch (error) { reject(error); throw error; } }); this.processQueue(category); }); } private async processQueue(category: string): Promise<void> { const queue = this.operationQueues.get(category); const active = this.activeOperations.get(category) || 0; if (!queue || queue.length === 0 || active >= this.maxConcurrent) { return; } const batch = queue.splice(0, Math.min(this.batchSize, this.maxConcurrent - active)); this.activeOperations.set(category, active + batch.length); // Execute batch in parallel const batchPromises = batch.map(async (operation) => { try { return await operation(); } catch (error) { // Error is already handled in the operation wrapper return null; } finally { const currentActive = this.activeOperations.get(category) || 0; this.activeOperations.set(category, Math.max(0, currentActive - 1)); // Process next batch if queue has items setImmediate(() => this.processQueue(category)); } }); await Promise.allSettled(batchPromises); } /** * Get queue statistics */ getStats(): Map<string, { queued: number; active: number }> { const stats = new Map(); for (const [category, queue] of this.operationQueues.entries()) { stats.set(category, { queued: queue.length, active: this.activeOperations.get(category) || 0, }); } return stats; } /** * Clear all queues */ clear(): void { this.operationQueues.clear(); this.activeOperations.clear(); } }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/quanticsoul4772/grafana-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server