@sethdouglasford/claude-flow
Version:
Claude Code Flow - Advanced AI-powered development workflows with SPARC methodology
1,266 lines • 50 kB
JavaScript
/**
* Comprehensive resource management system for swarm operations
*/
import { EventEmitter } from "node:events";
import { generateId } from "../utils/helpers.js";
/**
* Comprehensive resource management with allocation, monitoring, and optimization
*/
export class ResourceManager extends EventEmitter {
logger;
eventBus;
config;
// Resource tracking
resources = new Map();
pools = new Map();
reservations = new Map();
allocations = new Map();
// Monitoring and optimization
usageHistory = new Map();
predictions = new Map();
optimizer;
// Scheduling and cleanup
monitoringInterval;
cleanupInterval;
scalingInterval;
// Performance tracking
metrics;
constructor(config, logger, eventBus) {
super();
this.logger = logger;
this.eventBus = eventBus;
this.config = {
enableResourcePooling: true,
enableResourceMonitoring: true,
enableAutoScaling: true,
enableQoS: true,
monitoringInterval: 30000,
cleanupInterval: 300000,
defaultLimits: {
cpu: 4.0,
memory: 8 * 1024 * 1024 * 1024, // 8GB
disk: 100 * 1024 * 1024 * 1024, // 100GB
network: 1024 * 1024 * 1024, // 1Gbps
custom: {},
},
reservationTimeout: 300000, // 5 minutes
allocationStrategy: "best-fit",
priorityWeights: {
critical: 1.0,
high: 0.8,
normal: 0.6,
low: 0.4,
background: 0.2,
},
enablePredictiveAllocation: true,
enableResourceSharing: true,
debugMode: false,
...config,
};
this.optimizer = new ResourceOptimizer(this.config, this.logger);
this.metrics = new ResourceManagerMetrics();
this.setupEventHandlers();
}
setupEventHandlers() {
this.eventBus.on("agent:resource-request", (data) => {
this.handleResourceRequest(data);
});
this.eventBus.on("agent:resource-release", (data) => {
this.handleResourceRelease(data);
});
this.eventBus.on("resource.usage.updated", (data) => {
const resourceData = data;
// Convert number to ResourceUsage interface if needed
const resourceUsage = typeof resourceData.usage === "number"
? {
cpu: resourceData.usage,
memory: 0,
disk: 0,
network: 0,
custom: {},
timestamp: new Date(),
duration: 0,
}
: resourceData.usage;
this.updateResourceUsage(resourceData.resourceId, resourceUsage);
});
this.eventBus.on("resource:failure", (data) => {
this.handleResourceFailure(data);
});
this.eventBus.on("scaling:trigger", (data) => {
this.handleScalingTrigger(data);
});
this.eventBus.on("agent.status.changed", (data) => {
const agentData = data;
this.handleAgentStatusChange(agentData.agentId, agentData.from, agentData.to);
});
this.eventBus.on("task.resource.request", (data) => {
const taskData = data;
this.allocateResourcesForTask(taskData.taskId, taskData.requirements);
});
this.eventBus.on("task.resource.release", (data) => {
const taskData = data;
this.releaseResourcesForTask(taskData.taskId);
});
this.eventBus.on("resource.alert", (data) => {
const alertData = data;
this.handleResourceAlert(alertData.type, alertData.threshold, alertData.current);
});
}
async initialize() {
this.logger.info("Initializing resource manager", {
pooling: this.config.enableResourcePooling,
monitoring: this.config.enableResourceMonitoring,
autoScaling: this.config.enableAutoScaling,
});
// Initialize optimizer
await this.optimizer.initialize();
// Create default resource pools
await this.createDefaultPools();
// Start monitoring
if (this.config.enableResourceMonitoring) {
this.startMonitoring();
}
// Start cleanup
this.startCleanup();
// Start auto-scaling
if (this.config.enableAutoScaling) {
this.startAutoScaling();
}
this.emit("resource-manager:initialized");
}
async shutdown() {
this.logger.info("Shutting down resource manager");
// Stop intervals
if (this.monitoringInterval)
clearInterval(this.monitoringInterval);
if (this.cleanupInterval)
clearInterval(this.cleanupInterval);
if (this.scalingInterval)
clearInterval(this.scalingInterval);
// Release all active allocations
await this.releaseAllAllocations();
// Shutdown optimizer
await this.optimizer.shutdown();
this.emit("resource-manager:shutdown");
}
// === RESOURCE MANAGEMENT ===
async registerResource(type, name, capacity, metadata = {}) {
const resourceId = generateId("resource");
const resource = {
id: resourceId,
type,
name,
description: `${type} resource: ${name}`,
capacity,
allocated: this.createEmptyLimits(),
available: { ...capacity },
status: "available",
metadata: {
provider: "local",
capabilities: [],
performance: this.createDefaultPerformanceMetrics(),
reliability: this.createDefaultReliabilityMetrics(),
cost: this.createDefaultCostMetrics(),
lastUpdated: new Date(),
...metadata,
},
reservations: [],
allocations: [],
sharable: this.config.enableResourceSharing,
persistent: true,
cost: 1.0,
tags: [],
};
this.resources.set(resourceId, resource);
this.logger.info("Resource registered", {
resourceId,
type,
name,
capacity,
});
this.emit("resource:registered", { resource });
return resourceId;
}
async unregisterResource(resourceId) {
const resource = this.resources.get(resourceId);
if (!resource) {
throw new Error(`Resource ${resourceId} not found`);
}
// Check for active allocations
if (resource.allocations.length > 0) {
throw new Error(`Cannot unregister resource ${resourceId}: has active allocations`);
}
// Cancel pending reservations
for (const reservation of resource.reservations) {
await this.cancelReservation(reservation.id, "resource_unregistered");
}
this.resources.delete(resourceId);
this.logger.info("Resource unregistered", { resourceId });
this.emit("resource:unregistered", { resourceId });
}
// === RESOURCE ALLOCATION ===
async requestResources(agentId, requirements, options = {}) {
const reservationId = generateId("reservation");
const now = new Date();
const reservation = {
id: reservationId,
resourceId: "", // Will be set when resource is found
agentId,
taskId: options.taskId,
requirements,
status: "pending",
priority: options.priority || "normal",
createdAt: now,
expiresAt: options.timeout ? new Date(now.getTime() + options.timeout) :
new Date(now.getTime() + this.config.reservationTimeout),
metadata: {
preemptible: options.preemptible || false,
},
};
this.reservations.set(reservationId, reservation);
try {
// Find suitable resource
const resource = await this.findSuitableResource(requirements, reservation.priority);
if (!resource) {
reservation.status = "failed";
throw new Error("No suitable resource available");
}
// Reserve resource
reservation.resourceId = resource.id;
resource.reservations.push(reservation);
// Update availability
this.updateResourceAvailability(resource);
reservation.status = "confirmed";
this.logger.info("Resource reservation created", {
reservationId,
resourceId: resource.id,
agentId: agentId.id,
requirements,
});
this.emit("reservation:created", { reservation });
// Auto-activate if possible
if (this.canActivateReservation(reservation)) {
await this.activateReservation(reservationId);
}
return reservationId;
}
catch (error) {
reservation.status = "failed";
this.logger.error("Resource reservation failed", {
reservationId,
agentId: agentId.id,
error,
});
throw error;
}
}
async activateReservation(reservationId) {
const reservation = this.reservations.get(reservationId);
if (!reservation) {
throw new Error(`Reservation ${reservationId} not found`);
}
if (reservation.status !== "confirmed") {
throw new Error(`Reservation ${reservationId} is not confirmed`);
}
const resource = this.resources.get(reservation.resourceId);
if (!resource) {
throw new Error(`Resource ${reservation.resourceId} not found`);
}
// Create allocation
const allocationId = generateId("allocation");
const allocation = {
id: allocationId,
reservationId,
resourceId: resource.id,
agentId: reservation.agentId,
taskId: reservation.taskId,
allocated: this.calculateAllocation(reservation.requirements, resource),
actualUsage: this.createEmptyUsage(),
efficiency: 1.0,
startTime: new Date(),
status: "active",
qosViolations: [],
};
this.allocations.set(allocationId, allocation);
resource.allocations.push(allocation);
// Update resource allocated amounts
this.addToResourceLimits(resource.allocated, allocation.allocated);
this.updateResourceAvailability(resource);
// Update reservation status
reservation.status = "active";
reservation.activatedAt = new Date();
this.logger.info("Resource allocation activated", {
allocationId,
reservationId,
resourceId: resource.id,
agentId: reservation.agentId.id,
allocated: allocation.allocated,
});
this.emit("allocation:activated", { allocation });
return allocationId;
}
async releaseResources(allocationId, reason = "completed") {
const allocation = this.allocations.get(allocationId);
if (!allocation) {
throw new Error(`Allocation ${allocationId} not found`);
}
const resource = this.resources.get(allocation.resourceId);
if (!resource) {
throw new Error(`Resource ${allocation.resourceId} not found`);
}
// Update allocation status
allocation.status = "completed";
allocation.endTime = new Date();
// Calculate final efficiency
allocation.efficiency = this.calculateEfficiency(allocation);
// Remove from resource allocated amounts
this.subtractFromResourceLimits(resource.allocated, allocation.allocated);
// Remove allocation from resource
resource.allocations = resource.allocations.filter(a => a.id !== allocationId);
// Update resource availability
this.updateResourceAvailability(resource);
// Update reservation if exists
const reservation = this.reservations.get(allocation.reservationId);
if (reservation) {
reservation.releasedAt = new Date();
}
this.logger.info("Resource allocation released", {
allocationId,
resourceId: resource.id,
agentId: allocation.agentId.id,
reason,
efficiency: allocation.efficiency,
});
this.emit("allocation:released", { allocation, reason });
// Update metrics
this.metrics.recordAllocationReleased(allocation);
}
async cancelReservation(reservationId, reason = "cancelled") {
const reservation = this.reservations.get(reservationId);
if (!reservation) {
throw new Error(`Reservation ${reservationId} not found`);
}
// If reservation is active, release the allocation first
if (reservation.status === "active") {
const allocation = Array.from(this.allocations.values())
.find(a => a.reservationId === reservationId);
if (allocation) {
await this.releaseResources(allocation.id, reason);
}
}
// Update reservation status
reservation.status = "cancelled";
// Remove from resource if it was reserved
if (reservation.resourceId) {
const resource = this.resources.get(reservation.resourceId);
if (resource) {
resource.reservations = resource.reservations.filter(r => r.id !== reservationId);
this.updateResourceAvailability(resource);
}
}
this.logger.info("Resource reservation cancelled", {
reservationId,
reason,
});
this.emit("reservation:cancelled", { reservation, reason });
}
// === RESOURCE POOLS ===
async createResourcePool(name, type, resourceIds, strategy = "least-loaded") {
const poolId = generateId("pool");
// Validate resources exist and are of correct type
for (const resourceId of resourceIds) {
const resource = this.resources.get(resourceId);
if (!resource) {
throw new Error(`Resource ${resourceId} not found`);
}
if (resource.type !== type) {
throw new Error(`Resource ${resourceId} type mismatch: expected ${type}, got ${resource.type}`);
}
}
const pool = {
id: poolId,
name,
type,
resources: [...resourceIds],
strategy,
loadBalancing: "least-connections",
scaling: {
enabled: this.config.enableAutoScaling,
minResources: Math.max(1, resourceIds.length),
maxResources: resourceIds.length * 3,
scaleUpThreshold: 0.8,
scaleDownThreshold: 0.3,
cooldownPeriod: 300000,
metrics: [
{ name: "utilization", weight: 1.0, threshold: 0.8, aggregation: "avg" },
{ name: "queue_depth", weight: 0.5, threshold: 10, aggregation: "max" },
],
},
qos: {
guarantees: [],
objectives: [],
violations: {
autoRemediation: true,
escalationThreshold: 3,
penaltyFunction: "linear",
notificationEnabled: true,
},
},
statistics: this.createPoolStatistics(),
filters: [],
};
this.pools.set(poolId, pool);
this.logger.info("Resource pool created", {
poolId,
name,
type,
resourceCount: resourceIds.length,
});
this.emit("pool:created", { pool });
return poolId;
}
async addResourceToPool(poolId, resourceId) {
const pool = this.pools.get(poolId);
if (!pool) {
throw new Error(`Pool ${poolId} not found`);
}
const resource = this.resources.get(resourceId);
if (!resource) {
throw new Error(`Resource ${resourceId} not found`);
}
if (resource.type !== pool.type) {
throw new Error(`Resource type mismatch: pool expects ${pool.type}, resource is ${resource.type}`);
}
if (!pool.resources.includes(resourceId)) {
pool.resources.push(resourceId);
this.updatePoolStatistics(pool);
}
this.logger.info("Resource added to pool", { poolId, resourceId });
this.emit("pool:resource-added", { poolId, resourceId });
}
async removeResourceFromPool(poolId, resourceId) {
const pool = this.pools.get(poolId);
if (!pool) {
throw new Error(`Pool ${poolId} not found`);
}
if (pool.resources.length <= pool.scaling.minResources) {
throw new Error("Cannot remove resource: pool would go below minimum size");
}
pool.resources = pool.resources.filter(id => id !== resourceId);
this.updatePoolStatistics(pool);
this.logger.info("Resource removed from pool", { poolId, resourceId });
this.emit("pool:resource-removed", { poolId, resourceId });
}
// === RESOURCE DISCOVERY AND ALLOCATION ===
async findSuitableResource(requirements, priority) {
const candidates = [];
for (const resource of this.resources.values()) {
if (resource.status !== "available")
continue;
const score = this.calculateResourceScore(resource, requirements, priority);
if (score > 0) {
candidates.push({ resource, score });
}
}
if (candidates.length === 0) {
return null;
}
// Sort by score (highest first)
candidates.sort((a, b) => b.score - a.score);
// Apply allocation strategy
return this.selectResourceByStrategy(candidates, requirements);
}
calculateResourceScore(resource, requirements, priority) {
let score = 0;
// Check if resource can satisfy requirements
if (!this.canSatisfyRequirements(resource, requirements)) {
return 0;
}
// Base score from resource utilization (prefer less utilized)
const utilization = this.calculateResourceUtilization(resource);
score += (1 - utilization) * 100;
// Performance score
score += resource.metadata.performance.cpuScore * 10;
// Reliability score
score += resource.metadata.reliability.uptime * 50;
// Cost efficiency (lower cost is better)
score += (1 / resource.cost) * 20;
// Priority adjustment
const priorityWeight = this.config.priorityWeights[priority] || 1.0;
score *= priorityWeight;
return score;
}
canSatisfyRequirements(resource, requirements) {
// Check CPU
if (requirements.cpu && requirements.cpu.min > resource.available.cpu) {
return false;
}
// Check memory
if (requirements.memory && requirements.memory.min > resource.available.memory) {
return false;
}
// Check disk
if (requirements.disk && requirements.disk.min > resource.available.disk) {
return false;
}
// Check network
if (requirements.network && requirements.network.min > resource.available.network) {
return false;
}
// Check custom resources
if (requirements.custom) {
for (const [name, spec] of Object.entries(requirements.custom)) {
const available = resource.available.custom[name] || 0;
if (spec.min > available) {
return false;
}
}
}
// Check constraints
if (requirements.constraints) {
if (!this.checkConstraints(resource, requirements.constraints)) {
return false;
}
}
return true;
}
checkConstraints(resource, constraints) {
// Location constraints
if (constraints.location && constraints.location.length > 0) {
if (!constraints.location.includes(resource.location ?? "")) {
return false;
}
}
if (constraints.excludeLocation && constraints.excludeLocation.length > 0) {
if (constraints.excludeLocation.includes(resource.location ?? "")) {
return false;
}
}
// Cost constraints
if (constraints.maxCost && resource.cost > constraints.maxCost) {
return false;
}
// Time window constraints
if (constraints.timeWindow) {
const now = new Date();
if (now < constraints.timeWindow.start || now > constraints.timeWindow.end) {
return false;
}
}
return true;
}
selectResourceByStrategy(candidates, requirements) {
switch (this.config.allocationStrategy) {
case "first-fit":
return candidates[0].resource;
case "best-fit":
// Find resource with smallest waste
return candidates.reduce((best, current) => {
const bestWaste = this.calculateWaste(best.resource, requirements);
const currentWaste = this.calculateWaste(current.resource, requirements);
return currentWaste < bestWaste ? current : best;
}).resource;
case "worst-fit":
// Find resource with largest waste (for fragmentation avoidance)
return candidates.reduce((worst, current) => {
const worstWaste = this.calculateWaste(worst.resource, requirements);
const currentWaste = this.calculateWaste(current.resource, requirements);
return currentWaste > worstWaste ? current : worst;
}).resource;
case "balanced":
default:
// Use highest score (balanced approach)
return candidates[0].resource;
}
}
calculateWaste(resource, requirements) {
let waste = 0;
if (requirements.cpu) {
waste += Math.max(0, resource.available.cpu - requirements.cpu.min);
}
if (requirements.memory) {
waste += Math.max(0, resource.available.memory - requirements.memory.min);
}
return waste;
}
calculateAllocation(requirements, resource) {
const allocation = {
cpu: 0,
memory: 0,
disk: 0,
network: 0,
custom: {},
};
if (requirements.cpu) {
allocation.cpu = Math.min(requirements.cpu.preferred || requirements.cpu.min, resource.available.cpu);
}
if (requirements.memory) {
allocation.memory = Math.min(requirements.memory.preferred || requirements.memory.min, resource.available.memory);
}
if (requirements.disk) {
allocation.disk = Math.min(requirements.disk.preferred || requirements.disk.min, resource.available.disk);
}
if (requirements.network) {
allocation.network = Math.min(requirements.network.preferred || requirements.network.min, resource.available.network);
}
if (requirements.custom) {
for (const [name, spec] of Object.entries(requirements.custom)) {
const available = resource.available.custom[name] || 0;
allocation.custom[name] = Math.min(spec.preferred || spec.min, available);
}
}
return allocation;
}
// === MONITORING AND OPTIMIZATION ===
startMonitoring() {
this.monitoringInterval = setInterval(() => {
this.performMonitoring();
}, this.config.monitoringInterval);
this.logger.info("Started resource monitoring", {
interval: this.config.monitoringInterval,
});
}
startCleanup() {
this.cleanupInterval = setInterval(() => {
this.performCleanup();
}, this.config.cleanupInterval);
this.logger.info("Started resource cleanup", {
interval: this.config.cleanupInterval,
});
}
startAutoScaling() {
this.scalingInterval = setInterval(() => {
this.evaluateScaling();
}, 60000); // Every minute
this.logger.info("Started auto-scaling");
}
async performMonitoring() {
try {
// Update resource statistics
for (const resource of this.resources.values()) {
await this.updateResourceStatistics(resource);
}
// Update pool statistics
for (const pool of this.pools.values()) {
this.updatePoolStatistics(pool);
}
// Check QoS violations
if (this.config.enableQoS) {
await this.checkQoSViolations();
}
// Predictive analysis
if (this.config.enablePredictiveAllocation) {
await this.updatePredictions();
}
// Emit monitoring update
this.emit("monitoring:updated", {
resources: this.resources.size,
pools: this.pools.size,
allocations: this.allocations.size,
});
}
catch (error) {
this.logger.error("Monitoring failed", error);
}
}
async performCleanup() {
const now = new Date();
// Clean up expired reservations
const expiredReservations = Array.from(this.reservations.values())
.filter(r => r.expiresAt && r.expiresAt < now && r.status === "pending");
for (const reservation of expiredReservations) {
await this.cancelReservation(reservation.id, "expired");
}
// Clean up old usage history
const cutoff = new Date(now.getTime() - 86400000); // 24 hours
for (const [resourceId, history] of this.usageHistory) {
this.usageHistory.set(resourceId, history.filter(usage => usage.timestamp > cutoff));
}
this.logger.debug("Cleanup completed", {
expiredReservations: expiredReservations.length,
});
}
async evaluateScaling() {
for (const pool of this.pools.values()) {
if (!pool.scaling.enabled)
continue;
const metrics = this.calculatePoolMetrics(pool);
const shouldScale = this.shouldScale(pool, metrics);
if (shouldScale.action === "scale-up") {
await this.scalePoolUp(pool);
}
else if (shouldScale.action === "scale-down") {
await this.scalePoolDown(pool);
}
}
}
// === UTILITY METHODS ===
canActivateReservation(reservation) {
const resource = this.resources.get(reservation.resourceId);
if (!resource)
return false;
return this.canSatisfyRequirements(resource, reservation.requirements);
}
calculateResourceUtilization(resource) {
let totalCapacity = 0;
let totalAllocated = 0;
// CPU utilization
totalCapacity += resource.capacity.cpu;
totalAllocated += resource.allocated.cpu;
// Memory utilization
totalCapacity += resource.capacity.memory / (1024 * 1024); // Convert to MB for comparison
totalAllocated += resource.allocated.memory / (1024 * 1024);
return totalCapacity > 0 ? totalAllocated / totalCapacity : 0;
}
calculateEfficiency(allocation) {
if (!allocation.endTime)
return 0;
const duration = allocation.endTime.getTime() - allocation.startTime.getTime();
if (duration <= 0)
return 0;
// Calculate efficiency based on actual usage vs allocated
let efficiencySum = 0;
let factors = 0;
if (allocation.allocated.cpu > 0) {
efficiencySum += allocation.actualUsage.cpu / allocation.allocated.cpu;
factors++;
}
if (allocation.allocated.memory > 0) {
efficiencySum += allocation.actualUsage.memory / allocation.allocated.memory;
factors++;
}
return factors > 0 ? efficiencySum / factors : 1.0;
}
updateResourceAvailability(resource) {
resource.available = {
cpu: Math.max(0, resource.capacity.cpu - resource.allocated.cpu),
memory: Math.max(0, resource.capacity.memory - resource.allocated.memory),
disk: Math.max(0, resource.capacity.disk - resource.allocated.disk),
network: Math.max(0, resource.capacity.network - resource.allocated.network),
custom: {},
};
// Update custom resources
for (const [name, capacity] of Object.entries(resource.capacity.custom)) {
const allocated = resource.allocated.custom[name] || 0;
resource.available.custom[name] = Math.max(0, capacity - allocated);
}
}
addToResourceLimits(target, source) {
target.cpu += source.cpu;
target.memory += source.memory;
target.disk += source.disk;
target.network += source.network;
for (const [name, value] of Object.entries(source.custom)) {
target.custom[name] = (target.custom[name] || 0) + value;
}
}
subtractFromResourceLimits(target, source) {
target.cpu = Math.max(0, target.cpu - source.cpu);
target.memory = Math.max(0, target.memory - source.memory);
target.disk = Math.max(0, target.disk - source.disk);
target.network = Math.max(0, target.network - source.network);
for (const [name, value] of Object.entries(source.custom)) {
target.custom[name] = Math.max(0, (target.custom[name] || 0) - value);
}
}
createEmptyLimits() {
return {
cpu: 0,
memory: 0,
disk: 0,
network: 0,
custom: {},
};
}
createEmptyUsage() {
return {
cpu: 0,
memory: 0,
disk: 0,
network: 0,
custom: {},
timestamp: new Date(),
duration: 0,
};
}
createDefaultPerformanceMetrics() {
return {
cpuScore: 1.0,
memoryBandwidth: 1000000000, // 1GB/s
diskIOPS: 1000,
networkBandwidth: 1000000000, // 1Gbps
benchmarkResults: {},
};
}
createDefaultReliabilityMetrics() {
return {
uptime: 0.99,
meanTimeBetweenFailures: 8760, // 1 year in hours
errorRate: 0.01,
failureHistory: [],
};
}
createDefaultCostMetrics() {
return {
hourlyRate: 1.0,
dataTransferCost: 0.1,
storageCost: 0.1,
billing: "hourly",
};
}
createPoolStatistics() {
return {
totalResources: 0,
availableResources: 0,
utilizationRate: 0,
allocationSuccessRate: 100,
averageWaitTime: 0,
throughput: 0,
efficiency: 1.0,
costPerHour: 0,
qosScore: 100,
};
}
async createDefaultPools() {
// Create default compute pool if we have compute resources
const computeResources = Array.from(this.resources.values())
.filter(r => r.type === "compute")
.map(r => r.id);
if (computeResources.length > 0) {
await this.createResourcePool("default-compute", "compute", computeResources);
}
}
updateResourceUsage(resourceId, usage) {
const resource = this.resources.get(resourceId);
if (!resource)
return;
// Store usage history
const history = this.usageHistory.get(resourceId) || [];
history.push(usage);
// Keep only last 1000 entries
if (history.length > 1000) {
history.shift();
}
this.usageHistory.set(resourceId, history);
// Update active allocations with actual usage
for (const allocation of resource.allocations) {
if (allocation.status === "active") {
allocation.actualUsage = usage;
allocation.efficiency = this.calculateEfficiency(allocation);
}
}
}
async updateResourceStatistics(resource) {
// Update utilization
const utilization = this.calculateResourceUtilization(resource);
// Update performance metrics based on usage history
const history = this.usageHistory.get(resource.id) || [];
if (history.length > 0) {
const recent = history.slice(-10); // Last 10 measurements
const avgCpu = recent.reduce((sum, h) => sum + h.cpu, 0) / recent.length;
// Update performance score based on load
resource.metadata.performance.cpuScore = Math.max(0.1, 1.0 - (avgCpu / 100));
}
resource.metadata.lastUpdated = new Date();
}
updatePoolStatistics(pool) {
const resources = pool.resources.map(id => this.resources.get(id)).filter(Boolean);
pool.statistics.totalResources = resources.length;
pool.statistics.availableResources = resources.filter(r => r.status === "available").length;
if (resources.length > 0) {
const totalUtilization = resources.reduce((sum, r) => sum + this.calculateResourceUtilization(r), 0);
pool.statistics.utilizationRate = totalUtilization / resources.length;
const totalCost = resources.reduce((sum, r) => sum + r.cost, 0);
pool.statistics.costPerHour = totalCost;
}
}
async checkQoSViolations() {
// Check QoS for all active allocations
for (const allocation of this.allocations.values()) {
if (allocation.status !== "active")
continue;
const resource = this.resources.get(allocation.resourceId);
if (!resource)
continue;
// Find applicable pools
const pools = Array.from(this.pools.values())
.filter(p => p.resources.includes(resource.id));
for (const pool of pools) {
await this.checkPoolQoS(pool, allocation);
}
}
}
async checkPoolQoS(pool, allocation) {
for (const guarantee of pool.qos.guarantees) {
const value = this.getMetricValue(allocation, guarantee.metric);
const violated = this.evaluateQoSCondition(value, guarantee.operator, guarantee.threshold);
if (violated) {
const violation = {
timestamp: new Date(),
metric: guarantee.metric,
expected: guarantee.threshold,
actual: value,
severity: this.calculateViolationSeverity(guarantee, value),
duration: 0, // Will be calculated over time
resolved: false,
};
allocation.qosViolations.push(violation);
this.logger.warn("QoS violation detected", {
allocationId: allocation.id,
metric: guarantee.metric,
expected: guarantee.threshold,
actual: value,
});
this.emit("qos:violation", { allocation, violation });
// Auto-remediation if enabled
if (pool.qos.violations.autoRemediation) {
await this.remediateQoSViolation(allocation, violation);
}
}
}
}
async updatePredictions() {
for (const resource of this.resources.values()) {
const history = this.usageHistory.get(resource.id) || [];
if (history.length < 10)
continue; // Need minimum history
const prediction = await this.optimizer.predictUsage(resource, history);
this.predictions.set(resource.id, prediction);
}
}
calculatePoolMetrics(pool) {
const resources = pool.resources.map(id => this.resources.get(id)).filter(Boolean);
const metrics = {};
if (resources.length === 0)
return metrics;
// Calculate utilization
const totalUtilization = resources.reduce((sum, r) => sum + this.calculateResourceUtilization(r), 0);
metrics.utilization = totalUtilization / resources.length;
// Calculate queue depth (simplified)
const totalReservations = resources.reduce((sum, r) => sum + r.reservations.length, 0);
metrics.queue_depth = totalReservations;
return metrics;
}
shouldScale(pool, metrics) {
const { scaling } = pool;
// Check scale-up conditions
for (const metric of scaling.metrics) {
const value = metrics[metric.name] || 0;
if (metric.aggregation === "avg" && value > metric.threshold) {
if (pool.resources.length < scaling.maxResources) {
return { action: "scale-up", reason: `${metric.name} threshold exceeded` };
}
}
}
// Check scale-down conditions
for (const metric of scaling.metrics) {
const value = metrics[metric.name] || 0;
if (metric.aggregation === "avg" && value < scaling.scaleDownThreshold) {
if (pool.resources.length > scaling.minResources) {
return { action: "scale-down", reason: `${metric.name} below threshold` };
}
}
}
return { action: "none", reason: "No scaling needed" };
}
async scalePoolUp(pool) {
this.logger.info("Scaling pool up", { poolId: pool.id });
// Implementation would add new resources to the pool
this.emit("pool:scaled-up", { pool });
}
async scalePoolDown(pool) {
this.logger.info("Scaling pool down", { poolId: pool.id });
// Implementation would remove underutilized resources from the pool
this.emit("pool:scaled-down", { pool });
}
getMetricValue(allocation, metric) {
switch (metric) {
case "cpu": return allocation.actualUsage.cpu;
case "memory": return allocation.actualUsage.memory;
case "efficiency": return allocation.efficiency;
default: return 0;
}
}
evaluateQoSCondition(value, operator, threshold) {
switch (operator) {
case "gt": return value > threshold;
case "lt": return value < threshold;
case "eq": return value === threshold;
case "gte": return value >= threshold;
case "lte": return value <= threshold;
default: return false;
}
}
calculateViolationSeverity(guarantee, actualValue) {
const deviation = Math.abs(actualValue - guarantee.threshold) / guarantee.threshold;
if (deviation > 0.5)
return "critical";
if (deviation > 0.3)
return "high";
if (deviation > 0.1)
return "medium";
return "low";
}
async remediateQoSViolation(allocation, violation) {
this.logger.info("Attempting QoS violation remediation", {
allocationId: allocation.id,
metric: violation.metric,
severity: violation.severity,
});
// Simple remediation strategies
switch (violation.metric) {
case "cpu":
// Could migrate to a less loaded resource
break;
case "memory":
// Could increase memory allocation if available
break;
case "efficiency":
// Could provide optimization recommendations
break;
}
this.emit("qos:remediation-attempted", { allocation, violation });
}
async releaseAllAllocations() {
const activeAllocations = Array.from(this.allocations.values())
.filter(a => a.status === "active");
for (const allocation of activeAllocations) {
await this.releaseResources(allocation.id, "system_shutdown");
}
}
handleResourceRequest(data) {
// Handle resource requests from agents
this.emit("resource:request-received", data);
}
handleResourceRelease(data) {
// Handle resource releases from agents
this.emit("resource:release-received", data);
}
handleResourceFailure(data) {
const failureData = data;
const resource = this.resources.get(failureData.resourceId);
if (resource) {
resource.status = "failed";
// Record failure
resource.metadata.reliability.failureHistory.push({
timestamp: new Date(),
type: failureData.type || "unknown",
duration: failureData.duration || 0,
impact: failureData.impact || "medium",
resolved: false,
});
this.logger.error("Resource failure detected", {
resourceId: failureData.resourceId,
type: failureData.type,
});
this.emit("resource:failed", { resource, failure: failureData });
}
}
handleScalingTrigger(data) {
// Handle scaling triggers from monitoring system
this.emit("scaling:triggered", data);
}
handleAgentStatusChange(agentId, from, to) {
// Handle agent status change
this.emit("agent:status-changed", { agentId, from, to });
}
allocateResourcesForTask(taskId, requirements) {
// Handle task start
this.emit("task:started", { taskId, requirements });
}
releaseResourcesForTask(taskId) {
// Handle task completion
this.emit("task:completed", { taskId });
}
handleResourceAlert(type, threshold, current) {
// Handle system resource alert
this.emit("system:resource-alert", { type, threshold, current });
}
// === PUBLIC API ===
getResource(resourceId) {
return this.resources.get(resourceId);
}
getAllResources() {
return Array.from(this.resources.values());
}
getResourcesByType(type) {
return Array.from(this.resources.values()).filter(r => r.type === type);
}
getPool(poolId) {
return this.pools.get(poolId);
}
getAllPools() {
return Array.from(this.pools.values());
}
getReservation(reservationId) {
return this.reservations.get(reservationId);
}
getAllReservations() {
return Array.from(this.reservations.values());
}
getAllocation(allocationId) {
return this.allocations.get(allocationId);
}
getAllAllocations() {
return Array.from(this.allocations.values());
}
getResourceUsageHistory(resourceId) {
return this.usageHistory.get(resourceId) || [];
}
getResourcePrediction(resourceId) {
return this.predictions.get(resourceId);
}
getManagerStatistics() {
const resources = Array.from(this.resources.values());
const allocations = Array.from(this.allocations.values());
const totalCapacity = resources.reduce((sum, r) => sum + r.capacity.cpu, 0);
const totalAllocated = resources.reduce((sum, r) => sum + r.allocated.cpu, 0);
const activeAllocations = allocations.filter(a => a.status === "active");
const avgEfficiency = activeAllocations.length > 0 ?
activeAllocations.reduce((sum, a) => sum + a.efficiency, 0) / activeAllocations.length : 1.0;
return {
resources: this.resources.size,
pools: this.pools.size,
reservations: this.reservations.size,
allocations: this.allocations.size,
utilization: totalCapacity > 0 ? totalAllocated / totalCapacity : 0,
efficiency: avgEfficiency,
};
}
}
class ResourceOptimizer {
config;
logger;
constructor(config, logger) {
this.config = config;
this.logger = logger;
}
async initialize() {
this.logger.debug("Resource optimizer initialized");
}
async shutdown() {
this.logger.debug("Resource optimizer shutdown");
}
async predictUsage(resource, history) {
// Simple linear trend analysis
const predictions = [];
// Calculate trends
const cpuTrend = this.calculateTrend(history.map(h => h.cpu));
const memoryTrend = this.calculateTrend(history.map(h => h.memory));
const diskTrend = this.calculateTrend(history.map(h => h.disk));
// Generate predictions for next 24 hours
for (let i = 1; i <= 24; i++) {
const futureTime = new Date(Date.now() + i * 3600000); // i hours from now
predictions.push({
timestamp: futureTime,
predictedUsage: {
cpu: Math.max(0, Math.min(100, this.extrapolateTrend(cpuTrend, i))),
memory: Math.max(0, this.extrapolateTrend(memoryTrend, i)),
disk: Math.max(0, this.extrapolateTrend(diskTrend, i)),
network: 0, // Simplified
custom: {},
timestamp: futureTime,
duration: 3600000, // 1 hour
},
confidence: Math.max(0.1, 1.0 - (i * 0.05)), // Decreasing confidence over time
});
}
return {
resourceId: resource.id,
predictions,
trends: {
cpu: this.categorizeTrend(cpuTrend),
memory: this.categorizeTrend(memoryTrend),
disk: this.categorizeTrend(diskTrend),
},
recommendations: this.generateRecommendations(resource, history),
};
}
calculateTrend(values) {
if (values.length < 2) {
return { slope: 0, intercept: values[0] || 0, r2: 0 };
}
const n = values.length;
const sumX = values.reduce((sum, _, i) => sum + i, 0);
const sumY = values.reduce((sum, val) => sum + val, 0);
const sumXY = values.reduce((sum, val, i) => sum + i * val, 0);
const sumXX = values.reduce((sum, _, i) => sum + i * i, 0);
const slope = (n * sumXY - sumX * sumY) / (n * sumXX - sumX * sumX);
const intercept = (sumY - slope * sumX) / n;
// Calculate R²
const meanY = sumY / n;
const ssTotal = values.reduce((sum, val) => sum + Math.pow(val - meanY, 2), 0);
const ssRes = values.reduce((sum, val, i) => {
const predicted = slope * i + intercept;
return sum + Math.pow(val - predicted, 2);
}, 0);
const r2 = 1 - (ssRes / ssTotal);
return { slope, intercept, r2 };
}
extrapolateTrend(trend, steps) {
return trend.slope * steps + trend.intercept;
}
categorizeTrend(trend) {
const threshold = 0.1;
if (trend.slope > threshold)
return "increasing";
if (trend.slope < -threshold)
return "decreasing";
return "stable";
}
generateRecommendations(resource, history) {
const recommendations = [];
if (history.length === 0) {
return recommendations;
}
const recent = history.slice(-10);
const avgCpu = recent.reduce((sum, h) => sum + h.cpu, 0) / recent.length;
const avgMemory = recent.reduce((sum, h) => sum + h.memory, 0) / recent.length;
// CPU recommendations
if (avgCpu > 80) {
recommendations.push("High CPU usage detected. Consider scaling up or optimizing workloads.");
}
else if (avgCpu < 20) {
recommendations.push("Low CPU usage. Consider scaling down to reduce costs.");
}
// Memory recommendations
const memoryUtilization = avgMemory / resource.capacity.memory;
if (memoryUtilization > 0.9) {
recommendations.push("High memory usage. Consider increasing memory allocation.");
}
else if (memoryUtilization < 0.3) {
recommendations.push("Low memory usage. Consider reducing memory allocation.");
}
return recommendations;
}
}
class ResourceManagerMetrics {
allocationsCreated = 0;
allocationsReleased = 0;
reservationsFailed = 0;
qosViolations = 0;
recordAllocationCreated() {
this.allocationsCreated++;
}
recordAllocationReleased(allocation) {
this.allocationsReleased++;
}
recordReservationFailed() {
this.reservationsFailed++;
}
recordQoSViolation() {
this.qosViolations++;
}
getMetrics() {
return {
allocationsCreated: this.allocationsCreated,
allocationsReleased: this.allocationsReleased,
reservationsFailed: this.reservationsFailed,
qosViolations: this.qosViolations,
successRate: this.allocationsCreated > 0 ?
((this.allocationsCreated - this.reservationsFailed) / this.allocationsCreated) * 100 : 100,
};
}
}
//# sourceMappingURL=resource-manager.js.map