@celerispay/hazelcast-client 3.12.5-4 → 3.12.5-5

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.
@@ -82,9 +82,14 @@ export declare class ClientConnectionManager extends EventEmitter {
82
82
  destroyConnection(address: Address): void;
83
83
  /**
84
84
  * Cleans up all connections to a specific address during failover
85
- * @param address
85
+ * @param address The address to cleanup
86
86
  */
87
87
  cleanupConnectionsForFailover(address: Address): void;
88
+ /**
89
+ * Handles authentication errors by clearing failed connections and allowing retry
90
+ * @param address The address that had authentication issues
91
+ */
92
+ handleAuthenticationError(address: Address): void;
88
93
  shutdown(): void;
89
94
  private triggerConnect(address, asOwner);
90
95
  private connectTLSSocket(address, configOpts);
@@ -133,6 +133,16 @@ var ClientConnectionManager = /** @class */ (function (_super) {
133
133
  _this.failedConnections.delete(address.toString());
134
134
  return connection;
135
135
  }).catch(function (error) {
136
+ // Check if it's an authentication error
137
+ var isAuthError = error.message && (error.message.includes('Invalid Credentials') ||
138
+ error.message.includes('authentication') ||
139
+ error.message.includes('credentials'));
140
+ if (isAuthError) {
141
+ _this.logger.error('ClientConnectionManager', "Authentication failed for " + address.toString() + ", not retrying: " + error.message);
142
+ // Handle authentication errors specially
143
+ _this.handleAuthenticationError(address);
144
+ throw error;
145
+ }
136
146
  if (retryCount < _this.maxConnectionRetries) {
137
147
  _this.logger.warn('ClientConnectionManager', "Connection attempt " + (retryCount + 1) + " failed for " + address.toString() + ", retrying in " + _this.connectionRetryDelay + "ms");
138
148
  return new Promise(function (resolve, reject) {
@@ -328,7 +338,7 @@ var ClientConnectionManager = /** @class */ (function (_super) {
328
338
  };
329
339
  /**
330
340
  * Cleans up all connections to a specific address during failover
331
- * @param address
341
+ * @param address The address to cleanup
332
342
  */
333
343
  ClientConnectionManager.prototype.cleanupConnectionsForFailover = function (address) {
334
344
  var addressStr = address.toString();
@@ -338,6 +348,27 @@ var ClientConnectionManager = /** @class */ (function (_super) {
338
348
  // Remove from failed connections to allow reconnection after failover
339
349
  this.failedConnections.delete(addressStr);
340
350
  };
351
+ /**
352
+ * Handles authentication errors by clearing failed connections and allowing retry
353
+ * @param address The address that had authentication issues
354
+ */
355
+ ClientConnectionManager.prototype.handleAuthenticationError = function (address) {
356
+ var addressStr = address.toString();
357
+ this.logger.warn('ClientConnectionManager', "Handling authentication error for " + addressStr);
358
+ // Clear from failed connections to allow retry with fresh credentials
359
+ this.failedConnections.delete(addressStr);
360
+ // Also clear any established connections to this address
361
+ if (this.establishedConnections.hasOwnProperty(addressStr)) {
362
+ this.logger.info('ClientConnectionManager', "Clearing established connection to " + addressStr + " due to auth error");
363
+ this.destroyConnection(address);
364
+ }
365
+ // Clear any pending connections
366
+ if (this.pendingConnections.hasOwnProperty(addressStr)) {
367
+ this.logger.info('ClientConnectionManager', "Clearing pending connection to " + addressStr + " due to auth error");
368
+ this.pendingConnections[addressStr].reject(new Error('Authentication error, connection cleared'));
369
+ delete this.pendingConnections[addressStr];
370
+ }
371
+ };
341
372
  ClientConnectionManager.prototype.shutdown = function () {
342
373
  if (this.connectionHealthCheckInterval) {
343
374
  clearInterval(this.connectionHealthCheckInterval);
@@ -87,6 +87,10 @@ export declare class ClusterService {
87
87
  private onConnectionClosed(connection);
88
88
  private onHeartbeatStopped(connection);
89
89
  private triggerFailover();
90
+ /**
91
+ * Attempts emergency recovery when failover fails
92
+ */
93
+ private attemptEmergencyRecovery();
90
94
  private isAddressKnownDown(address);
91
95
  private markAddressAsDown(address);
92
96
  private getDownAddressesInfo();
@@ -50,7 +50,7 @@ var ClusterService = /** @class */ (function () {
50
50
  this.lastFailoverAttempt = 0;
51
51
  this.failoverCooldown = 5000; // 5 seconds cooldown between failover attempts
52
52
  this.downAddresses = new Map(); // address -> timestamp when marked down
53
- this.addressBlockDuration = 30000; // 30 seconds block duration for down addresses
53
+ this.addressBlockDuration = 15000; // Reduced from 30000ms to 15000ms
54
54
  this.reconnectionTask = null;
55
55
  this.reconnectionInterval = 10000; // 10 seconds between reconnection attempts
56
56
  this.client = client;
@@ -242,14 +242,50 @@ var ClusterService = /** @class */ (function () {
242
242
  })
243
243
  .catch(function (error) {
244
244
  _this.logger.error('ClusterService', 'Failover failed', error);
245
+ // If failover fails, try to unblock at least one address to allow recovery
246
+ _this.attemptEmergencyRecovery();
245
247
  _this.logCurrentState(); // Log state after failed failover
246
- // If failover fails, shutdown the client to prevent further issues
247
- _this.client.shutdown();
248
+ // Don't shutdown immediately, give recovery a chance
248
249
  })
249
250
  .finally(function () {
250
251
  _this.failoverInProgress = false;
251
252
  });
252
253
  };
254
+ /**
255
+ * Attempts emergency recovery when failover fails
256
+ */
257
+ ClusterService.prototype.attemptEmergencyRecovery = function () {
258
+ var _this = this;
259
+ this.logger.warn('ClusterService', 'Attempting emergency recovery...');
260
+ // Unblock at least one address to allow recovery
261
+ if (this.downAddresses.size > 0) {
262
+ var firstBlockedAddress_1 = Array.from(this.downAddresses.keys())[0];
263
+ this.logger.info('ClusterService', "Emergency unblocking address " + firstBlockedAddress_1);
264
+ this.downAddresses.delete(firstBlockedAddress_1);
265
+ // Try to connect to the unblocked address
266
+ try {
267
+ var _a = firstBlockedAddress_1.split(':'), host = _a[0], portStr = _a[1];
268
+ var port = parseInt(portStr, 10);
269
+ if (host && !isNaN(port)) {
270
+ var address_1 = new Address(host, port);
271
+ this.logger.info('ClusterService', "Attempting emergency connection to " + firstBlockedAddress_1);
272
+ // Try to connect without blocking
273
+ this.client.getConnectionManager().getOrConnect(address_1, false)
274
+ .then(function (connection) {
275
+ _this.logger.info('ClusterService', "Emergency connection successful to " + firstBlockedAddress_1);
276
+ _this.evaluateOwnershipChange(address_1, connection);
277
+ _this.client.getPartitionService().refresh();
278
+ })
279
+ .catch(function (error) {
280
+ _this.logger.warn('ClusterService', "Emergency connection failed to " + firstBlockedAddress_1 + ":", error);
281
+ });
282
+ }
283
+ }
284
+ catch (error) {
285
+ this.logger.error('ClusterService', 'Error during emergency recovery:', error);
286
+ }
287
+ }
288
+ };
253
289
  ClusterService.prototype.isAddressKnownDown = function (address) {
254
290
  var addressStr = address.toString();
255
291
  var downTime = this.downAddresses.get(addressStr);
@@ -651,6 +687,18 @@ var ClusterService = /** @class */ (function () {
651
687
  var _this = this;
652
688
  var addressStr = address.toString();
653
689
  var now = Date.now();
690
+ // Don't block if we already have a healthy connection to this address
691
+ if (this.client.getConnectionManager().hasConnection(address)) {
692
+ this.logger.debug('ClusterService', "Not blocking " + addressStr + " as we have a healthy connection");
693
+ return;
694
+ }
695
+ // Don't block if this would leave us with no available nodes
696
+ var totalDownAddresses = this.downAddresses.size;
697
+ var totalMembers = this.members.length;
698
+ if (totalDownAddresses >= totalMembers - 1) {
699
+ this.logger.warn('ClusterService', "Not blocking " + addressStr + " as it would leave us with no available nodes");
700
+ return;
701
+ }
654
702
  this.downAddresses.set(addressStr, now);
655
703
  this.logger.warn('ClusterService', "Marked address " + addressStr + " as down, will be blocked for " + blockDuration + "ms");
656
704
  // Schedule cleanup of this address after block duration
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@celerispay/hazelcast-client",
3
- "version": "3.12.5-4",
3
+ "version": "3.12.5-5",
4
4
  "description": "Hazelcast - open source In-Memory Data Grid - client for NodeJS with critical connection failover fixes",
5
5
  "main": "./lib/index.js",
6
6
  "scripts": {