@sailboat-computer/resilience
Version:
Enhanced resilience patterns for sailboat computer v3 with marine-specific adaptations
403 lines • 13.3 kB
JavaScript
;
/**
* 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