UNPKG

@sailboat-computer/resilience

Version:

Enhanced resilience patterns for sailboat computer v3 with marine-specific adaptations

430 lines 14.9 kB
"use strict"; /** * Resource pool implementation for bulkhead pattern */ Object.defineProperty(exports, "__esModule", { value: true }); exports.ResourcePool = void 0; const marine_constants_1 = require("../types/marine-constants"); /** * Resource pool for marine bulkhead pattern */ class ResourcePool { constructor(config) { this.activeResources = new Map(); this.requestQueue = []; this.eventListeners = []; this.nextResourceId = 1; this.config = { ...config }; this.metrics = this.initializeMetrics(); } /** * Request a resource from the pool */ async requestResource(request) { const fullRequest = { ...request, id: `req-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`, requestedAt: new Date() }; const startTime = Date.now(); // Check if resource can be allocated immediately if (this.canAllocateImmediately(fullRequest)) { const allocation = this.allocateResource(fullRequest); const waitTime = Date.now() - startTime; this.updateMetrics(waitTime, false); return { granted: true, resourceId: allocation.resourceId, waitTime, reason: 'Immediate allocation' }; } // Check if request should be queued or rejected if (!this.shouldQueue(fullRequest)) { this.metrics.rejectedRequests++; this.emitEvent('bulkhead_rejection', fullRequest.id, { reason: 'Pool capacity exceeded', poolName: this.config.poolName, priority: fullRequest.priority, marineSystemType: fullRequest.marineSystemType }, 'warning'); return { granted: false, waitTime: Date.now() - startTime, reason: 'Pool capacity exceeded - request rejected' }; } // Queue the request return this.queueRequest(fullRequest); } /** * Release a resource back to the pool */ releaseResource(resourceId) { const activeResource = this.activeResources.get(resourceId); if (!activeResource) { return false; } // Remove from active resources this.activeResources.delete(resourceId); this.metrics.activeRequests--; // Update metrics const usageTime = Date.now() - activeResource.allocatedAt.getTime(); this.updateResourceMetrics(activeResource, usageTime); // Process next request in queue if any this.processQueue(); this.emitEvent('recovery', resourceId, { poolName: this.config.poolName, usageTime, marineSystemType: activeResource.request.marineSystemType }, 'info'); return true; } /** * Update marine environment for adaptive behavior */ updateMarineEnvironment(environment) { this.marineEnvironment = environment; this.adjustPoolCapacity(); } /** * Get current pool metrics */ getMetrics() { return { ...this.metrics }; } /** * Get current pool status */ getStatus() { return { config: this.config, metrics: this.metrics, activeResources: Array.from(this.activeResources.values()).map(r => ({ resourceId: r.resourceId, priority: r.request.priority, marineSystemType: r.request.marineSystemType, allocatedAt: r.allocatedAt, powerConsumption: r.powerConsumption })), queuedRequests: this.requestQueue.map(r => ({ id: r.id, priority: r.priority, marineSystemType: r.marineSystemType, waitTime: Date.now() - r.requestedAt.getTime() })) }; } /** * Add event listener */ onEvent(listener) { this.eventListeners.push(listener); } /** * Force release all resources (emergency/maintenance) */ forceReleaseAll(reason = 'Emergency release') { const releasedCount = this.activeResources.size; this.activeResources.clear(); this.requestQueue = []; this.metrics.activeRequests = 0; this.metrics.queuedRequests = 0; this.emitEvent('recovery', 'pool-reset', { poolName: this.config.poolName, releasedCount, reason }, 'warning'); } /** * Check if resource can be allocated immediately */ canAllocateImmediately(request) { // Check basic capacity if (this.metrics.activeRequests >= this.config.maxConcurrentRequests) { return false; } // Check marine-specific constraints if (this.marineEnvironment?.criticalOperationsOnly) { return request.priority === 'critical' || request.marineSystemType === 'safety' || request.marineSystemType === 'navigation'; } // Check power constraints if (this.marineEnvironment?.powerStatus === 'critical') { const powerConsumption = this.estimatePowerConsumption(request); const currentPowerUsage = this.metrics.powerImpact; if (currentPowerUsage + powerConsumption > this.getMaxPowerAllowance()) { return false; } } return true; } /** * Allocate a resource */ allocateResource(request) { const resourceId = `${this.config.poolName}-${this.nextResourceId++}`; const activeResource = { resourceId, request, allocatedAt: new Date(), powerConsumption: this.estimatePowerConsumption(request), systemHealthImpact: this.estimateSystemHealthImpact(request) }; this.activeResources.set(resourceId, activeResource); this.metrics.activeRequests++; this.metrics.powerImpact += activeResource.powerConsumption; this.metrics.systemHealthImpact = Math.max(this.metrics.systemHealthImpact, activeResource.systemHealthImpact); if (request.priority === 'critical') { this.metrics.criticalRequestsActive++; } return { resourceId }; } /** * Check if request should be queued */ shouldQueue(request) { // Always queue critical requests if (request.priority === 'critical') { return true; } // Check queue capacity const maxQueueSize = Math.floor(this.config.maxConcurrentRequests * 2); if (this.requestQueue.length >= maxQueueSize) { return false; } // Check wait time limits if (request.timeoutMs < this.config.maxWaitTime) { return false; } return true; } /** * Queue a request */ async queueRequest(request) { // Insert request in priority order this.insertRequestByPriority(request); this.metrics.queuedRequests++; const queuePosition = this.requestQueue.findIndex(r => r.id === request.id) + 1; const estimatedWaitTime = this.estimateWaitTime(queuePosition); // Wait for allocation or timeout return new Promise((resolve) => { const startTime = Date.now(); const timeoutId = setTimeout(() => { // Remove from queue if timed out this.removeFromQueue(request.id); resolve({ granted: false, waitTime: Date.now() - startTime, reason: 'Request timed out in queue' }); }, request.timeoutMs); // Store timeout ID for cleanup request.timeoutId = timeoutId; request.resolve = (allocation) => { clearTimeout(timeoutId); resolve(allocation); }; }); } /** * Process queued requests */ processQueue() { while (this.requestQueue.length > 0) { const nextRequest = this.requestQueue[0]; if (!nextRequest) break; if (this.canAllocateImmediately(nextRequest)) { // Remove from queue this.requestQueue.shift(); this.metrics.queuedRequests--; // Allocate resource const allocation = this.allocateResource(nextRequest); const waitTime = Date.now() - nextRequest.requestedAt.getTime(); this.updateMetrics(waitTime, true); // Resolve the promise if (nextRequest.resolve) { nextRequest.resolve({ granted: true, resourceId: allocation.resourceId, waitTime, reason: 'Allocated from queue' }); } } else { // Can't allocate more resources break; } } } /** * Insert request by priority */ insertRequestByPriority(request) { const priorityOrder = { critical: 0, normal: 1, low: 2 }; const requestPriority = priorityOrder[request.priority]; let insertIndex = this.requestQueue.length; for (let i = 0; i < this.requestQueue.length; i++) { const queuedPriority = priorityOrder[this.requestQueue[i].priority]; if (requestPriority < queuedPriority) { insertIndex = i; break; } } this.requestQueue.splice(insertIndex, 0, request); } /** * Remove request from queue */ removeFromQueue(requestId) { const index = this.requestQueue.findIndex(r => r.id === requestId); if (index >= 0) { this.requestQueue.splice(index, 1); this.metrics.queuedRequests--; this.metrics.rejectedRequests++; return true; } return false; } /** * Estimate power consumption for request */ estimatePowerConsumption(request) { const basePower = { navigation: 5, // Watts safety: 3, comfort: 10, maintenance: 15 }; let power = basePower[request.marineSystemType] || 5; // Adjust for priority if (request.priority === 'critical') { power *= 1.5; // Critical operations may use more power } return power; } /** * Estimate system health impact */ estimateSystemHealthImpact(request) { const baseImpact = { navigation: 0.1, safety: 0.05, comfort: 0.3, maintenance: 0.5 }; return baseImpact[request.marineSystemType] || 0.1; } /** * Get maximum power allowance based on marine environment */ getMaxPowerAllowance() { if (!this.marineEnvironment) { return 100; // Default 100W } switch (this.marineEnvironment.powerStatus) { case 'critical': return 30; // Minimal power usage case 'conservation': return 60; // Reduced power usage case 'normal': default: return 100; // Normal power usage } } /** * Estimate wait time for queue position */ estimateWaitTime(queuePosition) { if (this.metrics.averageWaitTime === 0) { return queuePosition * 5000; // Default 5 seconds per position } return queuePosition * this.metrics.averageWaitTime; } /** * Adjust pool capacity based on marine conditions */ adjustPoolCapacity() { if (!this.marineEnvironment) return; // Reduce capacity in critical conditions if (this.marineEnvironment.criticalOperationsOnly) { this.config.maxConcurrentRequests = Math.max(Math.floor(this.config.maxConcurrentRequests * 0.5), 2 // Minimum 2 resources ); } // Adjust wait times based on conditions if (this.marineEnvironment.seaState === 'very_rough') { this.config.maxWaitTime *= 2; // Allow longer waits in rough seas } } /** * Update metrics */ updateMetrics(waitTime, fromQueue) { // Update wait time metrics const totalWaitTime = this.metrics.averageWaitTime * this.metrics.totalCapacity; this.metrics.averageWaitTime = (totalWaitTime + waitTime) / (this.metrics.totalCapacity + 1); this.metrics.maxWaitTime = Math.max(this.metrics.maxWaitTime, waitTime); } /** * Update resource-specific metrics */ updateResourceMetrics(resource, usageTime) { this.metrics.powerImpact -= resource.powerConsumption; if (resource.request.priority === 'critical') { this.metrics.criticalRequestsActive--; } // Recalculate system health impact this.metrics.systemHealthImpact = Math.max(...Array.from(this.activeResources.values()).map(r => r.systemHealthImpact), 0); } /** * Emit resilience event */ emitEvent(eventType, component, details, severity) { const event = { timestamp: new Date(), eventType, component, details, severity, marineContext: { operationalContext: this.marineEnvironment?.powerStatus === 'critical' ? marine_constants_1.OperationalContext.EMERGENCY : marine_constants_1.OperationalContext.SAILING, environmentalImpact: this.marineEnvironment?.seaState === 'very_rough', safetyImpact: details['marineSystemType'] === 'safety' } }; this.eventListeners.forEach(listener => { try { listener(event); } catch (error) { console.error('Error in resource pool event listener:', error); } }); } /** * Initialize metrics */ initializeMetrics() { return { poolName: this.config.poolName, totalCapacity: this.config.maxConcurrentRequests, availableResources: this.config.maxConcurrentRequests, activeRequests: 0, queuedRequests: 0, rejectedRequests: 0, averageWaitTime: 0, maxWaitTime: 0, powerImpact: 0, criticalRequestsActive: 0, systemHealthImpact: 0 }; } } exports.ResourcePool = ResourcePool; //# sourceMappingURL=ResourcePool.js.map