@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.
Files changed (134) hide show
  1. package/README.md +345 -0
  2. package/dist/cjs/abstract/BaseCircuitBreaker.js +253 -0
  3. package/dist/cjs/abstract/BaseClient.js +298 -0
  4. package/dist/cjs/abstract/BaseCompressionManager.js +377 -0
  5. package/dist/cjs/abstract/BaseConnectionPool.js +543 -0
  6. package/dist/cjs/abstract/BaseInterceptor.js +235 -0
  7. package/dist/cjs/abstract/BaseLoadBalancer.js +269 -0
  8. package/dist/cjs/abstract/BaseProtocol.js +269 -0
  9. package/dist/cjs/abstract/BaseRetryStrategy.js +255 -0
  10. package/dist/cjs/abstract/BaseSerializer.js +341 -0
  11. package/dist/cjs/abstract/BaseServiceDiscoverer.js +254 -0
  12. package/dist/cjs/abstract/BaseTimeoutManager.js +295 -0
  13. package/dist/cjs/abstract/index.js +25 -0
  14. package/dist/cjs/errors/CircuitBreakerError.js +16 -0
  15. package/dist/cjs/errors/CommunicationError.js +15 -0
  16. package/dist/cjs/errors/ConnectionError.js +15 -0
  17. package/dist/cjs/errors/DiscoveryError.js +15 -0
  18. package/dist/cjs/errors/LoadBalancerError.js +15 -0
  19. package/dist/cjs/errors/ProtocolError.js +15 -0
  20. package/dist/cjs/errors/RetryError.js +16 -0
  21. package/dist/cjs/errors/SerializationError.js +15 -0
  22. package/dist/cjs/errors/ServiceUnavailableError.js +15 -0
  23. package/dist/cjs/errors/TimeoutError.js +16 -0
  24. package/dist/cjs/errors/communicationErrorCodes.js +35 -0
  25. package/dist/cjs/errors/index.js +31 -0
  26. package/dist/cjs/index.js +38 -0
  27. package/dist/cjs/interfaces/CircuitBreaker.interface.js +6 -0
  28. package/dist/cjs/interfaces/Client.interface.js +6 -0
  29. package/dist/cjs/interfaces/Compression.interface.js +6 -0
  30. package/dist/cjs/interfaces/ConnectionPool.interface.js +6 -0
  31. package/dist/cjs/interfaces/Interceptor.interface.js +6 -0
  32. package/dist/cjs/interfaces/LoadBalancer.interface.js +2 -0
  33. package/dist/cjs/interfaces/Protocol.interface.js +6 -0
  34. package/dist/cjs/interfaces/RetryStrategy.interface.js +6 -0
  35. package/dist/cjs/interfaces/Serializer.interface.js +2 -0
  36. package/dist/cjs/interfaces/ServiceDiscovery.interface.js +6 -0
  37. package/dist/cjs/interfaces/Timeout.interface.js +6 -0
  38. package/dist/cjs/interfaces/index.js +6 -0
  39. package/dist/cjs/types/config.js +6 -0
  40. package/dist/cjs/types/events.js +6 -0
  41. package/dist/cjs/types/index.js +6 -0
  42. package/dist/cjs/types/request.js +6 -0
  43. package/dist/cjs/types/response.js +6 -0
  44. package/dist/cjs/types/service.js +6 -0
  45. package/dist/cjs/utils.js +200 -0
  46. package/dist/esm/abstract/BaseCircuitBreaker.js +249 -0
  47. package/dist/esm/abstract/BaseClient.js +294 -0
  48. package/dist/esm/abstract/BaseCompressionManager.js +373 -0
  49. package/dist/esm/abstract/BaseConnectionPool.js +539 -0
  50. package/dist/esm/abstract/BaseInterceptor.js +231 -0
  51. package/dist/esm/abstract/BaseLoadBalancer.js +265 -0
  52. package/dist/esm/abstract/BaseProtocol.js +265 -0
  53. package/dist/esm/abstract/BaseRetryStrategy.js +251 -0
  54. package/dist/esm/abstract/BaseSerializer.js +337 -0
  55. package/dist/esm/abstract/BaseServiceDiscoverer.js +250 -0
  56. package/dist/esm/abstract/BaseTimeoutManager.js +291 -0
  57. package/dist/esm/abstract/index.js +11 -0
  58. package/dist/esm/errors/CircuitBreakerError.js +12 -0
  59. package/dist/esm/errors/CommunicationError.js +11 -0
  60. package/dist/esm/errors/ConnectionError.js +11 -0
  61. package/dist/esm/errors/DiscoveryError.js +11 -0
  62. package/dist/esm/errors/LoadBalancerError.js +11 -0
  63. package/dist/esm/errors/ProtocolError.js +11 -0
  64. package/dist/esm/errors/RetryError.js +12 -0
  65. package/dist/esm/errors/SerializationError.js +11 -0
  66. package/dist/esm/errors/ServiceUnavailableError.js +11 -0
  67. package/dist/esm/errors/TimeoutError.js +12 -0
  68. package/dist/esm/errors/communicationErrorCodes.js +32 -0
  69. package/dist/esm/errors/index.js +17 -0
  70. package/dist/esm/index.js +18 -0
  71. package/dist/esm/interfaces/CircuitBreaker.interface.js +5 -0
  72. package/dist/esm/interfaces/Client.interface.js +5 -0
  73. package/dist/esm/interfaces/Compression.interface.js +5 -0
  74. package/dist/esm/interfaces/ConnectionPool.interface.js +5 -0
  75. package/dist/esm/interfaces/Interceptor.interface.js +5 -0
  76. package/dist/esm/interfaces/LoadBalancer.interface.js +1 -0
  77. package/dist/esm/interfaces/Protocol.interface.js +5 -0
  78. package/dist/esm/interfaces/RetryStrategy.interface.js +5 -0
  79. package/dist/esm/interfaces/Serializer.interface.js +1 -0
  80. package/dist/esm/interfaces/ServiceDiscovery.interface.js +5 -0
  81. package/dist/esm/interfaces/Timeout.interface.js +5 -0
  82. package/dist/esm/interfaces/index.js +5 -0
  83. package/dist/esm/types/config.js +5 -0
  84. package/dist/esm/types/events.js +5 -0
  85. package/dist/esm/types/index.js +5 -0
  86. package/dist/esm/types/request.js +5 -0
  87. package/dist/esm/types/response.js +5 -0
  88. package/dist/esm/types/service.js +5 -0
  89. package/dist/esm/utils.js +193 -0
  90. package/dist/types/abstract/BaseCircuitBreaker.d.ts +167 -0
  91. package/dist/types/abstract/BaseClient.d.ts +197 -0
  92. package/dist/types/abstract/BaseCompressionManager.d.ts +180 -0
  93. package/dist/types/abstract/BaseConnectionPool.d.ts +210 -0
  94. package/dist/types/abstract/BaseInterceptor.d.ts +150 -0
  95. package/dist/types/abstract/BaseLoadBalancer.d.ts +167 -0
  96. package/dist/types/abstract/BaseProtocol.d.ts +163 -0
  97. package/dist/types/abstract/BaseRetryStrategy.d.ts +130 -0
  98. package/dist/types/abstract/BaseSerializer.d.ts +181 -0
  99. package/dist/types/abstract/BaseServiceDiscoverer.d.ts +161 -0
  100. package/dist/types/abstract/BaseTimeoutManager.d.ts +145 -0
  101. package/dist/types/abstract/index.d.ts +11 -0
  102. package/dist/types/errors/CircuitBreakerError.d.ts +8 -0
  103. package/dist/types/errors/CommunicationError.d.ts +10 -0
  104. package/dist/types/errors/ConnectionError.d.ts +9 -0
  105. package/dist/types/errors/DiscoveryError.d.ts +9 -0
  106. package/dist/types/errors/LoadBalancerError.d.ts +9 -0
  107. package/dist/types/errors/ProtocolError.d.ts +9 -0
  108. package/dist/types/errors/RetryError.d.ts +11 -0
  109. package/dist/types/errors/SerializationError.d.ts +9 -0
  110. package/dist/types/errors/ServiceUnavailableError.d.ts +12 -0
  111. package/dist/types/errors/TimeoutError.d.ts +11 -0
  112. package/dist/types/errors/communicationErrorCodes.d.ts +27 -0
  113. package/dist/types/errors/index.d.ts +11 -0
  114. package/dist/types/index.d.ts +13 -0
  115. package/dist/types/interfaces/CircuitBreaker.interface.d.ts +150 -0
  116. package/dist/types/interfaces/Client.interface.d.ts +153 -0
  117. package/dist/types/interfaces/Compression.interface.d.ts +190 -0
  118. package/dist/types/interfaces/ConnectionPool.interface.d.ts +191 -0
  119. package/dist/types/interfaces/Interceptor.interface.d.ts +220 -0
  120. package/dist/types/interfaces/LoadBalancer.interface.d.ts +153 -0
  121. package/dist/types/interfaces/Protocol.interface.d.ts +117 -0
  122. package/dist/types/interfaces/RetryStrategy.interface.d.ts +160 -0
  123. package/dist/types/interfaces/Serializer.interface.d.ts +176 -0
  124. package/dist/types/interfaces/ServiceDiscovery.interface.d.ts +189 -0
  125. package/dist/types/interfaces/Timeout.interface.d.ts +135 -0
  126. package/dist/types/interfaces/index.d.ts +15 -0
  127. package/dist/types/types/config.d.ts +540 -0
  128. package/dist/types/types/events.d.ts +204 -0
  129. package/dist/types/types/index.d.ts +9 -0
  130. package/dist/types/types/request.d.ts +143 -0
  131. package/dist/types/types/response.d.ts +155 -0
  132. package/dist/types/types/service.d.ts +279 -0
  133. package/dist/types/utils.d.ts +179 -0
  134. package/package.json +88 -0
