UNPKG

claude-flow-tbowman01

Version:

Enterprise-grade AI agent orchestration with ruv-swarm integration (Alpha Release)

254 lines 9.35 kB
/** * Resource manager for preventing conflicts and deadlocks */ import { SystemEvents } from '../utils/types.js'; import { ResourceLockError } from '../utils/errors.js'; import { delay } from '../utils/helpers.js'; /** * Resource manager implementation */ export class ResourceManager { config; eventBus; logger; resources = new Map(); locks = new Map(); // resourceId -> agentId waitQueue = new Map(); // resourceId -> queue agentResources = new Map(); // agentId -> resourceIds constructor(config, eventBus, logger) { this.config = config; this.eventBus = eventBus; this.logger = logger; } async initialize() { this.logger.info('Initializing resource manager'); // Set up periodic cleanup setInterval(() => this.cleanup(), 30000); // Every 30 seconds } async shutdown() { this.logger.info('Shutting down resource manager'); // Release all locks for (const [resourceId, agentId] of this.locks) { await this.release(resourceId, agentId); } this.resources.clear(); this.locks.clear(); this.waitQueue.clear(); this.agentResources.clear(); } async acquire(resourceId, agentId, priority = 0) { this.logger.debug('Resource acquisition requested', { resourceId, agentId }); // Check if resource exists if (!this.resources.has(resourceId)) { this.resources.set(resourceId, { id: resourceId, type: 'generic', locked: false, }); } const resource = this.resources.get(resourceId); // Check if already locked by this agent if (this.locks.get(resourceId) === agentId) { this.logger.debug('Resource already locked by agent', { resourceId, agentId }); return; } // Try to acquire lock if (!resource.locked) { await this.lockResource(resourceId, agentId); return; } // Add to wait queue const request = { agentId, resourceId, timestamp: new Date(), priority, }; if (!this.waitQueue.has(resourceId)) { this.waitQueue.set(resourceId, []); } const queue = this.waitQueue.get(resourceId); queue.push(request); // Sort by priority and timestamp queue.sort((a, b) => { if (a.priority !== b.priority) { return b.priority - a.priority; // Higher priority first } return a.timestamp.getTime() - b.timestamp.getTime(); // Earlier first }); this.logger.info('Agent added to resource wait queue', { resourceId, agentId, queueLength: queue.length, }); // Wait for resource with timeout const startTime = Date.now(); while (Date.now() - startTime < this.config.resourceTimeout) { // Check if we're next in queue and resource is available const nextRequest = queue[0]; if (nextRequest?.agentId === agentId && !resource.locked) { // Remove from queue and acquire queue.shift(); await this.lockResource(resourceId, agentId); return; } // Check if our request is still in queue const ourRequest = queue.find((req) => req.agentId === agentId); if (!ourRequest) { // Request was removed (possibly by cleanup) throw new ResourceLockError('Resource request cancelled'); } await delay(100); } // Timeout - remove from queue const index = queue.findIndex((req) => req.agentId === agentId); if (index !== -1) { queue.splice(index, 1); } throw new ResourceLockError(`Resource acquisition timeout for ${resourceId}`, { resourceId, agentId, timeout: this.config.resourceTimeout, }); } async release(resourceId, agentId) { this.logger.debug('Resource release requested', { resourceId, agentId }); const currentLock = this.locks.get(resourceId); if (currentLock !== agentId) { this.logger.warn('Attempted to release unowned resource', { resourceId, agentId, currentLock, }); return; } // Release the lock this.unlockResource(resourceId, agentId); // Process wait queue const queue = this.waitQueue.get(resourceId); if (queue && queue.length > 0) { const nextRequest = queue.shift(); // Grant lock to next in queue await this.lockResource(resourceId, nextRequest.agentId); } } async releaseAllForAgent(agentId) { const resources = this.agentResources.get(agentId); if (!resources) { return; } this.logger.info('Releasing all resources for agent', { agentId, resourceCount: resources.size, }); const promises = Array.from(resources).map((resourceId) => this.release(resourceId, agentId)); await Promise.all(promises); this.agentResources.delete(agentId); } getAllocations() { return new Map(this.locks); } getWaitingRequests() { const waiting = new Map(); for (const [resourceId, queue] of this.waitQueue) { if (queue.length > 0) { waiting.set(queue[0].agentId, [...(waiting.get(queue[0].agentId) || []), resourceId]); } } return waiting; } async getHealthStatus() { const totalResources = this.resources.size; const lockedResources = this.locks.size; const waitingAgents = new Set(); let totalWaiting = 0; for (const queue of this.waitQueue.values()) { totalWaiting += queue.length; queue.forEach((req) => waitingAgents.add(req.agentId)); } return { healthy: true, metrics: { totalResources, lockedResources, freeResources: totalResources - lockedResources, waitingAgents: waitingAgents.size, totalWaitingRequests: totalWaiting, }, }; } async lockResource(resourceId, agentId) { const resource = this.resources.get(resourceId); resource.locked = true; resource.lockedBy = agentId; resource.lockedAt = new Date(); this.locks.set(resourceId, agentId); // Track agent resources if (!this.agentResources.has(agentId)) { this.agentResources.set(agentId, new Set()); } this.agentResources.get(agentId).add(resourceId); this.logger.info('Resource locked', { resourceId, agentId }); // Emit event this.eventBus.emit(SystemEvents.RESOURCE_ACQUIRED, { resourceId, agentId }); } unlockResource(resourceId, agentId) { const resource = this.resources.get(resourceId); if (!resource) { return; } resource.locked = false; delete resource.lockedBy; delete resource.lockedAt; this.locks.delete(resourceId); // Remove from agent resources this.agentResources.get(agentId)?.delete(resourceId); this.logger.info('Resource unlocked', { resourceId, agentId }); // Emit event this.eventBus.emit(SystemEvents.RESOURCE_RELEASED, { resourceId, agentId }); } async performMaintenance() { this.logger.debug('Performing resource manager maintenance'); this.cleanup(); } cleanup() { const now = Date.now(); // Clean up stale wait requests for (const [resourceId, queue] of this.waitQueue) { const filtered = queue.filter((req) => { const age = now - req.timestamp.getTime(); if (age > this.config.resourceTimeout) { this.logger.warn('Removing stale resource request', { resourceId, agentId: req.agentId, age, }); return false; } return true; }); if (filtered.length === 0) { this.waitQueue.delete(resourceId); } else { this.waitQueue.set(resourceId, filtered); } } // Clean up locks held too long for (const [resourceId, agentId] of this.locks) { const resource = this.resources.get(resourceId); if (resource?.lockedAt) { const lockAge = now - resource.lockedAt.getTime(); if (lockAge > this.config.resourceTimeout * 2) { this.logger.warn('Force releasing stale lock', { resourceId, agentId, lockAge, }); this.unlockResource(resourceId, agentId); } } } } } //# sourceMappingURL=resources.js.map