@zintrust/workers 0.1.27
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +861 -0
- package/dist/AnomalyDetection.d.ts +102 -0
- package/dist/AnomalyDetection.js +321 -0
- package/dist/AutoScaler.d.ts +127 -0
- package/dist/AutoScaler.js +425 -0
- package/dist/BroadcastWorker.d.ts +21 -0
- package/dist/BroadcastWorker.js +24 -0
- package/dist/CanaryController.d.ts +103 -0
- package/dist/CanaryController.js +380 -0
- package/dist/ChaosEngineering.d.ts +79 -0
- package/dist/ChaosEngineering.js +216 -0
- package/dist/CircuitBreaker.d.ts +106 -0
- package/dist/CircuitBreaker.js +374 -0
- package/dist/ClusterLock.d.ts +90 -0
- package/dist/ClusterLock.js +385 -0
- package/dist/ComplianceManager.d.ts +177 -0
- package/dist/ComplianceManager.js +556 -0
- package/dist/DatacenterOrchestrator.d.ts +133 -0
- package/dist/DatacenterOrchestrator.js +404 -0
- package/dist/DeadLetterQueue.d.ts +122 -0
- package/dist/DeadLetterQueue.js +539 -0
- package/dist/HealthMonitor.d.ts +42 -0
- package/dist/HealthMonitor.js +301 -0
- package/dist/MultiQueueWorker.d.ts +89 -0
- package/dist/MultiQueueWorker.js +277 -0
- package/dist/NotificationWorker.d.ts +21 -0
- package/dist/NotificationWorker.js +23 -0
- package/dist/Observability.d.ts +153 -0
- package/dist/Observability.js +530 -0
- package/dist/PluginManager.d.ts +123 -0
- package/dist/PluginManager.js +392 -0
- package/dist/PriorityQueue.d.ts +117 -0
- package/dist/PriorityQueue.js +244 -0
- package/dist/ResourceMonitor.d.ts +164 -0
- package/dist/ResourceMonitor.js +605 -0
- package/dist/SLAMonitor.d.ts +110 -0
- package/dist/SLAMonitor.js +274 -0
- package/dist/WorkerFactory.d.ts +193 -0
- package/dist/WorkerFactory.js +1507 -0
- package/dist/WorkerInit.d.ts +85 -0
- package/dist/WorkerInit.js +223 -0
- package/dist/WorkerMetrics.d.ts +114 -0
- package/dist/WorkerMetrics.js +509 -0
- package/dist/WorkerRegistry.d.ts +145 -0
- package/dist/WorkerRegistry.js +319 -0
- package/dist/WorkerShutdown.d.ts +61 -0
- package/dist/WorkerShutdown.js +159 -0
- package/dist/WorkerVersioning.d.ts +107 -0
- package/dist/WorkerVersioning.js +300 -0
- package/dist/build-manifest.json +462 -0
- package/dist/config/workerConfig.d.ts +3 -0
- package/dist/config/workerConfig.js +19 -0
- package/dist/createQueueWorker.d.ts +23 -0
- package/dist/createQueueWorker.js +113 -0
- package/dist/dashboard/index.d.ts +1 -0
- package/dist/dashboard/index.js +1 -0
- package/dist/dashboard/types.d.ts +117 -0
- package/dist/dashboard/types.js +1 -0
- package/dist/dashboard/workers-api.d.ts +4 -0
- package/dist/dashboard/workers-api.js +638 -0
- package/dist/dashboard/workers-dashboard-ui.d.ts +3 -0
- package/dist/dashboard/workers-dashboard-ui.js +1026 -0
- package/dist/dashboard/workers-dashboard.d.ts +4 -0
- package/dist/dashboard/workers-dashboard.js +904 -0
- package/dist/helper/index.d.ts +5 -0
- package/dist/helper/index.js +10 -0
- package/dist/http/WorkerApiController.d.ts +38 -0
- package/dist/http/WorkerApiController.js +312 -0
- package/dist/http/WorkerController.d.ts +374 -0
- package/dist/http/WorkerController.js +1351 -0
- package/dist/http/middleware/CustomValidation.d.ts +92 -0
- package/dist/http/middleware/CustomValidation.js +270 -0
- package/dist/http/middleware/DatacenterValidator.d.ts +3 -0
- package/dist/http/middleware/DatacenterValidator.js +94 -0
- package/dist/http/middleware/EditWorkerValidation.d.ts +7 -0
- package/dist/http/middleware/EditWorkerValidation.js +55 -0
- package/dist/http/middleware/FeaturesValidator.d.ts +3 -0
- package/dist/http/middleware/FeaturesValidator.js +60 -0
- package/dist/http/middleware/InfrastructureValidator.d.ts +31 -0
- package/dist/http/middleware/InfrastructureValidator.js +226 -0
- package/dist/http/middleware/OptionsValidator.d.ts +3 -0
- package/dist/http/middleware/OptionsValidator.js +112 -0
- package/dist/http/middleware/PayloadSanitizer.d.ts +7 -0
- package/dist/http/middleware/PayloadSanitizer.js +42 -0
- package/dist/http/middleware/ProcessorPathSanitizer.d.ts +3 -0
- package/dist/http/middleware/ProcessorPathSanitizer.js +74 -0
- package/dist/http/middleware/QueueNameSanitizer.d.ts +3 -0
- package/dist/http/middleware/QueueNameSanitizer.js +45 -0
- package/dist/http/middleware/ValidateDriver.d.ts +7 -0
- package/dist/http/middleware/ValidateDriver.js +20 -0
- package/dist/http/middleware/VersionSanitizer.d.ts +3 -0
- package/dist/http/middleware/VersionSanitizer.js +25 -0
- package/dist/http/middleware/WorkerNameSanitizer.d.ts +3 -0
- package/dist/http/middleware/WorkerNameSanitizer.js +46 -0
- package/dist/http/middleware/WorkerValidationChain.d.ts +27 -0
- package/dist/http/middleware/WorkerValidationChain.js +185 -0
- package/dist/index.d.ts +46 -0
- package/dist/index.js +48 -0
- package/dist/routes/workers.d.ts +12 -0
- package/dist/routes/workers.js +81 -0
- package/dist/storage/WorkerStore.d.ts +45 -0
- package/dist/storage/WorkerStore.js +195 -0
- package/dist/type.d.ts +76 -0
- package/dist/type.js +1 -0
- package/dist/ui/router/ui.d.ts +3 -0
- package/dist/ui/router/ui.js +83 -0
- package/dist/ui/types/worker-ui.d.ts +229 -0
- package/dist/ui/types/worker-ui.js +5 -0
- package/package.json +53 -0
- package/src/AnomalyDetection.ts +434 -0
- package/src/AutoScaler.ts +654 -0
- package/src/BroadcastWorker.ts +34 -0
- package/src/CanaryController.ts +531 -0
- package/src/ChaosEngineering.ts +301 -0
- package/src/CircuitBreaker.ts +495 -0
- package/src/ClusterLock.ts +499 -0
- package/src/ComplianceManager.ts +815 -0
- package/src/DatacenterOrchestrator.ts +561 -0
- package/src/DeadLetterQueue.ts +733 -0
- package/src/HealthMonitor.ts +390 -0
- package/src/MultiQueueWorker.ts +431 -0
- package/src/NotificationWorker.ts +33 -0
- package/src/Observability.ts +696 -0
- package/src/PluginManager.ts +551 -0
- package/src/PriorityQueue.ts +351 -0
- package/src/ResourceMonitor.ts +769 -0
- package/src/SLAMonitor.ts +408 -0
- package/src/WorkerFactory.ts +2108 -0
- package/src/WorkerInit.ts +313 -0
- package/src/WorkerMetrics.ts +709 -0
- package/src/WorkerRegistry.ts +443 -0
- package/src/WorkerShutdown.ts +210 -0
- package/src/WorkerVersioning.ts +422 -0
- package/src/config/workerConfig.ts +25 -0
- package/src/createQueueWorker.ts +174 -0
- package/src/dashboard/index.ts +6 -0
- package/src/dashboard/types.ts +141 -0
- package/src/dashboard/workers-api.ts +785 -0
- package/src/dashboard/zintrust.svg +30 -0
- package/src/helper/index.ts +11 -0
- package/src/http/WorkerApiController.ts +369 -0
- package/src/http/WorkerController.ts +1512 -0
- package/src/http/middleware/CustomValidation.ts +360 -0
- package/src/http/middleware/DatacenterValidator.ts +124 -0
- package/src/http/middleware/EditWorkerValidation.ts +74 -0
- package/src/http/middleware/FeaturesValidator.ts +82 -0
- package/src/http/middleware/InfrastructureValidator.ts +295 -0
- package/src/http/middleware/OptionsValidator.ts +144 -0
- package/src/http/middleware/PayloadSanitizer.ts +52 -0
- package/src/http/middleware/ProcessorPathSanitizer.ts +86 -0
- package/src/http/middleware/QueueNameSanitizer.ts +55 -0
- package/src/http/middleware/ValidateDriver.ts +29 -0
- package/src/http/middleware/VersionSanitizer.ts +30 -0
- package/src/http/middleware/WorkerNameSanitizer.ts +56 -0
- package/src/http/middleware/WorkerValidationChain.ts +230 -0
- package/src/index.ts +98 -0
- package/src/routes/workers.ts +154 -0
- package/src/storage/WorkerStore.ts +240 -0
- package/src/type.ts +89 -0
- package/src/types/queue-monitor.d.ts +38 -0
- package/src/types/queue-redis.d.ts +38 -0
- package/src/ui/README.md +13 -0
- package/src/ui/components/JsonEditor.js +670 -0
- package/src/ui/components/JsonViewer.js +387 -0
- package/src/ui/components/WorkerCard.js +178 -0
- package/src/ui/components/WorkerExpandPanel.js +257 -0
- package/src/ui/components/fetcher.js +42 -0
- package/src/ui/components/sla-scorecard.js +32 -0
- package/src/ui/components/styles.css +30 -0
- package/src/ui/components/table-expander.js +34 -0
- package/src/ui/integration/worker-ui-integration.js +565 -0
- package/src/ui/router/ui.ts +99 -0
- package/src/ui/services/workerApi.js +240 -0
- package/src/ui/types/worker-ui.ts +283 -0
- package/src/ui/utils/jsonValidator.js +444 -0
- package/src/ui/workers/index.html +202 -0
- package/src/ui/workers/main.js +1781 -0
- package/src/ui/workers/styles.css +1350 -0
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Circuit Breaker Pattern
|
|
3
|
+
* Fault tolerance with version tracking and automatic recovery
|
|
4
|
+
* Sealed namespace for immutability
|
|
5
|
+
*/
|
|
6
|
+
export type CircuitState = 'closed' | 'open' | 'half-open';
|
|
7
|
+
export type CircuitBreakerConfig = {
|
|
8
|
+
failureThreshold: number;
|
|
9
|
+
successThreshold: number;
|
|
10
|
+
timeout: number;
|
|
11
|
+
resetTimeout: number;
|
|
12
|
+
volumeThreshold: number;
|
|
13
|
+
};
|
|
14
|
+
export type CircuitBreakerState = {
|
|
15
|
+
workerName: string;
|
|
16
|
+
version: string;
|
|
17
|
+
state: CircuitState;
|
|
18
|
+
failureCount: number;
|
|
19
|
+
successCount: number;
|
|
20
|
+
totalRequests: number;
|
|
21
|
+
lastFailureTime: Date | null;
|
|
22
|
+
lastSuccessTime: Date | null;
|
|
23
|
+
lastStateChange: Date;
|
|
24
|
+
nextRetryTime: Date | null;
|
|
25
|
+
errorRate: number;
|
|
26
|
+
};
|
|
27
|
+
export type CircuitBreakerEvent = {
|
|
28
|
+
workerName: string;
|
|
29
|
+
version: string;
|
|
30
|
+
event: 'opened' | 'closed' | 'half-open' | 'success' | 'failure' | 'rejected';
|
|
31
|
+
state: CircuitState;
|
|
32
|
+
timestamp: Date;
|
|
33
|
+
reason?: string;
|
|
34
|
+
error?: Error;
|
|
35
|
+
};
|
|
36
|
+
/**
|
|
37
|
+
* Circuit Breaker - Sealed namespace
|
|
38
|
+
*/
|
|
39
|
+
export declare const CircuitBreaker: Readonly<{
|
|
40
|
+
/**
|
|
41
|
+
* Initialize circuit breaker for a worker version
|
|
42
|
+
*/
|
|
43
|
+
initialize(workerName: string, version: string): void;
|
|
44
|
+
/**
|
|
45
|
+
* Check if circuit allows execution
|
|
46
|
+
*/
|
|
47
|
+
canExecute(workerName: string, version: string): boolean;
|
|
48
|
+
/**
|
|
49
|
+
* Record successful execution
|
|
50
|
+
*/
|
|
51
|
+
recordSuccess(workerName: string, version: string): void;
|
|
52
|
+
/**
|
|
53
|
+
* Record failed execution
|
|
54
|
+
*/
|
|
55
|
+
recordFailure(workerName: string, version: string, error: Error): void;
|
|
56
|
+
/**
|
|
57
|
+
* Record rejected execution (when circuit is open)
|
|
58
|
+
*/
|
|
59
|
+
recordRejection(workerName: string, version: string): void;
|
|
60
|
+
/**
|
|
61
|
+
* Get circuit state
|
|
62
|
+
*/
|
|
63
|
+
getState(workerName: string, version: string): CircuitBreakerState | null;
|
|
64
|
+
/**
|
|
65
|
+
* Get all circuit states
|
|
66
|
+
*/
|
|
67
|
+
getAllStates(): ReadonlyArray<CircuitBreakerState>;
|
|
68
|
+
/**
|
|
69
|
+
* Get circuit states by worker name (all versions)
|
|
70
|
+
*/
|
|
71
|
+
getStatesByWorker(workerName: string): ReadonlyArray<CircuitBreakerState>;
|
|
72
|
+
/**
|
|
73
|
+
* Get event history
|
|
74
|
+
*/
|
|
75
|
+
getEventHistory(workerName: string, version: string, limit?: number): ReadonlyArray<CircuitBreakerEvent>;
|
|
76
|
+
/**
|
|
77
|
+
* Manually reset circuit to closed state
|
|
78
|
+
*/
|
|
79
|
+
reset(workerName: string, version: string): void;
|
|
80
|
+
/**
|
|
81
|
+
* Manually force circuit to open state
|
|
82
|
+
*/
|
|
83
|
+
forceOpen(workerName: string, version: string, reason: string): void;
|
|
84
|
+
/**
|
|
85
|
+
* Delete circuit breaker
|
|
86
|
+
*/
|
|
87
|
+
delete(workerName: string, version: string): void;
|
|
88
|
+
/**
|
|
89
|
+
* Delete all circuit breakers for a worker (all versions)
|
|
90
|
+
*/
|
|
91
|
+
deleteWorker(workerName: string): void;
|
|
92
|
+
/**
|
|
93
|
+
* Get summary statistics
|
|
94
|
+
*/
|
|
95
|
+
getSummary(): {
|
|
96
|
+
totalCircuits: number;
|
|
97
|
+
openCircuits: number;
|
|
98
|
+
halfOpenCircuits: number;
|
|
99
|
+
closedCircuits: number;
|
|
100
|
+
circuitsByWorker: Record<string, number>;
|
|
101
|
+
};
|
|
102
|
+
/**
|
|
103
|
+
* Shutdown and clear all circuits
|
|
104
|
+
*/
|
|
105
|
+
shutdown(): void;
|
|
106
|
+
}>;
|
|
@@ -0,0 +1,374 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Circuit Breaker Pattern
|
|
3
|
+
* Fault tolerance with version tracking and automatic recovery
|
|
4
|
+
* Sealed namespace for immutability
|
|
5
|
+
*/
|
|
6
|
+
import { Logger } from '@zintrust/core';
|
|
7
|
+
// Internal state
|
|
8
|
+
const circuits = new Map();
|
|
9
|
+
const eventHistory = new Map();
|
|
10
|
+
const defaultConfig = {
|
|
11
|
+
failureThreshold: 5,
|
|
12
|
+
successThreshold: 3,
|
|
13
|
+
timeout: 60000, // 1 minute
|
|
14
|
+
resetTimeout: 300000, // 5 minutes
|
|
15
|
+
volumeThreshold: 10,
|
|
16
|
+
};
|
|
17
|
+
/**
|
|
18
|
+
* Helper: Get circuit key
|
|
19
|
+
*/
|
|
20
|
+
const getCircuitKey = (workerName, version) => {
|
|
21
|
+
return `${workerName}:${version}`;
|
|
22
|
+
};
|
|
23
|
+
/**
|
|
24
|
+
* Helper: Record event
|
|
25
|
+
*/
|
|
26
|
+
const recordEvent = (event) => {
|
|
27
|
+
const key = getCircuitKey(event.workerName, event.version);
|
|
28
|
+
let history = eventHistory.get(key);
|
|
29
|
+
if (!history) {
|
|
30
|
+
history = [];
|
|
31
|
+
eventHistory.set(key, history);
|
|
32
|
+
}
|
|
33
|
+
history.push(event);
|
|
34
|
+
// Keep only last 1000 events
|
|
35
|
+
if (history.length > 1000) {
|
|
36
|
+
history.shift();
|
|
37
|
+
}
|
|
38
|
+
Logger.debug(`Circuit breaker event: ${event.workerName}:${event.version} - ${event.event}`, {
|
|
39
|
+
state: event.state,
|
|
40
|
+
reason: event.reason,
|
|
41
|
+
});
|
|
42
|
+
};
|
|
43
|
+
/**
|
|
44
|
+
* Helper: Transition to new state
|
|
45
|
+
*/
|
|
46
|
+
const transitionState = (circuit, newState, reason) => {
|
|
47
|
+
const oldState = circuit.state;
|
|
48
|
+
circuit.state = newState;
|
|
49
|
+
circuit.lastStateChange = new Date();
|
|
50
|
+
if (newState === 'open') {
|
|
51
|
+
circuit.nextRetryTime = new Date(Date.now() + defaultConfig.timeout);
|
|
52
|
+
}
|
|
53
|
+
else if (newState === 'closed') {
|
|
54
|
+
circuit.failureCount = 0;
|
|
55
|
+
circuit.successCount = 0;
|
|
56
|
+
circuit.nextRetryTime = null;
|
|
57
|
+
}
|
|
58
|
+
else if (newState === 'half-open') {
|
|
59
|
+
circuit.successCount = 0;
|
|
60
|
+
circuit.nextRetryTime = null;
|
|
61
|
+
}
|
|
62
|
+
Logger.info(`Circuit breaker state transition: ${circuit.workerName}:${circuit.version}`, {
|
|
63
|
+
from: oldState,
|
|
64
|
+
to: newState,
|
|
65
|
+
reason,
|
|
66
|
+
});
|
|
67
|
+
const eventType = newState === 'open' ? 'opened' : newState;
|
|
68
|
+
recordEvent({
|
|
69
|
+
workerName: circuit.workerName,
|
|
70
|
+
version: circuit.version,
|
|
71
|
+
event: eventType,
|
|
72
|
+
state: newState,
|
|
73
|
+
timestamp: new Date(),
|
|
74
|
+
reason,
|
|
75
|
+
});
|
|
76
|
+
};
|
|
77
|
+
/**
|
|
78
|
+
* Helper: Calculate error rate
|
|
79
|
+
*/
|
|
80
|
+
const calculateErrorRate = (circuit) => {
|
|
81
|
+
if (circuit.totalRequests === 0) {
|
|
82
|
+
return 0;
|
|
83
|
+
}
|
|
84
|
+
return (circuit.failureCount / circuit.totalRequests) * 100;
|
|
85
|
+
};
|
|
86
|
+
/**
|
|
87
|
+
* Helper: Check if should reset failure count
|
|
88
|
+
*/
|
|
89
|
+
const shouldResetFailureCount = (circuit) => {
|
|
90
|
+
if (!circuit.lastFailureTime) {
|
|
91
|
+
return false;
|
|
92
|
+
}
|
|
93
|
+
const timeSinceLastFailure = Date.now() - circuit.lastFailureTime.getTime();
|
|
94
|
+
return timeSinceLastFailure > defaultConfig.resetTimeout;
|
|
95
|
+
};
|
|
96
|
+
/**
|
|
97
|
+
* Circuit Breaker - Sealed namespace
|
|
98
|
+
*/
|
|
99
|
+
export const CircuitBreaker = Object.freeze({
|
|
100
|
+
/**
|
|
101
|
+
* Initialize circuit breaker for a worker version
|
|
102
|
+
*/
|
|
103
|
+
initialize(workerName, version) {
|
|
104
|
+
const key = getCircuitKey(workerName, version);
|
|
105
|
+
if (circuits.has(key)) {
|
|
106
|
+
Logger.debug(`Circuit breaker already exists: ${key}`);
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
const circuit = {
|
|
110
|
+
workerName,
|
|
111
|
+
version,
|
|
112
|
+
state: 'closed',
|
|
113
|
+
failureCount: 0,
|
|
114
|
+
successCount: 0,
|
|
115
|
+
totalRequests: 0,
|
|
116
|
+
lastFailureTime: null,
|
|
117
|
+
lastSuccessTime: null,
|
|
118
|
+
lastStateChange: new Date(),
|
|
119
|
+
nextRetryTime: null,
|
|
120
|
+
errorRate: 0,
|
|
121
|
+
};
|
|
122
|
+
circuits.set(key, circuit);
|
|
123
|
+
Logger.info(`Circuit breaker initialized: ${key}`);
|
|
124
|
+
},
|
|
125
|
+
/**
|
|
126
|
+
* Check if circuit allows execution
|
|
127
|
+
*/
|
|
128
|
+
canExecute(workerName, version) {
|
|
129
|
+
const key = getCircuitKey(workerName, version);
|
|
130
|
+
const circuit = circuits.get(key);
|
|
131
|
+
if (!circuit) {
|
|
132
|
+
// No circuit exists, create and allow
|
|
133
|
+
CircuitBreaker.initialize(workerName, version);
|
|
134
|
+
return true;
|
|
135
|
+
}
|
|
136
|
+
const now = Date.now();
|
|
137
|
+
// Reset failure count if enough time has passed
|
|
138
|
+
if (circuit.state === 'closed' && shouldResetFailureCount(circuit)) {
|
|
139
|
+
circuit.failureCount = 0;
|
|
140
|
+
circuit.totalRequests = 0;
|
|
141
|
+
Logger.debug(`Reset failure count for ${key}`);
|
|
142
|
+
}
|
|
143
|
+
switch (circuit.state) {
|
|
144
|
+
case 'closed':
|
|
145
|
+
return true;
|
|
146
|
+
case 'open':
|
|
147
|
+
// Check if timeout has passed to try half-open
|
|
148
|
+
if (circuit.nextRetryTime && now >= circuit.nextRetryTime.getTime()) {
|
|
149
|
+
transitionState(circuit, 'half-open', 'Timeout elapsed, attempting recovery');
|
|
150
|
+
return true;
|
|
151
|
+
}
|
|
152
|
+
return false;
|
|
153
|
+
case 'half-open':
|
|
154
|
+
// Allow limited requests to test recovery
|
|
155
|
+
return true;
|
|
156
|
+
}
|
|
157
|
+
},
|
|
158
|
+
/**
|
|
159
|
+
* Record successful execution
|
|
160
|
+
*/
|
|
161
|
+
recordSuccess(workerName, version) {
|
|
162
|
+
const key = getCircuitKey(workerName, version);
|
|
163
|
+
const circuit = circuits.get(key);
|
|
164
|
+
if (!circuit) {
|
|
165
|
+
CircuitBreaker.initialize(workerName, version);
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
168
|
+
circuit.successCount++;
|
|
169
|
+
circuit.totalRequests++;
|
|
170
|
+
circuit.lastSuccessTime = new Date();
|
|
171
|
+
circuit.errorRate = calculateErrorRate(circuit);
|
|
172
|
+
recordEvent({
|
|
173
|
+
workerName,
|
|
174
|
+
version,
|
|
175
|
+
event: 'success',
|
|
176
|
+
state: circuit.state,
|
|
177
|
+
timestamp: new Date(),
|
|
178
|
+
});
|
|
179
|
+
// Transition based on state
|
|
180
|
+
if (circuit.state === 'half-open') {
|
|
181
|
+
if (circuit.successCount >= defaultConfig.successThreshold) {
|
|
182
|
+
transitionState(circuit, 'closed', `${circuit.successCount} consecutive successes`);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
},
|
|
186
|
+
/**
|
|
187
|
+
* Record failed execution
|
|
188
|
+
*/
|
|
189
|
+
recordFailure(workerName, version, error) {
|
|
190
|
+
const key = getCircuitKey(workerName, version);
|
|
191
|
+
const circuit = circuits.get(key);
|
|
192
|
+
if (!circuit) {
|
|
193
|
+
CircuitBreaker.initialize(workerName, version);
|
|
194
|
+
return;
|
|
195
|
+
}
|
|
196
|
+
circuit.failureCount++;
|
|
197
|
+
circuit.totalRequests++;
|
|
198
|
+
circuit.lastFailureTime = new Date();
|
|
199
|
+
circuit.errorRate = calculateErrorRate(circuit);
|
|
200
|
+
recordEvent({
|
|
201
|
+
workerName,
|
|
202
|
+
version,
|
|
203
|
+
event: 'failure',
|
|
204
|
+
state: circuit.state,
|
|
205
|
+
timestamp: new Date(),
|
|
206
|
+
error,
|
|
207
|
+
});
|
|
208
|
+
// Transition based on state and thresholds
|
|
209
|
+
if (circuit.state === 'half-open') {
|
|
210
|
+
// Any failure in half-open reopens the circuit
|
|
211
|
+
transitionState(circuit, 'open', 'Failure during recovery attempt');
|
|
212
|
+
}
|
|
213
|
+
else if (circuit.state === 'closed') {
|
|
214
|
+
// Check if should open based on failure threshold
|
|
215
|
+
if (circuit.totalRequests >= defaultConfig.volumeThreshold) {
|
|
216
|
+
if (circuit.failureCount >= defaultConfig.failureThreshold) {
|
|
217
|
+
transitionState(circuit, 'open', `Failure threshold exceeded: ${circuit.failureCount}/${defaultConfig.failureThreshold}`);
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
},
|
|
222
|
+
/**
|
|
223
|
+
* Record rejected execution (when circuit is open)
|
|
224
|
+
*/
|
|
225
|
+
recordRejection(workerName, version) {
|
|
226
|
+
const key = getCircuitKey(workerName, version);
|
|
227
|
+
const circuit = circuits.get(key);
|
|
228
|
+
if (!circuit) {
|
|
229
|
+
return;
|
|
230
|
+
}
|
|
231
|
+
recordEvent({
|
|
232
|
+
workerName,
|
|
233
|
+
version,
|
|
234
|
+
event: 'rejected',
|
|
235
|
+
state: circuit.state,
|
|
236
|
+
timestamp: new Date(),
|
|
237
|
+
reason: 'Circuit breaker is open',
|
|
238
|
+
});
|
|
239
|
+
},
|
|
240
|
+
/**
|
|
241
|
+
* Get circuit state
|
|
242
|
+
*/
|
|
243
|
+
getState(workerName, version) {
|
|
244
|
+
const key = getCircuitKey(workerName, version);
|
|
245
|
+
const circuit = circuits.get(key);
|
|
246
|
+
return circuit ? { ...circuit } : null;
|
|
247
|
+
},
|
|
248
|
+
/**
|
|
249
|
+
* Get all circuit states
|
|
250
|
+
*/
|
|
251
|
+
getAllStates() {
|
|
252
|
+
return Array.from(circuits.values()).map((circuit) => ({ ...circuit }));
|
|
253
|
+
},
|
|
254
|
+
/**
|
|
255
|
+
* Get circuit states by worker name (all versions)
|
|
256
|
+
*/
|
|
257
|
+
getStatesByWorker(workerName) {
|
|
258
|
+
const states = [];
|
|
259
|
+
for (const circuit of circuits.values()) {
|
|
260
|
+
if (circuit.workerName === workerName) {
|
|
261
|
+
states.push({ ...circuit });
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
return states;
|
|
265
|
+
},
|
|
266
|
+
/**
|
|
267
|
+
* Get event history
|
|
268
|
+
*/
|
|
269
|
+
getEventHistory(workerName, version, limit = 100) {
|
|
270
|
+
const key = getCircuitKey(workerName, version);
|
|
271
|
+
const history = eventHistory.get(key) ?? [];
|
|
272
|
+
return history.slice(-limit).map((event) => ({ ...event }));
|
|
273
|
+
},
|
|
274
|
+
/**
|
|
275
|
+
* Manually reset circuit to closed state
|
|
276
|
+
*/
|
|
277
|
+
reset(workerName, version) {
|
|
278
|
+
const key = getCircuitKey(workerName, version);
|
|
279
|
+
const circuit = circuits.get(key);
|
|
280
|
+
if (!circuit) {
|
|
281
|
+
Logger.warn(`Circuit breaker not found: ${key}`);
|
|
282
|
+
return;
|
|
283
|
+
}
|
|
284
|
+
transitionState(circuit, 'closed', 'Manual reset');
|
|
285
|
+
circuit.failureCount = 0;
|
|
286
|
+
circuit.successCount = 0;
|
|
287
|
+
circuit.totalRequests = 0;
|
|
288
|
+
circuit.errorRate = 0;
|
|
289
|
+
Logger.info(`Circuit breaker manually reset: ${key}`);
|
|
290
|
+
},
|
|
291
|
+
/**
|
|
292
|
+
* Manually force circuit to open state
|
|
293
|
+
*/
|
|
294
|
+
forceOpen(workerName, version, reason) {
|
|
295
|
+
const key = getCircuitKey(workerName, version);
|
|
296
|
+
const circuit = circuits.get(key);
|
|
297
|
+
if (!circuit) {
|
|
298
|
+
CircuitBreaker.initialize(workerName, version);
|
|
299
|
+
const newCircuit = circuits.get(key);
|
|
300
|
+
if (!newCircuit) {
|
|
301
|
+
return;
|
|
302
|
+
}
|
|
303
|
+
transitionState(newCircuit, 'open', `Forced open: ${reason}`);
|
|
304
|
+
return;
|
|
305
|
+
}
|
|
306
|
+
transitionState(circuit, 'open', `Forced open: ${reason}`);
|
|
307
|
+
Logger.warn(`Circuit breaker forced open: ${key}`, { reason });
|
|
308
|
+
},
|
|
309
|
+
/**
|
|
310
|
+
* Delete circuit breaker
|
|
311
|
+
*/
|
|
312
|
+
delete(workerName, version) {
|
|
313
|
+
const key = getCircuitKey(workerName, version);
|
|
314
|
+
circuits.delete(key);
|
|
315
|
+
eventHistory.delete(key);
|
|
316
|
+
Logger.info(`Circuit breaker deleted: ${key}`);
|
|
317
|
+
},
|
|
318
|
+
/**
|
|
319
|
+
* Delete all circuit breakers for a worker (all versions)
|
|
320
|
+
*/
|
|
321
|
+
deleteWorker(workerName) {
|
|
322
|
+
const keysToDelete = [];
|
|
323
|
+
for (const [key, circuit] of circuits.entries()) {
|
|
324
|
+
if (circuit.workerName === workerName) {
|
|
325
|
+
keysToDelete.push(key);
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
for (const key of keysToDelete) {
|
|
329
|
+
circuits.delete(key);
|
|
330
|
+
eventHistory.delete(key);
|
|
331
|
+
}
|
|
332
|
+
Logger.info(`Deleted all circuit breakers for worker: ${workerName}`, {
|
|
333
|
+
count: keysToDelete.length,
|
|
334
|
+
});
|
|
335
|
+
},
|
|
336
|
+
/**
|
|
337
|
+
* Get summary statistics
|
|
338
|
+
*/
|
|
339
|
+
getSummary() {
|
|
340
|
+
const summary = {
|
|
341
|
+
totalCircuits: circuits.size,
|
|
342
|
+
openCircuits: 0,
|
|
343
|
+
halfOpenCircuits: 0,
|
|
344
|
+
closedCircuits: 0,
|
|
345
|
+
circuitsByWorker: {},
|
|
346
|
+
};
|
|
347
|
+
for (const circuit of circuits.values()) {
|
|
348
|
+
switch (circuit.state) {
|
|
349
|
+
case 'open':
|
|
350
|
+
summary.openCircuits++;
|
|
351
|
+
break;
|
|
352
|
+
case 'half-open':
|
|
353
|
+
summary.halfOpenCircuits++;
|
|
354
|
+
break;
|
|
355
|
+
case 'closed':
|
|
356
|
+
summary.closedCircuits++;
|
|
357
|
+
break;
|
|
358
|
+
}
|
|
359
|
+
summary.circuitsByWorker[circuit.workerName] =
|
|
360
|
+
(summary.circuitsByWorker[circuit.workerName] || 0) + 1;
|
|
361
|
+
}
|
|
362
|
+
return summary;
|
|
363
|
+
},
|
|
364
|
+
/**
|
|
365
|
+
* Shutdown and clear all circuits
|
|
366
|
+
*/
|
|
367
|
+
shutdown() {
|
|
368
|
+
Logger.info('CircuitBreaker shutting down...');
|
|
369
|
+
circuits.clear();
|
|
370
|
+
eventHistory.clear();
|
|
371
|
+
Logger.info('CircuitBreaker shutdown complete');
|
|
372
|
+
},
|
|
373
|
+
});
|
|
374
|
+
// Graceful shutdown handled by WorkerShutdown
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cluster Lock Manager
|
|
3
|
+
* Distributed locking using Redis for multi-instance worker coordination
|
|
4
|
+
* Sealed namespace for immutability
|
|
5
|
+
*/
|
|
6
|
+
import { type RedisConfig } from '@zintrust/core';
|
|
7
|
+
export type LockAcquisitionOptions = {
|
|
8
|
+
lockKey: string;
|
|
9
|
+
ttl: number;
|
|
10
|
+
region?: string;
|
|
11
|
+
userId?: string;
|
|
12
|
+
};
|
|
13
|
+
export type LockInfo = {
|
|
14
|
+
lockKey: string;
|
|
15
|
+
instanceId: string;
|
|
16
|
+
acquiredAt: Date;
|
|
17
|
+
expiresAt: Date;
|
|
18
|
+
region: string;
|
|
19
|
+
userId?: string;
|
|
20
|
+
};
|
|
21
|
+
export type AuditLogEntry = {
|
|
22
|
+
timestamp: Date;
|
|
23
|
+
operation: 'acquire' | 'release' | 'extend' | 'force-release';
|
|
24
|
+
lockKey: string;
|
|
25
|
+
instanceId: string;
|
|
26
|
+
userId?: string;
|
|
27
|
+
reason?: string;
|
|
28
|
+
success: boolean;
|
|
29
|
+
};
|
|
30
|
+
/**
|
|
31
|
+
* Cluster Lock Manager - Sealed namespace
|
|
32
|
+
*/
|
|
33
|
+
export declare const ClusterLock: Readonly<{
|
|
34
|
+
/**
|
|
35
|
+
* Initialize the lock manager with Redis connection
|
|
36
|
+
*/
|
|
37
|
+
initialize(config: RedisConfig): void;
|
|
38
|
+
/**
|
|
39
|
+
* Acquire a distributed lock
|
|
40
|
+
*/
|
|
41
|
+
acquire(options: LockAcquisitionOptions): Promise<boolean>;
|
|
42
|
+
/**
|
|
43
|
+
* Release a distributed lock
|
|
44
|
+
*/
|
|
45
|
+
release(lockKey: string, userId?: string): Promise<boolean>;
|
|
46
|
+
/**
|
|
47
|
+
* Extend lock TTL
|
|
48
|
+
*/
|
|
49
|
+
extend(lockKey: string, ttl: number): Promise<boolean>;
|
|
50
|
+
/**
|
|
51
|
+
* Check if lock is held by this instance
|
|
52
|
+
*/
|
|
53
|
+
isHeldByMe(lockKey: string): Promise<boolean>;
|
|
54
|
+
/**
|
|
55
|
+
* Force release a lock (admin operation)
|
|
56
|
+
*/
|
|
57
|
+
forceRelease(lockKey: string, userId: string, reason: string): Promise<boolean>;
|
|
58
|
+
/**
|
|
59
|
+
* List all locks
|
|
60
|
+
*/
|
|
61
|
+
listLocks(): Promise<ReadonlyArray<{
|
|
62
|
+
key: string;
|
|
63
|
+
owner: string;
|
|
64
|
+
region?: string;
|
|
65
|
+
}>>;
|
|
66
|
+
/**
|
|
67
|
+
* Get lock owner
|
|
68
|
+
*/
|
|
69
|
+
getLockOwner(lockKey: string): Promise<string | null>;
|
|
70
|
+
/**
|
|
71
|
+
* Get locks by region
|
|
72
|
+
*/
|
|
73
|
+
getLocksByRegion(region: string): ReadonlyArray<LockInfo>;
|
|
74
|
+
/**
|
|
75
|
+
* Get audit log for a lock
|
|
76
|
+
*/
|
|
77
|
+
getAuditLog(lockKey: string, limit?: number): Promise<ReadonlyArray<AuditLogEntry>>;
|
|
78
|
+
/**
|
|
79
|
+
* Get active locks held by this instance
|
|
80
|
+
*/
|
|
81
|
+
getActiveLocks(): ReadonlyArray<LockInfo>;
|
|
82
|
+
/**
|
|
83
|
+
* Get instance ID
|
|
84
|
+
*/
|
|
85
|
+
getInstanceId(): string;
|
|
86
|
+
/**
|
|
87
|
+
* Shutdown and release all locks
|
|
88
|
+
*/
|
|
89
|
+
shutdown(): Promise<void>;
|
|
90
|
+
}>;
|