@@ -0,0 +1,539 @@
1
+ /**
2
+ * Abstract base connection pool implementation
3
+ * @packageDocumentation
4
+ */
5
+ import { CommunicationError } from '../errors/CommunicationError.js';
6
+ import { COMMUNICATION_ERROR_CODES } from '../errors/communicationErrorCodes.js';
7
+ /**
8
+ * Abstract base connection pool implementation
9
+ * Provides common functionality for connection pooling
10
+ */
11
+ export class BaseConnectionPool {
12
+ /**
13
+ * Create a new base connection pool instance
14
+ * @param name Pool name
15
+ * @param config Pool configuration
16
+ * @param createConnection Connection factory function
17
+ * @param validateConnection Connection validation function
18
+ */
19
+ constructor(name, config, createConnection, validateConnection) {
20
+ /** Is pool healthy? */
21
+ this.isHealthy = true;
22
+ /** Total connections in pool */
23
+ this.totalConnections = 0;
24
+ /** Active connections */
25
+ this.activeConnections = 0;
26
+ /** Idle connections */
27
+ this.idleConnections = 0;
28
+ /** Waiting clients */
29
+ this.waitingClients = 0;
30
+ /** Connection pool */
31
+ this.pool = new Set();
32
+ /** Active connections set */
33
+ this.activeConnectionsSet = new Set();
34
+ /** Idle connections queue */
35
+ this.idleConnectionsQueue = [];
36
+ /** Waiting clients queue */
37
+ this.waitingQueue = [];
38
+ /** Pool statistics */
39
+ this.stats = {
40
+ totalAcquisitions: 0,
41
+ totalReleases: 0,
42
+ totalDestructions: 0,
43
+ totalErrors: 0,
44
+ totalAcquisitionTime: 0,
45
+ maxAcquisitionTime: 0,
46
+ connectionStats: new Map(),
47
+ };
48
+ this.name = name;
49
+ this.config = { ...config };
50
+ this.createConnectionFn = createConnection;
51
+ this.validateConnectionFn = validateConnection || (async (conn) => conn.isHealthy());
52
+ this.initialize();
53
+ }
54
+ /**
55
+ * Initialize connection pool
56
+ */
57
+ async initialize() {
58
+ // Set up cleanup interval
59
+ if (this.config.validationInterval) {
60
+ this.cleanupInterval = setInterval(() => this.cleanupIdleConnections(), this.config.validationInterval);
61
+ }
62
+ // Set up health check interval
63
+ if (this.config.validationInterval) {
64
+ this.healthCheckInterval = setInterval(() => this.checkPoolHealth(), this.config.validationInterval);
65
+ }
66
+ // Warm up pool if configured
67
+ if (this.config.warmup && this.config.minConnections) {
68
+ await this.warmupPool();
69
+ }
70
+ }
71
+ /**
72
+ * Warm up the pool with minimum connections
73
+ */
74
+ async warmupPool() {
75
+ const minConnections = this.config.minConnections || 0;
76
+ const connectionsToCreate = Math.max(0, minConnections - this.totalConnections);
77
+ for (let i = 0; i < connectionsToCreate; i++) {
78
+ try {
79
+ const connection = await this.createConnection();
80
+ this.idleConnectionsQueue.push(connection);
81
+ this.pool.add(connection);
82
+ this.totalConnections++;
83
+ this.idleConnections++;
84
+ }
85
+ catch (error) {
86
+ // Log error but continue
87
+ this.stats.totalErrors++;
88
+ }
89
+ }
90
+ }
91
+ /**
92
+ * Acquire a connection from the pool
93
+ * @param timeout Acquisition timeout in milliseconds
94
+ * @returns Promise resolving to connection acquisition
95
+ * @throws {CommunicationError} If timeout or pool exhausted
96
+ */
97
+ async acquire(timeout) {
98
+ const startTime = Date.now();
99
+ const acquireTimeout = timeout || this.config.acquireTimeout || 10000;
100
+ // Check if we can create a new connection
101
+ if (this.activeConnections < (this.config.maxConnections || 10)) {
102
+ const connection = await this.getOrCreateConnection();
103
+ if (connection) {
104
+ const waitTime = Date.now() - startTime;
105
+ this.recordAcquisition(startTime, connection);
106
+ return this.createAcquisitionResult(connection, waitTime);
107
+ }
108
+ }
109
+ // If we can't create a new connection, wait for one to become available
110
+ return new Promise((resolve, reject) => {
111
+ const timer = setTimeout(() => {
112
+ this.removeWaitingClient(reject, startTime);
113
+ reject(new CommunicationError(COMMUNICATION_ERROR_CODES.CONNECTION_TIMEOUT, 503, {
114
+ message: `Connection acquisition timeout after ${acquireTimeout}ms`,
115
+ pool: this.name,
116
+ waitingClients: this.waitingClients,
117
+ activeConnections: this.activeConnections,
118
+ }));
119
+ }, acquireTimeout);
120
+ this.waitingQueue.push({
121
+ resolve: (acquisition) => {
122
+ clearTimeout(timer);
123
+ resolve(acquisition);
124
+ },
125
+ reject: (error) => {
126
+ clearTimeout(timer);
127
+ reject(error);
128
+ },
129
+ timeout: timer,
130
+ startTime,
131
+ });
132
+ this.waitingClients++;
133
+ });
134
+ }
135
+ /**
136
+ * Get or create a connection
137
+ * @returns Connection or null if cannot create
138
+ */
139
+ async getOrCreateConnection() {
140
+ // Try to get from idle queue
141
+ while (this.idleConnectionsQueue.length > 0) {
142
+ const connection = this.idleConnectionsQueue.shift();
143
+ if (await this.validateConnection(connection)) {
144
+ return connection;
145
+ }
146
+ else {
147
+ await this.destroyConnection(connection);
148
+ }
149
+ }
150
+ // Create new connection if under limit
151
+ if (this.totalConnections < (this.config.maxConnections || 10)) {
152
+ try {
153
+ const connection = await this.createConnection();
154
+ this.pool.add(connection);
155
+ this.totalConnections++;
156
+ return connection;
157
+ }
158
+ catch (error) {
159
+ this.stats.totalErrors++;
160
+ return null;
161
+ }
162
+ }
163
+ return null;
164
+ }
165
+ /**
166
+ * Validate a connection
167
+ * @param connection Connection to validate
168
+ * @returns True if connection is valid
169
+ */
170
+ async validateConnection(connection) {
171
+ try {
172
+ const isValid = await this.validateConnectionFn(connection);
173
+ if (!isValid) {
174
+ await this.destroyConnection(connection);
175
+ return false;
176
+ }
177
+ return isValid;
178
+ }
179
+ catch (error) {
180
+ await this.destroyConnection(connection);
181
+ return false;
182
+ }
183
+ }
184
+ /**
185
+ * Release a connection back to the pool
186
+ * @param connection Connection to release
187
+ */
188
+ async release(connection) {
189
+ // Remove from active connections
190
+ this.activeConnectionsSet.delete(connection);
191
+ this.activeConnections--;
192
+ // Validate connection before returning to pool
193
+ if (await this.validateConnection(connection)) {
194
+ // Reset connection state
195
+ await connection.reset();
196
+ // Add to idle queue
197
+ this.idleConnectionsQueue.push(connection);
198
+ this.idleConnections++;
199
+ // Update connection stats
200
+ this.updateConnectionStats(connection, true);
201
+ // Notify waiting clients
202
+ this.notifyWaitingClients();
203
+ }
204
+ else {
205
+ // Destroy invalid connection
206
+ await this.destroyConnection(connection);
207
+ }
208
+ this.stats.totalReleases++;
209
+ }
210
+ /**
211
+ * Destroy a connection (remove from pool)
212
+ * @param connection Connection to destroy
213
+ */
214
+ async destroy(connection) {
215
+ await this.destroyConnection(connection);
216
+ }
217
+ /**
218
+ * Destroy a connection (internal)
219
+ * @param connection Connection to destroy
220
+ */
221
+ async destroyConnection(connection) {
222
+ try {
223
+ await connection.close();
224
+ }
225
+ catch (error) {
226
+ // Ignore close errors
227
+ }
228
+ // Remove from all collections
229
+ this.pool.delete(connection);
230
+ this.activeConnectionsSet.delete(connection);
231
+ const idleIndex = this.idleConnectionsQueue.indexOf(connection);
232
+ if (idleIndex !== -1) {
233
+ this.idleConnectionsQueue.splice(idleIndex, 1);
234
+ this.idleConnections--;
235
+ }
236
+ this.totalConnections--;
237
+ this.stats.totalDestructions++;
238
+ // Remove from stats
239
+ this.stats.connectionStats.delete(connection.id);
240
+ }
241
+ /**
242
+ * Execute a function with a connection from the pool
243
+ * @param fn Function to execute with connection
244
+ * @param timeout Operation timeout in milliseconds
245
+ * @returns Promise resolving to function result
246
+ */
247
+ async withConnection(fn, timeout) {
248
+ const acquisition = await this.acquire(timeout);
249
+ try {
250
+ const result = await fn(acquisition.connection);
251
+ return result;
252
+ }
253
+ finally {
254
+ await this.release(acquisition.connection);
255
+ }
256
+ }
257
+ /**
258
+ * Record connection acquisition
259
+ * @param startTime Acquisition start time
260
+ * @param connection Acquired connection
261
+ */
262
+ recordAcquisition(startTime, connection) {
263
+ const waitTime = Date.now() - startTime;
264
+ // Update connection stats
265
+ const connStats = this.stats.connectionStats.get(connection.id) || {
266
+ createdAt: connection.createdAt,
267
+ lastUsedAt: Date.now(),
268
+ usageCount: 0,
269
+ totalUsageTime: 0,
270
+ isHealthy: connection.isHealthy(),
271
+ };
272
+ connStats.usageCount++;
273
+ connStats.lastUsedAt = Date.now();
274
+ this.stats.connectionStats.set(connection.id, connStats);
275
+ // Update pool stats
276
+ this.stats.totalAcquisitions++;
277
+ this.stats.totalAcquisitionTime += waitTime;
278
+ this.stats.maxAcquisitionTime = Math.max(this.stats.maxAcquisitionTime, waitTime);
279
+ // Update pool state
280
+ const idleIndex = this.idleConnectionsQueue.indexOf(connection);
281
+ if (idleIndex !== -1) {
282
+ this.idleConnectionsQueue.splice(idleIndex, 1);
283
+ this.idleConnections--;
284
+ }
285
+ this.activeConnectionsSet.add(connection);
286
+ this.activeConnections++;
287
+ }
288
+ /**
289
+ * Create acquisition result
290
+ * @param connection Acquired connection
291
+ * @param waitTime Wait time in milliseconds
292
+ * @returns Connection acquisition result
293
+ */
294
+ createAcquisitionResult(connection, waitTime) {
295
+ return {
296
+ connection,
297
+ timestamp: Date.now(),
298
+ waitTime,
299
+ poolStats: {
300
+ totalConnections: this.totalConnections,
301
+ activeConnections: this.activeConnections,
302
+ idleConnections: this.idleConnections,
303
+ waitingClients: this.waitingClients,
304
+ },
305
+ };
306
+ }
307
+ /**
308
+ * Update connection statistics
309
+ * @param connection Connection
310
+ * @param released Whether connection was released
311
+ */
312
+ updateConnectionStats(connection, released) {
313
+ const connStats = this.stats.connectionStats.get(connection.id);
314
+ if (connStats && released) {
315
+ const usageTime = Date.now() - connStats.lastUsedAt;
316
+ connStats.totalUsageTime += usageTime;
317
+ connStats.isHealthy = connection.isHealthy();
318
+ this.stats.connectionStats.set(connection.id, connStats);
319
+ }
320
+ }
321
+ /**
322
+ * Remove waiting client from queue
323
+ * @param reject Reject function
324
+ * @param startTime Start time
325
+ */
326
+ removeWaitingClient(reject, startTime) {
327
+ const index = this.waitingQueue.findIndex(client => client.reject === reject);
328
+ if (index !== -1) {
329
+ this.waitingQueue.splice(index, 1);
330
+ this.waitingClients--;
331
+ }
332
+ }
333
+ /**
334
+ * Notify waiting clients
335
+ */
336
+ async notifyWaitingClients() {
337
+ while (this.waitingQueue.length > 0 && this.idleConnectionsQueue.length > 0) {
338
+ const connection = this.idleConnectionsQueue.shift();
339
+ if (!connection)
340
+ break;
341
+ if (await this.validateConnection(connection)) {
342
+ const client = this.waitingQueue.shift();
343
+ if (!client) {
344
+ this.idleConnectionsQueue.unshift(connection);
345
+ break;
346
+ }
347
+ this.waitingClients--;
348
+ clearTimeout(client.timeout);
349
+ const waitTime = Date.now() - client.startTime;
350
+ this.recordAcquisition(client.startTime, connection);
351
+ client.resolve(this.createAcquisitionResult(connection, waitTime));
352
+ }
353
+ else {
354
+ await this.destroyConnection(connection);
355
+ }
356
+ }
357
+ }
358
+ /**
359
+ * Clean up idle connections
360
+ */
361
+ async cleanupIdleConnections() {
362
+ const now = Date.now();
363
+ const maxIdleTime = this.config.idleTimeout || 60000;
364
+ for (let i = this.idleConnectionsQueue.length - 1; i >= 0; i--) {
365
+ const connection = this.idleConnectionsQueue[i];
366
+ const idleTime = now - connection.lastUsedAt;
367
+ // Check idle timeout
368
+ if (idleTime > maxIdleTime) {
369
+ this.idleConnectionsQueue.splice(i, 1);
370
+ this.idleConnections--;
371
+ await this.destroyConnection(connection);
372
+ continue;
373
+ }
374
+ // Check max lifetime
375
+ if (this.config.maxLifetime) {
376
+ const age = now - connection.createdAt;
377
+ if (age > this.config.maxLifetime) {
378
+ this.idleConnectionsQueue.splice(i, 1);
379
+ this.idleConnections--;
380
+ await this.destroyConnection(connection);
381
+ }
382
+ }
383
+ }
384
+ }
385
+ /**
386
+ * Check pool health
387
+ */
388
+ async checkPoolHealth() {
389
+ try {
390
+ // Validate all idle connections
391
+ for (const connection of this.idleConnectionsQueue) {
392
+ if (!(await this.validateConnection(connection))) {
393
+ const index = this.idleConnectionsQueue.indexOf(connection);
394
+ if (index !== -1) {
395
+ this.idleConnectionsQueue.splice(index, 1);
396
+ this.idleConnections--;
397
+ }
398
+ }
399
+ }
400
+ // Check if pool meets minimum connections
401
+ if (this.config.minConnections && this.totalConnections < this.config.minConnections) {
402
+ await this.warmupPool();
403
+ }
404
+ this.isHealthy = true;
405
+ }
406
+ catch (error) {
407
+ this.isHealthy = false;
408
+ this.stats.totalErrors++;
409
+ }
410
+ }
411
+ /**
412
+ * Drain the pool (gracefully close all connections)
413
+ * @param force Force immediate drain
414
+ */
415
+ async drain(force) {
416
+ // Stop accepting new requests
417
+ this.isHealthy = false;
418
+ // Clear intervals
419
+ if (this.cleanupInterval) {
420
+ clearInterval(this.cleanupInterval);
421
+ this.cleanupInterval = undefined;
422
+ }
423
+ if (this.healthCheckInterval) {
424
+ clearInterval(this.healthCheckInterval);
425
+ this.healthCheckInterval = undefined;
426
+ }
427
+ // Wait for active connections to finish (if not forced)
428
+ if (!force) {
429
+ const maxWaitTime = 30000; // 30 seconds max wait
430
+ const startTime = Date.now();
431
+ while (this.activeConnections > 0 && (Date.now() - startTime) < maxWaitTime) {
432
+ await new Promise(resolve => setTimeout(resolve, 100));
433
+ }
434
+ }
435
+ // Close all connections
436
+ const closePromises = Array.from(this.pool).map(conn => conn.close());
437
+ await Promise.allSettled(closePromises);
438
+ // Clear all collections
439
+ this.pool.clear();
440
+ this.activeConnectionsSet.clear();
441
+ this.idleConnectionsQueue.length = 0;
442
+ this.totalConnections = 0;
443
+ this.activeConnections = 0;
444
+ this.idleConnections = 0;
445
+ // Clear waiting queue
446
+ for (const client of this.waitingQueue) {
447
+ clearTimeout(client.timeout);
448
+ client.reject(new CommunicationError(COMMUNICATION_ERROR_CODES.CONNECTION_ERROR, 503, { message: 'Pool is draining' }));
449
+ }
450
+ this.waitingQueue.length = 0;
451
+ this.waitingClients = 0;
452
+ }
453
+ /**
454
+ * Clear the pool (force close all connections)
455
+ */
456
+ async clear() {
457
+ await this.drain(true);
458
+ }
459
+ /**
460
+ * Health check for the pool
461
+ */
462
+ async healthCheck() {
463
+ const healthy = this.isHealthy && this.totalConnections > 0;
464
+ return {
465
+ healthy,
466
+ message: healthy ? 'Connection pool is healthy' : 'Connection pool is not healthy',
467
+ details: {
468
+ name: this.name,
469
+ totalConnections: this.totalConnections,
470
+ activeConnections: this.activeConnections,
471
+ idleConnections: this.idleConnections,
472
+ waitingClients: this.waitingClients,
473
+ config: this.config,
474
+ statistics: this.getStats(),
475
+ },
476
+ };
477
+ }
478
+ /**
479
+ * Get pool statistics
480
+ */
481
+ getStats() {
482
+ const connectionStats = Array.from(this.stats.connectionStats.entries()).map(([id, stats]) => ({
483
+ id,
484
+ createdAt: stats.createdAt,
485
+ lastUsedAt: stats.lastUsedAt,
486
+ usageCount: stats.usageCount,
487
+ isHealthy: stats.isHealthy,
488
+ }));
489
+ const acquisitionTimeAverage = this.stats.totalAcquisitions > 0
490
+ ? this.stats.totalAcquisitionTime / this.stats.totalAcquisitions
491
+ : 0;
492
+ const poolUtilization = this.config.maxConnections
493
+ ? this.activeConnections / this.config.maxConnections
494
+ : 0;
495
+ return {
496
+ totalConnections: this.totalConnections,
497
+ activeConnections: this.activeConnections,
498
+ idleConnections: this.idleConnections,
499
+ waitingClients: this.waitingClients,
500
+ totalAcquisitions: this.stats.totalAcquisitions,
501
+ totalReleases: this.stats.totalReleases,
502
+ totalDestructions: this.stats.totalDestructions,
503
+ totalErrors: this.stats.totalErrors,
504
+ acquisitionTimeAverage,
505
+ acquisitionTimeMax: this.stats.maxAcquisitionTime,
506
+ poolUtilization,
507
+ connectionStats,
508
+ };
509
+ }
510
+ /**
511
+ * Reset pool statistics
512
+ */
513
+ resetStats() {
514
+ this.stats = {
515
+ totalAcquisitions: 0,
516
+ totalReleases: 0,
517
+ totalDestructions: 0,
518
+ totalErrors: 0,
519
+ totalAcquisitionTime: 0,
520
+ maxAcquisitionTime: 0,
521
+ connectionStats: new Map(),
522
+ };
523
+ }
524
+ /**
525
+ * Update pool configuration
526
+ * @param config New configuration
527
+ */
528
+ updateConfig(config) {
529
+ this.config = { ...this.config, ...config };
530
+ // Restart intervals if needed
531
+ if (this.cleanupInterval) {
532
+ clearInterval(this.cleanupInterval);
533
+ }
534
+ if (this.healthCheckInterval) {
535
+ clearInterval(this.healthCheckInterval);
536
+ }
537
+ this.initialize();
538
+ }
539
+ }