@naman_deep_singh/communication-core 1.0.0
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 +345 -0
- package/dist/cjs/abstract/BaseCircuitBreaker.js +253 -0
- package/dist/cjs/abstract/BaseClient.js +298 -0
- package/dist/cjs/abstract/BaseCompressionManager.js +377 -0
- package/dist/cjs/abstract/BaseConnectionPool.js +543 -0
- package/dist/cjs/abstract/BaseInterceptor.js +235 -0
- package/dist/cjs/abstract/BaseLoadBalancer.js +269 -0
- package/dist/cjs/abstract/BaseProtocol.js +269 -0
- package/dist/cjs/abstract/BaseRetryStrategy.js +255 -0
- package/dist/cjs/abstract/BaseSerializer.js +341 -0
- package/dist/cjs/abstract/BaseServiceDiscoverer.js +254 -0
- package/dist/cjs/abstract/BaseTimeoutManager.js +295 -0
- package/dist/cjs/abstract/index.js +25 -0
- package/dist/cjs/errors/CircuitBreakerError.js +16 -0
- package/dist/cjs/errors/CommunicationError.js +15 -0
- package/dist/cjs/errors/ConnectionError.js +15 -0
- package/dist/cjs/errors/DiscoveryError.js +15 -0
- package/dist/cjs/errors/LoadBalancerError.js +15 -0
- package/dist/cjs/errors/ProtocolError.js +15 -0
- package/dist/cjs/errors/RetryError.js +16 -0
- package/dist/cjs/errors/SerializationError.js +15 -0
- package/dist/cjs/errors/ServiceUnavailableError.js +15 -0
- package/dist/cjs/errors/TimeoutError.js +16 -0
- package/dist/cjs/errors/communicationErrorCodes.js +35 -0
- package/dist/cjs/errors/index.js +31 -0
- package/dist/cjs/index.js +38 -0
- package/dist/cjs/interfaces/CircuitBreaker.interface.js +6 -0
- package/dist/cjs/interfaces/Client.interface.js +6 -0
- package/dist/cjs/interfaces/Compression.interface.js +6 -0
- package/dist/cjs/interfaces/ConnectionPool.interface.js +6 -0
- package/dist/cjs/interfaces/Interceptor.interface.js +6 -0
- package/dist/cjs/interfaces/LoadBalancer.interface.js +2 -0
- package/dist/cjs/interfaces/Protocol.interface.js +6 -0
- package/dist/cjs/interfaces/RetryStrategy.interface.js +6 -0
- package/dist/cjs/interfaces/Serializer.interface.js +2 -0
- package/dist/cjs/interfaces/ServiceDiscovery.interface.js +6 -0
- package/dist/cjs/interfaces/Timeout.interface.js +6 -0
- package/dist/cjs/interfaces/index.js +6 -0
- package/dist/cjs/types/config.js +6 -0
- package/dist/cjs/types/events.js +6 -0
- package/dist/cjs/types/index.js +6 -0
- package/dist/cjs/types/request.js +6 -0
- package/dist/cjs/types/response.js +6 -0
- package/dist/cjs/types/service.js +6 -0
- package/dist/cjs/utils.js +200 -0
- package/dist/esm/abstract/BaseCircuitBreaker.js +249 -0
- package/dist/esm/abstract/BaseClient.js +294 -0
- package/dist/esm/abstract/BaseCompressionManager.js +373 -0
- package/dist/esm/abstract/BaseConnectionPool.js +539 -0
- package/dist/esm/abstract/BaseInterceptor.js +231 -0
- package/dist/esm/abstract/BaseLoadBalancer.js +265 -0
- package/dist/esm/abstract/BaseProtocol.js +265 -0
- package/dist/esm/abstract/BaseRetryStrategy.js +251 -0
- package/dist/esm/abstract/BaseSerializer.js +337 -0
- package/dist/esm/abstract/BaseServiceDiscoverer.js +250 -0
- package/dist/esm/abstract/BaseTimeoutManager.js +291 -0
- package/dist/esm/abstract/index.js +11 -0
- package/dist/esm/errors/CircuitBreakerError.js +12 -0
- package/dist/esm/errors/CommunicationError.js +11 -0
- package/dist/esm/errors/ConnectionError.js +11 -0
- package/dist/esm/errors/DiscoveryError.js +11 -0
- package/dist/esm/errors/LoadBalancerError.js +11 -0
- package/dist/esm/errors/ProtocolError.js +11 -0
- package/dist/esm/errors/RetryError.js +12 -0
- package/dist/esm/errors/SerializationError.js +11 -0
- package/dist/esm/errors/ServiceUnavailableError.js +11 -0
- package/dist/esm/errors/TimeoutError.js +12 -0
- package/dist/esm/errors/communicationErrorCodes.js +32 -0
- package/dist/esm/errors/index.js +17 -0
- package/dist/esm/index.js +18 -0
- package/dist/esm/interfaces/CircuitBreaker.interface.js +5 -0
- package/dist/esm/interfaces/Client.interface.js +5 -0
- package/dist/esm/interfaces/Compression.interface.js +5 -0
- package/dist/esm/interfaces/ConnectionPool.interface.js +5 -0
- package/dist/esm/interfaces/Interceptor.interface.js +5 -0
- package/dist/esm/interfaces/LoadBalancer.interface.js +1 -0
- package/dist/esm/interfaces/Protocol.interface.js +5 -0
- package/dist/esm/interfaces/RetryStrategy.interface.js +5 -0
- package/dist/esm/interfaces/Serializer.interface.js +1 -0
- package/dist/esm/interfaces/ServiceDiscovery.interface.js +5 -0
- package/dist/esm/interfaces/Timeout.interface.js +5 -0
- package/dist/esm/interfaces/index.js +5 -0
- package/dist/esm/types/config.js +5 -0
- package/dist/esm/types/events.js +5 -0
- package/dist/esm/types/index.js +5 -0
- package/dist/esm/types/request.js +5 -0
- package/dist/esm/types/response.js +5 -0
- package/dist/esm/types/service.js +5 -0
- package/dist/esm/utils.js +193 -0
- package/dist/types/abstract/BaseCircuitBreaker.d.ts +167 -0
- package/dist/types/abstract/BaseClient.d.ts +197 -0
- package/dist/types/abstract/BaseCompressionManager.d.ts +180 -0
- package/dist/types/abstract/BaseConnectionPool.d.ts +210 -0
- package/dist/types/abstract/BaseInterceptor.d.ts +150 -0
- package/dist/types/abstract/BaseLoadBalancer.d.ts +167 -0
- package/dist/types/abstract/BaseProtocol.d.ts +163 -0
- package/dist/types/abstract/BaseRetryStrategy.d.ts +130 -0
- package/dist/types/abstract/BaseSerializer.d.ts +181 -0
- package/dist/types/abstract/BaseServiceDiscoverer.d.ts +161 -0
- package/dist/types/abstract/BaseTimeoutManager.d.ts +145 -0
- package/dist/types/abstract/index.d.ts +11 -0
- package/dist/types/errors/CircuitBreakerError.d.ts +8 -0
- package/dist/types/errors/CommunicationError.d.ts +10 -0
- package/dist/types/errors/ConnectionError.d.ts +9 -0
- package/dist/types/errors/DiscoveryError.d.ts +9 -0
- package/dist/types/errors/LoadBalancerError.d.ts +9 -0
- package/dist/types/errors/ProtocolError.d.ts +9 -0
- package/dist/types/errors/RetryError.d.ts +11 -0
- package/dist/types/errors/SerializationError.d.ts +9 -0
- package/dist/types/errors/ServiceUnavailableError.d.ts +12 -0
- package/dist/types/errors/TimeoutError.d.ts +11 -0
- package/dist/types/errors/communicationErrorCodes.d.ts +27 -0
- package/dist/types/errors/index.d.ts +11 -0
- package/dist/types/index.d.ts +13 -0
- package/dist/types/interfaces/CircuitBreaker.interface.d.ts +150 -0
- package/dist/types/interfaces/Client.interface.d.ts +153 -0
- package/dist/types/interfaces/Compression.interface.d.ts +190 -0
- package/dist/types/interfaces/ConnectionPool.interface.d.ts +191 -0
- package/dist/types/interfaces/Interceptor.interface.d.ts +220 -0
- package/dist/types/interfaces/LoadBalancer.interface.d.ts +153 -0
- package/dist/types/interfaces/Protocol.interface.d.ts +117 -0
- package/dist/types/interfaces/RetryStrategy.interface.d.ts +160 -0
- package/dist/types/interfaces/Serializer.interface.d.ts +176 -0
- package/dist/types/interfaces/ServiceDiscovery.interface.d.ts +189 -0
- package/dist/types/interfaces/Timeout.interface.d.ts +135 -0
- package/dist/types/interfaces/index.d.ts +15 -0
- package/dist/types/types/config.d.ts +540 -0
- package/dist/types/types/events.d.ts +204 -0
- package/dist/types/types/index.d.ts +9 -0
- package/dist/types/types/request.d.ts +143 -0
- package/dist/types/types/response.d.ts +155 -0
- package/dist/types/types/service.d.ts +279 -0
- package/dist/types/utils.d.ts +179 -0
- package/package.json +88 -0
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Utilities integration with @naman_deep_singh/utils package
|
|
4
|
+
* @packageDocumentation
|
|
5
|
+
*/
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.CompressionUtils = exports.ConnectionPoolUtils = exports.TimeoutUtils = exports.BaseConnection = void 0;
|
|
8
|
+
const utils_1 = require("@naman_deep_singh/utils");
|
|
9
|
+
/**
|
|
10
|
+
* Base connection implementation that satisfies both interfaces
|
|
11
|
+
*/
|
|
12
|
+
class BaseConnection {
|
|
13
|
+
}
|
|
14
|
+
exports.BaseConnection = BaseConnection;
|
|
15
|
+
/**
|
|
16
|
+
* Timeout utilities wrapper
|
|
17
|
+
*/
|
|
18
|
+
class TimeoutUtils {
|
|
19
|
+
/**
|
|
20
|
+
* Execute with timeout
|
|
21
|
+
*/
|
|
22
|
+
static async withTimeout(promise, timeoutMs, errorMessage) {
|
|
23
|
+
return utils_1.TimeoutManager.withTimeout(promise, timeoutMs, errorMessage);
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Create delay
|
|
27
|
+
*/
|
|
28
|
+
static async delay(ms) {
|
|
29
|
+
return utils_1.TimeoutManager.delay(ms);
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Retry with timeout
|
|
33
|
+
*/
|
|
34
|
+
static async retryWithTimeout(fn, options) {
|
|
35
|
+
return utils_1.TimeoutManager.retryWithTimeout(fn, options);
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Create a timeout promise
|
|
39
|
+
*/
|
|
40
|
+
static createTimeout(timeoutMs, message) {
|
|
41
|
+
return new Promise((_, reject) => {
|
|
42
|
+
setTimeout(() => {
|
|
43
|
+
reject(new Error(message || `Timeout after ${timeoutMs}ms`));
|
|
44
|
+
}, timeoutMs);
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Create an abort signal for timeout
|
|
49
|
+
*/
|
|
50
|
+
static createAbortSignal(timeoutMs) {
|
|
51
|
+
const controller = new AbortController();
|
|
52
|
+
setTimeout(() => controller.abort(), timeoutMs);
|
|
53
|
+
return controller.signal;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Race promises with timeout
|
|
57
|
+
*/
|
|
58
|
+
static async raceWithTimeout(promises, timeoutMs, errorMessage) {
|
|
59
|
+
const timeoutPromise = this.createTimeout(timeoutMs, errorMessage);
|
|
60
|
+
return Promise.race([...promises, timeoutPromise]);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
exports.TimeoutUtils = TimeoutUtils;
|
|
64
|
+
/**
|
|
65
|
+
* Connection pool wrapper
|
|
66
|
+
*/
|
|
67
|
+
class ConnectionPoolUtils {
|
|
68
|
+
/**
|
|
69
|
+
* Create a generic connection pool
|
|
70
|
+
*/
|
|
71
|
+
static createPool(config) {
|
|
72
|
+
return new utils_1.GenericPool({
|
|
73
|
+
name: config.name,
|
|
74
|
+
minConnections: config.minConnections,
|
|
75
|
+
maxConnections: config.maxConnections,
|
|
76
|
+
createConnection: config.createConnection,
|
|
77
|
+
validateConnection: config.validateConnection,
|
|
78
|
+
idleTimeoutMs: config.idleTimeout,
|
|
79
|
+
maxLifetimeMs: config.maxLifetime,
|
|
80
|
+
acquireTimeoutMs: config.acquireTimeout,
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Create a simple pool with basic configuration
|
|
85
|
+
*/
|
|
86
|
+
static createSimplePool(name, createConnection, options) {
|
|
87
|
+
return new utils_1.GenericPool({
|
|
88
|
+
name,
|
|
89
|
+
minConnections: options?.minConnections,
|
|
90
|
+
maxConnections: options?.maxConnections,
|
|
91
|
+
createConnection,
|
|
92
|
+
idleTimeoutMs: options?.idleTimeout,
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Create an adapter to convert property-based isHealthy to function-based
|
|
97
|
+
*/
|
|
98
|
+
static createConnectionAdapter(connection, closeFn = async () => { }, healthCheckFn = async () => true, resetFn = async () => { }) {
|
|
99
|
+
// Handle isHealthy conversion
|
|
100
|
+
const getIsHealthy = () => {
|
|
101
|
+
if (typeof connection.isHealthy === 'function') {
|
|
102
|
+
return connection.isHealthy();
|
|
103
|
+
}
|
|
104
|
+
return connection.isHealthy ?? true;
|
|
105
|
+
};
|
|
106
|
+
return {
|
|
107
|
+
id: connection.id || `conn-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`,
|
|
108
|
+
// isHealthy must be a function
|
|
109
|
+
isHealthy: getIsHealthy,
|
|
110
|
+
createdAt: connection.createdAt || Date.now(),
|
|
111
|
+
lastUsedAt: connection.lastUsedAt || Date.now(),
|
|
112
|
+
usageCount: connection.usageCount || 0,
|
|
113
|
+
metadata: connection.metadata || {},
|
|
114
|
+
close: closeFn,
|
|
115
|
+
healthCheck: healthCheckFn,
|
|
116
|
+
reset: resetFn,
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
exports.ConnectionPoolUtils = ConnectionPoolUtils;
|
|
121
|
+
/**
|
|
122
|
+
* Compression utilities wrapper
|
|
123
|
+
*/
|
|
124
|
+
class CompressionUtils {
|
|
125
|
+
/**
|
|
126
|
+
* Compress data
|
|
127
|
+
*/
|
|
128
|
+
static async compress(data, options) {
|
|
129
|
+
const result = await utils_1.Compression.compress(Buffer.from(data), {
|
|
130
|
+
algorithm: options?.algorithm || 'gzip',
|
|
131
|
+
level: options?.level || 6,
|
|
132
|
+
});
|
|
133
|
+
return Buffer.from(result);
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Decompress data
|
|
137
|
+
*/
|
|
138
|
+
static async decompress(data, options) {
|
|
139
|
+
const result = await utils_1.Compression.decompress(Buffer.from(data), {
|
|
140
|
+
algorithm: options?.algorithm || 'gzip',
|
|
141
|
+
});
|
|
142
|
+
return Buffer.from(result);
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* Compress data with metrics
|
|
146
|
+
*/
|
|
147
|
+
static async compressWithMetrics(data, options) {
|
|
148
|
+
const startTime = Date.now();
|
|
149
|
+
const originalSize = Buffer.from(data).length;
|
|
150
|
+
const compressed = await utils_1.Compression.compress(Buffer.from(data), {
|
|
151
|
+
algorithm: options?.algorithm || 'gzip',
|
|
152
|
+
level: options?.level || 6,
|
|
153
|
+
});
|
|
154
|
+
const compressionTime = Date.now() - startTime;
|
|
155
|
+
const compressedSize = compressed.length;
|
|
156
|
+
const compressionRatio = compressedSize / originalSize;
|
|
157
|
+
return {
|
|
158
|
+
data: Buffer.from(compressed),
|
|
159
|
+
originalSize,
|
|
160
|
+
compressedSize,
|
|
161
|
+
compressionRatio,
|
|
162
|
+
compressionTime,
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* Get optimal compression level based on data size
|
|
167
|
+
*/
|
|
168
|
+
static getOptimalLevel(dataSize) {
|
|
169
|
+
if (dataSize < 1024)
|
|
170
|
+
return 1; // Small data, fast compression
|
|
171
|
+
if (dataSize < 10240)
|
|
172
|
+
return 6; // Medium data, balanced
|
|
173
|
+
return 9; // Large data, best compression
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* Estimate compression ratio for data type
|
|
177
|
+
*/
|
|
178
|
+
static estimateCompressionRatio(contentType) {
|
|
179
|
+
if (!contentType)
|
|
180
|
+
return 0.7;
|
|
181
|
+
const type = contentType.toLowerCase();
|
|
182
|
+
if (type.includes('json') || type.includes('xml'))
|
|
183
|
+
return 0.3;
|
|
184
|
+
if (type.includes('text'))
|
|
185
|
+
return 0.4;
|
|
186
|
+
if (type.includes('image') || type.includes('video'))
|
|
187
|
+
return 0.95;
|
|
188
|
+
return 0.7;
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
exports.CompressionUtils = CompressionUtils;
|
|
192
|
+
/**
|
|
193
|
+
* Compression algorithm type
|
|
194
|
+
*/
|
|
195
|
+
CompressionUtils.Algorithm = {
|
|
196
|
+
GZIP: 'gzip',
|
|
197
|
+
DEFLATE: 'deflate',
|
|
198
|
+
BROTLI: 'brotli',
|
|
199
|
+
NONE: 'none',
|
|
200
|
+
};
|
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Abstract base circuit breaker implementation
|
|
3
|
+
* @packageDocumentation
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* Abstract base circuit breaker implementation
|
|
7
|
+
* Provides common functionality for all circuit breaker implementations
|
|
8
|
+
*/
|
|
9
|
+
export class BaseCircuitBreaker {
|
|
10
|
+
/**
|
|
11
|
+
* Create a new base circuit breaker instance
|
|
12
|
+
* @param name Circuit breaker name
|
|
13
|
+
* @param config Circuit breaker configuration
|
|
14
|
+
*/
|
|
15
|
+
constructor(name, config) {
|
|
16
|
+
/** Current state */
|
|
17
|
+
this.state = 'closed';
|
|
18
|
+
/** Failure count */
|
|
19
|
+
this.failureCount = 0;
|
|
20
|
+
/** Success count */
|
|
21
|
+
this.successCount = 0;
|
|
22
|
+
/** Circuit breaker statistics */
|
|
23
|
+
this.stats = {
|
|
24
|
+
totalExecutions: 0,
|
|
25
|
+
totalSuccesses: 0,
|
|
26
|
+
totalFailures: 0,
|
|
27
|
+
successRate: 0,
|
|
28
|
+
failureRate: 0,
|
|
29
|
+
totalTimeOpen: 0,
|
|
30
|
+
totalTimeClosed: 0,
|
|
31
|
+
lastStateChange: Date.now(),
|
|
32
|
+
};
|
|
33
|
+
/** Time when current state was entered */
|
|
34
|
+
this.stateEnteredTime = Date.now();
|
|
35
|
+
this.name = name;
|
|
36
|
+
this.config = { ...config };
|
|
37
|
+
this.initialize();
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Initialize circuit breaker
|
|
41
|
+
*/
|
|
42
|
+
initialize() {
|
|
43
|
+
// Can be overridden by subclasses
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Check if circuit breaker should allow execution
|
|
47
|
+
* @returns True if execution is allowed
|
|
48
|
+
*/
|
|
49
|
+
shouldAllowExecution() {
|
|
50
|
+
switch (this.state) {
|
|
51
|
+
case 'closed':
|
|
52
|
+
return true;
|
|
53
|
+
case 'open':
|
|
54
|
+
if (this.nextResetTime && Date.now() >= this.nextResetTime) {
|
|
55
|
+
this.transitionToHalfOpen();
|
|
56
|
+
return true;
|
|
57
|
+
}
|
|
58
|
+
return false;
|
|
59
|
+
case 'half-open':
|
|
60
|
+
return this.failureCount < (this.config.halfOpenMaxAttempts || 1);
|
|
61
|
+
default:
|
|
62
|
+
return false;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Transition circuit breaker to closed state
|
|
67
|
+
*/
|
|
68
|
+
transitionToClosed() {
|
|
69
|
+
const previousState = this.state;
|
|
70
|
+
this.state = 'closed';
|
|
71
|
+
this.failureCount = 0;
|
|
72
|
+
this.successCount = 0;
|
|
73
|
+
this.nextResetTime = undefined;
|
|
74
|
+
this.updateStateTime(previousState);
|
|
75
|
+
this.onStateChange('closed', previousState);
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Transition circuit breaker to open state
|
|
79
|
+
* @param error Optional error that caused the transition
|
|
80
|
+
*/
|
|
81
|
+
transitionToOpen(error) {
|
|
82
|
+
const previousState = this.state;
|
|
83
|
+
this.state = 'open';
|
|
84
|
+
this.nextResetTime = Date.now() + (this.config.resetTimeout || 30000);
|
|
85
|
+
this.updateStateTime(previousState);
|
|
86
|
+
this.onStateChange('open', previousState, error);
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Transition circuit breaker to half-open state
|
|
90
|
+
*/
|
|
91
|
+
transitionToHalfOpen() {
|
|
92
|
+
const previousState = this.state;
|
|
93
|
+
this.state = 'half-open';
|
|
94
|
+
this.failureCount = 0;
|
|
95
|
+
this.successCount = 0;
|
|
96
|
+
this.updateStateTime(previousState);
|
|
97
|
+
this.onStateChange('half-open', previousState);
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Update state timing statistics
|
|
101
|
+
* @param previousState Previous state
|
|
102
|
+
*/
|
|
103
|
+
updateStateTime(previousState) {
|
|
104
|
+
const now = Date.now();
|
|
105
|
+
const timeInState = now - this.stateEnteredTime;
|
|
106
|
+
if (previousState === 'open') {
|
|
107
|
+
this.stats.totalTimeOpen += timeInState;
|
|
108
|
+
}
|
|
109
|
+
else if (previousState === 'closed') {
|
|
110
|
+
this.stats.totalTimeClosed += timeInState;
|
|
111
|
+
}
|
|
112
|
+
this.stateEnteredTime = now;
|
|
113
|
+
this.stats.lastStateChange = now;
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Hook for state changes
|
|
117
|
+
* @param newState New state
|
|
118
|
+
* @param previousState Previous state
|
|
119
|
+
* @param error Optional error that caused state change
|
|
120
|
+
*/
|
|
121
|
+
onStateChange(newState, previousState, error) {
|
|
122
|
+
// Can be overridden by subclasses
|
|
123
|
+
// Emit events, log, etc.
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Check if failure threshold is reached
|
|
127
|
+
* @returns True if failure threshold reached
|
|
128
|
+
*/
|
|
129
|
+
isFailureThresholdReached() {
|
|
130
|
+
return this.failureCount >= (this.config.failureThreshold || 5);
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Check if success threshold is reached
|
|
134
|
+
* @returns True if success threshold reached
|
|
135
|
+
*/
|
|
136
|
+
isSuccessThresholdReached() {
|
|
137
|
+
return this.successCount >= (this.config.successThreshold || 1);
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Manually trip the circuit breaker to open state
|
|
141
|
+
* @param error Error that caused the trip
|
|
142
|
+
*/
|
|
143
|
+
trip(error) {
|
|
144
|
+
this.transitionToOpen(error);
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* Manually reset the circuit breaker to closed state
|
|
148
|
+
*/
|
|
149
|
+
reset() {
|
|
150
|
+
this.transitionToClosed();
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Check if circuit breaker is currently open
|
|
154
|
+
*/
|
|
155
|
+
isOpen() {
|
|
156
|
+
return this.state === 'open';
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* Check if circuit breaker is currently closed
|
|
160
|
+
*/
|
|
161
|
+
isClosed() {
|
|
162
|
+
return this.state === 'closed';
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* Check if circuit breaker is in half-open state
|
|
166
|
+
*/
|
|
167
|
+
isHalfOpen() {
|
|
168
|
+
return this.state === 'half-open';
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* Record a successful execution
|
|
172
|
+
*/
|
|
173
|
+
recordSuccess() {
|
|
174
|
+
this.successCount++;
|
|
175
|
+
this.lastSuccessTime = Date.now();
|
|
176
|
+
this.stats.totalSuccesses++;
|
|
177
|
+
if (this.state === 'half-open' && this.isSuccessThresholdReached()) {
|
|
178
|
+
this.transitionToClosed();
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
/**
|
|
182
|
+
* Record a failed execution
|
|
183
|
+
* @param error Error that occurred
|
|
184
|
+
*/
|
|
185
|
+
recordFailure(error) {
|
|
186
|
+
this.failureCount++;
|
|
187
|
+
this.lastFailureTime = Date.now();
|
|
188
|
+
this.stats.totalFailures++;
|
|
189
|
+
if (this.state === 'half-open') {
|
|
190
|
+
this.transitionToOpen(error);
|
|
191
|
+
}
|
|
192
|
+
else if (this.state === 'closed' && this.isFailureThresholdReached()) {
|
|
193
|
+
this.transitionToOpen(error);
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
/**
|
|
197
|
+
* Get circuit breaker statistics
|
|
198
|
+
*/
|
|
199
|
+
getStats() {
|
|
200
|
+
const total = this.stats.totalSuccesses + this.stats.totalFailures;
|
|
201
|
+
return {
|
|
202
|
+
...this.stats,
|
|
203
|
+
successRate: total > 0 ? this.stats.totalSuccesses / total : 0,
|
|
204
|
+
failureRate: total > 0 ? this.stats.totalFailures / total : 0,
|
|
205
|
+
};
|
|
206
|
+
}
|
|
207
|
+
/**
|
|
208
|
+
* Reset circuit breaker statistics
|
|
209
|
+
*/
|
|
210
|
+
resetStats() {
|
|
211
|
+
this.stats = {
|
|
212
|
+
totalExecutions: 0,
|
|
213
|
+
totalSuccesses: 0,
|
|
214
|
+
totalFailures: 0,
|
|
215
|
+
successRate: 0,
|
|
216
|
+
failureRate: 0,
|
|
217
|
+
totalTimeOpen: 0,
|
|
218
|
+
totalTimeClosed: 0,
|
|
219
|
+
lastStateChange: Date.now(),
|
|
220
|
+
};
|
|
221
|
+
}
|
|
222
|
+
/**
|
|
223
|
+
* Update circuit breaker configuration
|
|
224
|
+
* @param config New configuration
|
|
225
|
+
*/
|
|
226
|
+
updateConfig(config) {
|
|
227
|
+
this.config = { ...this.config, ...config };
|
|
228
|
+
}
|
|
229
|
+
/**
|
|
230
|
+
* Health check for the circuit breaker
|
|
231
|
+
*/
|
|
232
|
+
healthCheck() {
|
|
233
|
+
const healthy = this.state === 'closed' || this.state === 'half-open';
|
|
234
|
+
return {
|
|
235
|
+
healthy,
|
|
236
|
+
state: this.state,
|
|
237
|
+
message: healthy ? 'Circuit breaker is operational' : 'Circuit breaker is open',
|
|
238
|
+
details: {
|
|
239
|
+
name: this.name,
|
|
240
|
+
failureCount: this.failureCount,
|
|
241
|
+
successCount: this.successCount,
|
|
242
|
+
lastFailureTime: this.lastFailureTime,
|
|
243
|
+
lastSuccessTime: this.lastSuccessTime,
|
|
244
|
+
nextResetTime: this.nextResetTime,
|
|
245
|
+
config: this.config,
|
|
246
|
+
},
|
|
247
|
+
};
|
|
248
|
+
}
|
|
249
|
+
}
|