UNPKG

@sailboat-computer/resilience

Version:

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

403 lines 13.3 kB
"use strict"; /** * Bulkhead manager for coordinating multiple resource pools */ Object.defineProperty(exports, "__esModule", { value: true }); exports.BulkheadContexts = exports.executeWithBulkhead = exports.defaultBulkheadManager = exports.BulkheadManager = void 0; const ResourcePool_1 = require("./ResourcePool"); const marine_constants_1 = require("../types/marine-constants"); /** * Manager for coordinating multiple bulkhead resource pools */ class BulkheadManager { /** * Get the singleton instance of BulkheadManager * This ensures only one instance exists across the application */ static getInstance() { if (!BulkheadManager.instance) { BulkheadManager.instance = new BulkheadManager(); } return BulkheadManager.instance; } constructor() { this.pools = new Map(); this.eventListeners = []; // Initialize with default marine resource pools this.initializeDefaultPools(); } /** * Execute operation with bulkhead protection */ async execute(operation, context) { const startTime = Date.now(); const pool = this.getPool(context.poolName); if (!pool) { return { success: false, error: new Error(`Resource pool '${context.poolName}' not found`), resourceAllocation: { granted: false, waitTime: 0, reason: 'Pool not found' }, poolMetrics: this.getEmptyMetrics(context.poolName), executionTime: Date.now() - startTime }; } // Request resource from pool const resourceRequest = { priority: context.priority, operationalContext: context.operationalContext, timeoutMs: context.timeoutMs || 30000, // Default 30 seconds marineSystemType: context.marineSystemType }; const allocation = await pool.requestResource(resourceRequest); if (!allocation.granted) { return { success: false, error: new Error(`Resource allocation failed: ${allocation.reason}`), resourceAllocation: allocation, poolMetrics: pool.getMetrics(), executionTime: Date.now() - startTime }; } // Execute operation with resource try { const result = await operation(); const executionTime = Date.now() - startTime; // Release resource if (allocation.resourceId) { pool.releaseResource(allocation.resourceId); } return { success: true, data: result, resourceAllocation: allocation, poolMetrics: pool.getMetrics(), executionTime }; } catch (error) { const executionTime = Date.now() - startTime; // Release resource on error if (allocation.resourceId) { pool.releaseResource(allocation.resourceId); } return { success: false, error: error, resourceAllocation: allocation, poolMetrics: pool.getMetrics(), executionTime }; } } /** * Create or update a resource pool */ createPool(config) { const pool = new ResourcePool_1.ResourcePool(config); // Forward events from pool pool.onEvent((event) => { this.emitEvent(event); }); // Update with current marine environment if (this.marineEnvironment) { pool.updateMarineEnvironment(this.marineEnvironment); } this.pools.set(config.poolName, pool); } /** * Get resource pool by name */ getPool(poolName) { return this.pools.get(poolName); } /** * Get all pool metrics */ getAllPoolMetrics() { const metrics = {}; for (const [poolName, pool] of this.pools) { metrics[poolName] = pool.getMetrics(); } return metrics; } /** * Get system-wide bulkhead status */ getSystemStatus() { const poolStatuses = {}; let totalCapacity = 0; let totalActive = 0; let totalQueued = 0; let totalRejected = 0; let totalPowerImpact = 0; for (const [poolName, pool] of this.pools) { const status = pool.getStatus(); poolStatuses[poolName] = status; totalCapacity += status.metrics.totalCapacity; totalActive += status.metrics.activeRequests; totalQueued += status.metrics.queuedRequests; totalRejected += status.metrics.rejectedRequests; totalPowerImpact += status.metrics.powerImpact; } return { pools: poolStatuses, systemTotals: { totalCapacity, totalActive, totalQueued, totalRejected, totalPowerImpact, utilizationPercentage: totalCapacity > 0 ? (totalActive / totalCapacity) * 100 : 0 }, marineEnvironment: this.marineEnvironment }; } /** * Update marine environment for all pools */ updateMarineEnvironment(environment) { this.marineEnvironment = environment; for (const pool of this.pools.values()) { pool.updateMarineEnvironment(environment); } // Emit environment update event this.emitEvent({ timestamp: new Date(), eventType: 'recovery', component: 'bulkhead-manager', details: { action: 'marine_environment_updated', seaState: environment.seaState, powerStatus: environment.powerStatus, criticalOperationsOnly: environment.criticalOperationsOnly }, severity: 'info', marineContext: { operationalContext: environment.powerStatus === 'critical' ? marine_constants_1.OperationalContext.EMERGENCY : marine_constants_1.OperationalContext.SAILING, environmentalImpact: environment.seaState === 'very_rough', safetyImpact: environment.criticalOperationsOnly } }); } /** * Emergency release all resources across all pools */ emergencyReleaseAll(reason = 'Emergency shutdown') { let totalReleased = 0; for (const [poolName, pool] of this.pools) { const status = pool.getStatus(); const activeCount = status.metrics.activeRequests; pool.forceReleaseAll(reason); totalReleased += activeCount; } this.emitEvent({ timestamp: new Date(), eventType: 'recovery', component: 'bulkhead-manager', details: { action: 'emergency_release_all', totalResourcesReleased: totalReleased, reason }, severity: 'critical', marineContext: { operationalContext: marine_constants_1.OperationalContext.EMERGENCY, environmentalImpact: true, safetyImpact: true } }); } /** * Add event listener */ onEvent(listener) { this.eventListeners.push(listener); } /** * Remove a resource pool */ removePool(poolName) { const pool = this.pools.get(poolName); if (pool) { // Force release all resources before removing pool.forceReleaseAll('Pool being removed'); this.pools.delete(poolName); return true; } return false; } /** * Get pool names */ getPoolNames() { return Array.from(this.pools.keys()); } /** * Check if pool exists */ hasPool(poolName) { return this.pools.has(poolName); } /** * Initialize default marine resource pools */ initializeDefaultPools() { // Navigation pool - critical for vessel safety this.createPool({ poolName: 'navigation', maxConcurrentRequests: 8, maxWaitTime: 5000, // 5 seconds priority: 'critical', marineSystemType: 'navigation', powerConsumption: 'medium', operationalDependency: 'essential' }); // Safety pool - highest priority this.createPool({ poolName: 'safety', maxConcurrentRequests: 6, maxWaitTime: 3000, // 3 seconds priority: 'critical', marineSystemType: 'safety', powerConsumption: 'low', operationalDependency: 'essential' }); // Comfort pool - lower priority, can be throttled this.createPool({ poolName: 'comfort', maxConcurrentRequests: 4, maxWaitTime: 15000, // 15 seconds priority: 'low', marineSystemType: 'comfort', powerConsumption: 'high', operationalDependency: 'optional' }); // Maintenance pool - for system maintenance operations this.createPool({ poolName: 'maintenance', maxConcurrentRequests: 2, maxWaitTime: 60000, // 1 minute priority: 'normal', marineSystemType: 'maintenance', powerConsumption: 'high', operationalDependency: 'important' }); // Communication pool - for network operations this.createPool({ poolName: 'communication', maxConcurrentRequests: 3, maxWaitTime: 30000, // 30 seconds priority: 'normal', marineSystemType: 'comfort', // Communication is comfort/convenience powerConsumption: 'medium', operationalDependency: 'important' }); } /** * Emit resilience event */ emitEvent(event) { this.eventListeners.forEach(listener => { try { listener(event); } catch (error) { console.error('Error in bulkhead manager event listener:', error); } }); } /** * Get empty metrics for error cases */ getEmptyMetrics(poolName) { return { poolName, totalCapacity: 0, availableResources: 0, activeRequests: 0, queuedRequests: 0, rejectedRequests: 0, averageWaitTime: 0, maxWaitTime: 0, powerImpact: 0, criticalRequestsActive: 0, systemHealthImpact: 0 }; } } exports.BulkheadManager = BulkheadManager; BulkheadManager.instance = null; /** * Default bulkhead manager instance * Using the getInstance method ensures we always get the same instance */ exports.defaultBulkheadManager = BulkheadManager.getInstance(); /** * Convenience function to execute with bulkhead protection * Always uses the singleton instance to ensure consistency */ async function executeWithBulkhead(operation, context) { return BulkheadManager.getInstance().execute(operation, context); } exports.executeWithBulkhead = executeWithBulkhead; /** * Pre-configured bulkhead execution contexts */ exports.BulkheadContexts = { /** * Navigation operations (GPS, compass, autopilot) */ navigation: (operationalContext = marine_constants_1.OperationalContext.SAILING) => ({ poolName: 'navigation', priority: 'critical', marineSystemType: 'navigation', operationalContext, timeoutMs: 5000 }), /** * Safety operations (anchor alarm, collision avoidance) */ safety: (operationalContext = marine_constants_1.OperationalContext.SAILING) => ({ poolName: 'safety', priority: 'critical', marineSystemType: 'safety', operationalContext, timeoutMs: 3000 }), /** * Comfort operations (lighting, entertainment, climate) */ comfort: (operationalContext = marine_constants_1.OperationalContext.SAILING) => ({ poolName: 'comfort', priority: 'low', marineSystemType: 'comfort', operationalContext, timeoutMs: 15000 }), /** * Maintenance operations (diagnostics, updates) */ maintenance: (operationalContext = marine_constants_1.OperationalContext.MAINTENANCE) => ({ poolName: 'maintenance', priority: 'normal', marineSystemType: 'maintenance', operationalContext, timeoutMs: 60000 }), /** * Communication operations (weather, email, messaging) */ communication: (operationalContext = marine_constants_1.OperationalContext.SAILING) => ({ poolName: 'communication', priority: 'normal', marineSystemType: 'comfort', operationalContext, timeoutMs: 30000 }) }; //# sourceMappingURL=BulkheadManager.js.map