@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.
@@ -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
- return this.closedTime === 0;
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);