@celerispay/hazelcast-client 3.12.5-3 → 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,11 +133,21 @@ 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
|
-
return new Promise(function (resolve) {
|
|
148
|
+
return new Promise(function (resolve, reject) {
|
|
139
149
|
setTimeout(function () {
|
|
140
|
-
_this.retryConnection(address, asOwner, retryCount + 1).then(resolve).catch(
|
|
150
|
+
_this.retryConnection(address, asOwner, retryCount + 1).then(resolve).catch(reject);
|
|
141
151
|
}, _this.connectionRetryDelay);
|
|
142
152
|
});
|
|
143
153
|
}
|
|
@@ -273,6 +283,13 @@ var ClientConnectionManager = /** @class */ (function (_super) {
|
|
|
273
283
|
this.pendingConnections[addressIndex] = connectionResolver;
|
|
274
284
|
this.retryConnection(address, asOwner)
|
|
275
285
|
.then(function (clientConnection) {
|
|
286
|
+
// Safety check: ensure we got a proper ClientConnection
|
|
287
|
+
if (!clientConnection || typeof clientConnection.getAddress !== 'function') {
|
|
288
|
+
var error = new Error("Invalid connection object returned: " + typeof clientConnection);
|
|
289
|
+
_this.logger.error('ClientConnectionManager', error.message);
|
|
290
|
+
connectionResolver.reject(error);
|
|
291
|
+
return;
|
|
292
|
+
}
|
|
276
293
|
_this.establishedConnections[clientConnection.getAddress().toString()] = clientConnection;
|
|
277
294
|
_this.onConnectionOpened(clientConnection);
|
|
278
295
|
connectionResolver.resolve(clientConnection);
|
|
@@ -321,7 +338,7 @@ var ClientConnectionManager = /** @class */ (function (_super) {
|
|
|
321
338
|
};
|
|
322
339
|
/**
|
|
323
340
|
* Cleans up all connections to a specific address during failover
|
|
324
|
-
* @param address
|
|
341
|
+
* @param address The address to cleanup
|
|
325
342
|
*/
|
|
326
343
|
ClientConnectionManager.prototype.cleanupConnectionsForFailover = function (address) {
|
|
327
344
|
var addressStr = address.toString();
|
|
@@ -331,6 +348,27 @@ var ClientConnectionManager = /** @class */ (function (_super) {
|
|
|
331
348
|
// Remove from failed connections to allow reconnection after failover
|
|
332
349
|
this.failedConnections.delete(addressStr);
|
|
333
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
|
+
};
|
|
334
372
|
ClientConnectionManager.prototype.shutdown = function () {
|
|
335
373
|
if (this.connectionHealthCheckInterval) {
|
|
336
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 =
|
|
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
|
-
//
|
|
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