UNPKG

@sailboat-computer/resilience

Version:

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

488 lines (432 loc) 12.9 kB
/** * Bulkhead manager for coordinating multiple resource pools */ import { ResourcePool, ResourceRequest, ResourceAllocation } from './ResourcePool'; import { BulkheadConfig, ResourcePoolMetrics, ResilienceEvent, MarineEnvironmentStatus } from '../types'; import { OperationalContext, OperationalContextType } from '../types/marine-constants'; /** * Bulkhead execution context */ export interface BulkheadExecutionContext { poolName: string; priority: 'critical' | 'normal' | 'low'; marineSystemType: 'navigation' | 'safety' | 'comfort' | 'maintenance'; operationalContext: OperationalContextType; timeoutMs?: number; } /** * Bulkhead execution result */ export interface BulkheadExecutionResult<T> { success: boolean; data?: T; error?: Error; resourceAllocation: ResourceAllocation; poolMetrics: ResourcePoolMetrics; executionTime: number; } /** * Manager for coordinating multiple bulkhead resource pools */ export class BulkheadManager { private static instance: BulkheadManager | null = null; private pools: Map<string, ResourcePool> = new Map(); private eventListeners: ((event: ResilienceEvent) => void)[] = []; private marineEnvironment?: MarineEnvironmentStatus; /** * Get the singleton instance of BulkheadManager * This ensures only one instance exists across the application */ public static getInstance(): BulkheadManager { if (!BulkheadManager.instance) { BulkheadManager.instance = new BulkheadManager(); } return BulkheadManager.instance; } constructor() { // Initialize with default marine resource pools this.initializeDefaultPools(); } /** * Execute operation with bulkhead protection */ async execute<T>( operation: () => Promise<T>, context: BulkheadExecutionContext ): Promise<BulkheadExecutionResult<T>> { 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: Omit<ResourceRequest, 'id' | 'requestedAt'> = { 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 as Error, resourceAllocation: allocation, poolMetrics: pool.getMetrics(), executionTime }; } } /** * Create or update a resource pool */ createPool(config: BulkheadConfig): void { const pool = new 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: string): ResourcePool | undefined { return this.pools.get(poolName); } /** * Get all pool metrics */ getAllPoolMetrics(): Record<string, ResourcePoolMetrics> { const metrics: Record<string, ResourcePoolMetrics> = {}; for (const [poolName, pool] of this.pools) { metrics[poolName] = pool.getMetrics(); } return metrics; } /** * Get system-wide bulkhead status */ getSystemStatus() { const poolStatuses: Record<string, any> = {}; 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: MarineEnvironmentStatus): void { 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' ? OperationalContext.EMERGENCY : OperationalContext.SAILING, environmentalImpact: environment.seaState === 'very_rough', safetyImpact: environment.criticalOperationsOnly } }); } /** * Emergency release all resources across all pools */ emergencyReleaseAll(reason: string = 'Emergency shutdown'): void { 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: OperationalContext.EMERGENCY, environmentalImpact: true, safetyImpact: true } }); } /** * Add event listener */ onEvent(listener: (event: ResilienceEvent) => void): void { this.eventListeners.push(listener); } /** * Remove a resource pool */ removePool(poolName: string): boolean { 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(): string[] { return Array.from(this.pools.keys()); } /** * Check if pool exists */ hasPool(poolName: string): boolean { return this.pools.has(poolName); } /** * Initialize default marine resource pools */ private initializeDefaultPools(): void { // 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 */ private emitEvent(event: ResilienceEvent): void { this.eventListeners.forEach(listener => { try { listener(event); } catch (error) { console.error('Error in bulkhead manager event listener:', error); } }); } /** * Get empty metrics for error cases */ private getEmptyMetrics(poolName: string): ResourcePoolMetrics { return { poolName, totalCapacity: 0, availableResources: 0, activeRequests: 0, queuedRequests: 0, rejectedRequests: 0, averageWaitTime: 0, maxWaitTime: 0, powerImpact: 0, criticalRequestsActive: 0, systemHealthImpact: 0 }; } } /** * Default bulkhead manager instance * Using the getInstance method ensures we always get the same instance */ export const defaultBulkheadManager = BulkheadManager.getInstance(); /** * Convenience function to execute with bulkhead protection * Always uses the singleton instance to ensure consistency */ export async function executeWithBulkhead<T>( operation: () => Promise<T>, context: BulkheadExecutionContext ): Promise<BulkheadExecutionResult<T>> { return BulkheadManager.getInstance().execute(operation, context); } /** * Pre-configured bulkhead execution contexts */ export const BulkheadContexts = { /** * Navigation operations (GPS, compass, autopilot) */ navigation: (operationalContext: OperationalContextType = OperationalContext.SAILING): BulkheadExecutionContext => ({ poolName: 'navigation', priority: 'critical', marineSystemType: 'navigation', operationalContext, timeoutMs: 5000 }), /** * Safety operations (anchor alarm, collision avoidance) */ safety: (operationalContext: OperationalContextType = OperationalContext.SAILING): BulkheadExecutionContext => ({ poolName: 'safety', priority: 'critical', marineSystemType: 'safety', operationalContext, timeoutMs: 3000 }), /** * Comfort operations (lighting, entertainment, climate) */ comfort: (operationalContext: OperationalContextType = OperationalContext.SAILING): BulkheadExecutionContext => ({ poolName: 'comfort', priority: 'low', marineSystemType: 'comfort', operationalContext, timeoutMs: 15000 }), /** * Maintenance operations (diagnostics, updates) */ maintenance: (operationalContext: OperationalContextType = OperationalContext.MAINTENANCE): BulkheadExecutionContext => ({ poolName: 'maintenance', priority: 'normal', marineSystemType: 'maintenance', operationalContext, timeoutMs: 60000 }), /** * Communication operations (weather, email, messaging) */ communication: (operationalContext: OperationalContextType = OperationalContext.SAILING): BulkheadExecutionContext => ({ poolName: 'communication', priority: 'normal', marineSystemType: 'comfort', operationalContext, timeoutMs: 30000 }) };