@celerispay/hazelcast-client 3.12.5 → 3.12.7-2
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/CHANGELOG.md +111 -87
- package/CHANGES_UNCOMMITTED.md +53 -0
- package/FAILOVER_FIXES.md +148 -230
- package/FAULT_TOLERANCE_IMPROVEMENTS.md +208 -0
- package/HAZELCAST_CLIENT_EVOLUTION.md +402 -0
- package/QUICK_START.md +184 -95
- package/RELEASE_SUMMARY.md +227 -147
- package/lib/HeartbeatService.js +11 -2
- package/lib/PartitionService.d.ts +14 -0
- package/lib/PartitionService.js +32 -9
- package/lib/invocation/ClientConnection.d.ts +14 -0
- package/lib/invocation/ClientConnection.js +95 -1
- package/lib/invocation/ClientConnectionManager.d.ts +99 -0
- package/lib/invocation/ClientConnectionManager.js +394 -10
- package/lib/invocation/ClusterService.d.ts +91 -5
- package/lib/invocation/ClusterService.js +490 -17
- package/lib/invocation/ConnectionAuthenticator.d.ts +11 -0
- package/lib/invocation/ConnectionAuthenticator.js +85 -12
- package/lib/invocation/CredentialPreservationService.d.ts +141 -0
- package/lib/invocation/CredentialPreservationService.js +377 -0
- package/lib/invocation/HazelcastFailoverManager.d.ts +102 -0
- package/lib/invocation/HazelcastFailoverManager.js +285 -0
- package/lib/invocation/InvocationService.js +8 -0
- package/lib/nearcache/StaleReadDetectorImpl.js +31 -4
- package/lib/proxy/ProxyManager.js +25 -4
- package/package.json +20 -28
|
@@ -217,7 +217,44 @@ var ClientConnection = /** @class */ (function () {
|
|
|
217
217
|
_this.lastWriteTimeMillis = Date.now();
|
|
218
218
|
});
|
|
219
219
|
this.reader = new FrameReader();
|
|
220
|
+
this.logger = client.getLoggingService().getLogger();
|
|
221
|
+
// Add socket event handlers for proper connection state tracking
|
|
222
|
+
this.setupSocketEventHandlers();
|
|
220
223
|
}
|
|
224
|
+
/**
|
|
225
|
+
* Sets up socket event handlers for proper connection state tracking
|
|
226
|
+
*/
|
|
227
|
+
ClientConnection.prototype.setupSocketEventHandlers = function () {
|
|
228
|
+
var _this = this;
|
|
229
|
+
// Handle socket close events
|
|
230
|
+
this.socket.on('close', function () {
|
|
231
|
+
_this.closedTime = Date.now();
|
|
232
|
+
_this.logger.debug('ClientConnection', "Socket closed for " + _this.address.toString());
|
|
233
|
+
});
|
|
234
|
+
// Handle socket end events - be more resilient to temporary disconnections
|
|
235
|
+
this.socket.on('end', function () {
|
|
236
|
+
_this.logger.debug('ClientConnection', "Socket ended by remote for " + _this.address.toString());
|
|
237
|
+
// Don't immediately mark as closed - this could be a temporary network hiccup
|
|
238
|
+
// Only mark as closed if we're certain it's a real disconnection
|
|
239
|
+
// The health check will handle this more gracefully
|
|
240
|
+
});
|
|
241
|
+
// Handle socket errors - be more resilient
|
|
242
|
+
this.socket.on('error', function (error) {
|
|
243
|
+
_this.logger.warn('ClientConnection', "Socket error for " + _this.address.toString() + ":", error);
|
|
244
|
+
// Only mark as closed for specific error types that indicate real disconnection
|
|
245
|
+
if (error.code === 'ECONNRESET' || error.code === 'EPIPE') {
|
|
246
|
+
_this.logger.warn('ClientConnection', "Critical socket error for " + _this.address.toString() + ", marking as closed");
|
|
247
|
+
_this.closedTime = Date.now();
|
|
248
|
+
}
|
|
249
|
+
// For other errors, don't immediately close - let health check handle it
|
|
250
|
+
});
|
|
251
|
+
// Handle socket timeout - be more resilient
|
|
252
|
+
this.socket.on('timeout', function () {
|
|
253
|
+
_this.logger.warn('ClientConnection', "Socket timeout for " + _this.address.toString());
|
|
254
|
+
// Don't set closedTime here as the socket might still be usable
|
|
255
|
+
// Let the health check handle this more gracefully
|
|
256
|
+
});
|
|
257
|
+
};
|
|
221
258
|
/**
|
|
222
259
|
* Returns the address of local port that is associated with this connection.
|
|
223
260
|
* @returns
|
|
@@ -251,11 +288,68 @@ var ClientConnection = /** @class */ (function () {
|
|
|
251
288
|
* Closes this connection.
|
|
252
289
|
*/
|
|
253
290
|
ClientConnection.prototype.close = function () {
|
|
291
|
+
var addressStr = this.address.toString();
|
|
292
|
+
// Log who is initiating the disconnection
|
|
293
|
+
this.logger.info('ClientConnection', "\uD83D\uDD0C CLIENT INITIATED DISCONNECTION from " + addressStr);
|
|
294
|
+
// Log the call stack to understand why this is being called
|
|
295
|
+
this.logger.info('ClientConnection', "\uD83D\uDD0D Close Connection Call Stack for " + addressStr + ":");
|
|
296
|
+
var stack = new Error().stack;
|
|
297
|
+
if (stack) {
|
|
298
|
+
this.logger.info('ClientConnection', " - Called from: " + stack.split('\n').slice(1, 4).join(' | '));
|
|
299
|
+
}
|
|
300
|
+
// Log connection state at closure
|
|
301
|
+
this.logger.info('ClientConnection', "\uD83D\uDCCA Connection State at Closure for " + addressStr + ":");
|
|
302
|
+
this.logger.info('ClientConnection', " - Is Alive: " + this.isAlive());
|
|
303
|
+
this.logger.info('ClientConnection', " - Is Healthy: " + this.isHealthy());
|
|
304
|
+
this.logger.info('ClientConnection', " - Is Heartbeating: " + this.isHeartbeating());
|
|
305
|
+
this.logger.info('ClientConnection', " - Is Owner: " + this.isAuthenticatedAsOwner());
|
|
306
|
+
this.logger.info('ClientConnection', " - Last Read: " + (this.lastReadTimeMillis > 0 ? Math.round((Date.now() - this.lastReadTimeMillis) / 1000) + 's ago' : 'never'));
|
|
307
|
+
this.logger.info('ClientConnection', " - Last Write: " + (this.lastWriteTimeMillis > 0 ? Math.round((Date.now() - this.lastWriteTimeMillis) / 1000) + 's ago' : 'never'));
|
|
308
|
+
this.logger.info('ClientConnection', " - Connection Age: " + Math.round((Date.now() - this.startTime) / 1000) + "s");
|
|
254
309
|
this.socket.end();
|
|
255
310
|
this.closedTime = Date.now();
|
|
311
|
+
this.logger.info('ClientConnection', "\u2705 Disconnection completed for " + addressStr);
|
|
256
312
|
};
|
|
313
|
+
/**
|
|
314
|
+
* Checks if the connection is alive and healthy
|
|
315
|
+
* @returns true if connection is alive, false otherwise
|
|
316
|
+
*/
|
|
257
317
|
ClientConnection.prototype.isAlive = function () {
|
|
258
|
-
|
|
318
|
+
// Check if we've explicitly closed the connection
|
|
319
|
+
if (this.closedTime !== 0) {
|
|
320
|
+
return false;
|
|
321
|
+
}
|
|
322
|
+
// Check if the underlying socket is still connected
|
|
323
|
+
if (!this.socket || this.socket.destroyed) {
|
|
324
|
+
return false;
|
|
325
|
+
}
|
|
326
|
+
// Check if the socket is writable (indicates it's still connected)
|
|
327
|
+
if (!this.socket.writable) {
|
|
328
|
+
return false;
|
|
329
|
+
}
|
|
330
|
+
// More resilient data freshness check - only fail if REALLY stale
|
|
331
|
+
var now = Date.now();
|
|
332
|
+
var lastReadThreshold = 120000; // Increased from 30s to 2 minutes for production resilience
|
|
333
|
+
if (this.lastReadTimeMillis > 0 && (now - this.lastReadTimeMillis) > lastReadThreshold) {
|
|
334
|
+
// Only mark as dead if we haven't received data for 2 minutes
|
|
335
|
+
// This prevents false drops due to temporary network hiccups
|
|
336
|
+
return false;
|
|
337
|
+
}
|
|
338
|
+
return true;
|
|
339
|
+
};
|
|
340
|
+
/**
|
|
341
|
+
* Performs a more thorough health check of the connection
|
|
342
|
+
* @returns true if connection is healthy, false otherwise
|
|
343
|
+
*/
|
|
344
|
+
ClientConnection.prototype.isHealthy = function () {
|
|
345
|
+
var addressStr = this.address.toString();
|
|
346
|
+
if (!this.isAlive()) {
|
|
347
|
+
return false;
|
|
348
|
+
}
|
|
349
|
+
if (!this.isHeartbeating()) {
|
|
350
|
+
return false;
|
|
351
|
+
}
|
|
352
|
+
return true;
|
|
259
353
|
};
|
|
260
354
|
ClientConnection.prototype.isHeartbeating = function () {
|
|
261
355
|
return this.heartbeating;
|
|
@@ -23,8 +23,12 @@ export declare class ClientConnectionManager extends EventEmitter {
|
|
|
23
23
|
private failedConnections;
|
|
24
24
|
private readonly maxConnectionRetries;
|
|
25
25
|
private readonly connectionRetryDelay;
|
|
26
|
+
private connectionCleanupTask;
|
|
27
|
+
private readonly connectionCleanupInterval;
|
|
26
28
|
constructor(client: HazelcastClient, addressTranslator: AddressTranslator, addressProviders: AddressProvider[]);
|
|
27
29
|
private startConnectionHealthCheck();
|
|
30
|
+
private startConnectionCleanupTask();
|
|
31
|
+
private cleanupStaleConnections();
|
|
28
32
|
private checkConnectionHealth();
|
|
29
33
|
private isConnectionHealthy(connection);
|
|
30
34
|
private retryConnection(address, asOwner, retryCount?);
|
|
@@ -32,6 +36,87 @@ export declare class ClientConnectionManager extends EventEmitter {
|
|
|
32
36
|
getActiveConnections(): {
|
|
33
37
|
[address: string]: ClientConnection;
|
|
34
38
|
};
|
|
39
|
+
/**
|
|
40
|
+
* Checks if we already have a connection to the given address
|
|
41
|
+
* @param address The address to check
|
|
42
|
+
* @returns true if connection exists and is healthy, false otherwise
|
|
43
|
+
*/
|
|
44
|
+
hasConnection(address: Address): boolean;
|
|
45
|
+
/**
|
|
46
|
+
* Forces cleanup of all dead connections
|
|
47
|
+
* This is useful during failover to prevent connection leakage
|
|
48
|
+
*/
|
|
49
|
+
forceCleanupDeadConnections(): void;
|
|
50
|
+
/**
|
|
51
|
+
* Gets an existing connection to a specific address if it exists
|
|
52
|
+
* @param address The address to check for existing connection
|
|
53
|
+
* @returns The existing connection or undefined if none exists
|
|
54
|
+
*/
|
|
55
|
+
getConnection(address: Address): ClientConnection | undefined;
|
|
56
|
+
/**
|
|
57
|
+
* Gets all established connections
|
|
58
|
+
* @returns Object containing all established connections
|
|
59
|
+
*/
|
|
60
|
+
getEstablishedConnections(): {
|
|
61
|
+
[address: string]: ClientConnection;
|
|
62
|
+
};
|
|
63
|
+
/**
|
|
64
|
+
* Gets all pending connections
|
|
65
|
+
* @returns Object containing all pending connections
|
|
66
|
+
*/
|
|
67
|
+
getPendingConnections(): {
|
|
68
|
+
[address: string]: Promise.Resolver<ClientConnection>;
|
|
69
|
+
};
|
|
70
|
+
private credentialPreservationService;
|
|
71
|
+
private memberAddedEvents;
|
|
72
|
+
/**
|
|
73
|
+
* Updates preserved credentials with server-provided data
|
|
74
|
+
* @param address The address to update credentials for
|
|
75
|
+
* @param newUuid The new UUID from server member event
|
|
76
|
+
*/
|
|
77
|
+
updatePreservedCredentials(address: Address, newUuid: string): void;
|
|
78
|
+
/**
|
|
79
|
+
* Records that a member added event was received from server
|
|
80
|
+
* @param address The address that had a server member added event
|
|
81
|
+
*/
|
|
82
|
+
recordMemberAddedEvent(address: Address): void;
|
|
83
|
+
/**
|
|
84
|
+
* Checks if we received a server member added event for an address
|
|
85
|
+
* @param address The address to check
|
|
86
|
+
* @returns True if server member added event was received
|
|
87
|
+
*/
|
|
88
|
+
hasMemberAddedEvent(address: Address): boolean;
|
|
89
|
+
/**
|
|
90
|
+
* Clears member added events for an address (useful during failover)
|
|
91
|
+
* @param address The address to clear events for
|
|
92
|
+
*/
|
|
93
|
+
clearMemberAddedEvents(address: Address): void;
|
|
94
|
+
/**
|
|
95
|
+
* Updates ALL credentials with server-provided owner UUID
|
|
96
|
+
* @param newOwnerUuid The new owner UUID from server cluster state
|
|
97
|
+
*/
|
|
98
|
+
updateAllCredentialsWithNewOwnerUuid(newOwnerUuid: string): void;
|
|
99
|
+
/**
|
|
100
|
+
* Gets the current owner UUID from server-stored credentials
|
|
101
|
+
* @returns The current owner UUID or null if not found
|
|
102
|
+
*/
|
|
103
|
+
getCurrentOwnerUuid(): string | null;
|
|
104
|
+
/**
|
|
105
|
+
* Validates that all stored credentials are consistent with server data
|
|
106
|
+
* @returns True if all credentials are consistent, false otherwise
|
|
107
|
+
*/
|
|
108
|
+
validateCredentialConsistency(): boolean;
|
|
109
|
+
/**
|
|
110
|
+
* Requests current cluster state from server when reconnecting
|
|
111
|
+
* This ensures we have the most up-to-date member information
|
|
112
|
+
* @returns Server cluster state
|
|
113
|
+
*/
|
|
114
|
+
requestServerClusterState(): any;
|
|
115
|
+
/**
|
|
116
|
+
* Gets the current owner connection for server requests
|
|
117
|
+
* @returns The owner connection or null if not available
|
|
118
|
+
*/
|
|
119
|
+
private getOwnerConnection();
|
|
35
120
|
/**
|
|
36
121
|
* Returns the {@link ClientConnection} with given {@link Address}. If there is no such connection established,
|
|
37
122
|
* it first connects to the address and then return the {@link ClientConnection}.
|
|
@@ -45,6 +130,20 @@ export declare class ClientConnectionManager extends EventEmitter {
|
|
|
45
130
|
* @param address
|
|
46
131
|
*/
|
|
47
132
|
destroyConnection(address: Address): void;
|
|
133
|
+
/**
|
|
134
|
+
* Cleans up all connections to a specific address during failover
|
|
135
|
+
* @param address The address to cleanup
|
|
136
|
+
*/
|
|
137
|
+
cleanupConnectionsForFailover(address: Address): void;
|
|
138
|
+
/**
|
|
139
|
+
* Clears all stored credentials - used for single-node cluster reset
|
|
140
|
+
*/
|
|
141
|
+
clearAllCredentials(): void;
|
|
142
|
+
/**
|
|
143
|
+
* Handles authentication errors by clearing failed connections and allowing retry
|
|
144
|
+
* @param address The address that had authentication issues
|
|
145
|
+
*/
|
|
146
|
+
handleAuthenticationError(address: Address): void;
|
|
48
147
|
shutdown(): void;
|
|
49
148
|
private triggerConnect(address, asOwner);
|
|
50
149
|
private connectTLSSocket(address, configOpts);
|