@celerispay/hazelcast-client 3.12.5-8 → 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.
@@ -0,0 +1,285 @@
1
+ "use strict";
2
+ /*
3
+ * Copyright (c) 2008-2021, Hazelcast, Inc. All Rights Reserved.
4
+ *
5
+ * Licensed under the Apache License, Version 2.0 (the "License");
6
+ * you may not use this file except in compliance with the License.
7
+ * You may obtain a copy of the License at
8
+ *
9
+ * http://www.apache.org/licenses/LICENSE-2.0
10
+ *
11
+ * Unless required by applicable law or agreed to in writing, software
12
+ * distributed under the License is distributed on an "AS IS" BASIS,
13
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ * See the License for the specific language governing permissions and
15
+ * limitations under the License.
16
+ */
17
+ Object.defineProperty(exports, "__esModule", { value: true });
18
+ /**
19
+ * Node state in the cluster
20
+ */
21
+ var NodeState;
22
+ (function (NodeState) {
23
+ NodeState["UNKNOWN"] = "unknown";
24
+ NodeState["CONNECTING"] = "connecting";
25
+ NodeState["CONNECTED"] = "connected";
26
+ NodeState["OWNER"] = "owner";
27
+ NodeState["CHILD"] = "child";
28
+ NodeState["DISCONNECTED"] = "disconnected";
29
+ NodeState["FAILED"] = "failed";
30
+ })(NodeState = exports.NodeState || (exports.NodeState = {}));
31
+ /**
32
+ * Implements the Java client's failover behavior
33
+ * This ensures seamless node transitions and proper ownership handling
34
+ */
35
+ var HazelcastFailoverManager = /** @class */ (function () {
36
+ function HazelcastFailoverManager(client, logger) {
37
+ this.nodes = new Map();
38
+ this.failoverInProgress = false;
39
+ this.failoverTimeout = 30000; // 30 seconds
40
+ this.currentOwner = null;
41
+ this.client = client;
42
+ this.logger = logger;
43
+ }
44
+ /**
45
+ * Registers a node in the cluster
46
+ * @param address The node address
47
+ * @param connection The connection to the node
48
+ * @param isOwner Whether this node is the owner
49
+ */
50
+ HazelcastFailoverManager.prototype.registerNode = function (address, connection, isOwner) {
51
+ if (isOwner === void 0) { isOwner = false; }
52
+ var addressStr = address.toString();
53
+ var nodeInfo = {
54
+ address: address,
55
+ state: isOwner ? NodeState.OWNER : NodeState.CHILD,
56
+ connection: connection,
57
+ lastSeen: Date.now(),
58
+ failureCount: 0,
59
+ isOwner: isOwner
60
+ };
61
+ this.nodes.set(addressStr, nodeInfo);
62
+ if (isOwner) {
63
+ this.currentOwner = address;
64
+ this.logger.info('HazelcastFailoverManager', "Registered owner node: " + addressStr);
65
+ }
66
+ else {
67
+ this.logger.info('HazelcastFailoverManager', "Registered child node: " + addressStr);
68
+ }
69
+ };
70
+ /**
71
+ * Handles node disconnection gracefully
72
+ * @param address The disconnected node address
73
+ */
74
+ HazelcastFailoverManager.prototype.handleNodeDisconnection = function (address) {
75
+ var addressStr = address.toString();
76
+ var nodeInfo = this.nodes.get(addressStr);
77
+ if (!nodeInfo) {
78
+ return;
79
+ }
80
+ this.logger.warn('HazelcastFailoverManager', "Node " + addressStr + " disconnected, state: " + nodeInfo.state);
81
+ if (nodeInfo.isOwner) {
82
+ // Owner node disconnected - initiate failover
83
+ this.handleOwnerDisconnection(address);
84
+ }
85
+ else {
86
+ // Child node disconnected - just mark as disconnected
87
+ this.handleChildDisconnection(address);
88
+ }
89
+ };
90
+ /**
91
+ * Handles owner node disconnection
92
+ * @param address The disconnected owner node address
93
+ */
94
+ HazelcastFailoverManager.prototype.handleOwnerDisconnection = function (address) {
95
+ var addressStr = address.toString();
96
+ this.logger.warn('HazelcastFailoverManager', "Owner node " + addressStr + " disconnected, initiating failover");
97
+ // Mark current owner as failed
98
+ var nodeInfo = this.nodes.get(addressStr);
99
+ if (nodeInfo) {
100
+ nodeInfo.state = NodeState.FAILED;
101
+ nodeInfo.connection = null;
102
+ }
103
+ this.currentOwner = null;
104
+ // Find the best candidate for new owner
105
+ var newOwner = this.selectNewOwner();
106
+ if (newOwner) {
107
+ this.promoteToOwner(newOwner);
108
+ }
109
+ else {
110
+ this.logger.error('HazelcastFailoverManager', 'No suitable node found for ownership, cluster may be down');
111
+ }
112
+ };
113
+ /**
114
+ * Handles child node disconnection
115
+ * @param address The disconnected child node address
116
+ */
117
+ HazelcastFailoverManager.prototype.handleChildDisconnection = function (address) {
118
+ var addressStr = address.toString();
119
+ this.logger.info('HazelcastFailoverManager', "Child node " + addressStr + " disconnected");
120
+ // Mark as disconnected but keep in nodes map
121
+ var nodeInfo = this.nodes.get(addressStr);
122
+ if (nodeInfo) {
123
+ nodeInfo.state = NodeState.DISCONNECTED;
124
+ nodeInfo.connection = null;
125
+ }
126
+ // No failover needed for child nodes
127
+ };
128
+ /**
129
+ * Selects the best candidate for new owner
130
+ * @returns The address of the best candidate, or null if none available
131
+ */
132
+ HazelcastFailoverManager.prototype.selectNewOwner = function () {
133
+ var candidates = [];
134
+ this.nodes.forEach(function (nodeInfo) {
135
+ if (nodeInfo.state === NodeState.CONNECTED &&
136
+ nodeInfo.connection &&
137
+ nodeInfo.connection.isAlive() &&
138
+ !nodeInfo.isOwner) {
139
+ candidates.push(nodeInfo);
140
+ }
141
+ });
142
+ if (candidates.length === 0) {
143
+ return null;
144
+ }
145
+ // Select the node with the lowest failure count and most recent connection
146
+ candidates.sort(function (a, b) {
147
+ if (a.failureCount !== b.failureCount) {
148
+ return a.failureCount - b.failureCount;
149
+ }
150
+ return b.lastSeen - a.lastSeen;
151
+ });
152
+ return candidates[0].address;
153
+ };
154
+ /**
155
+ * Promotes a node to owner
156
+ * @param address The address of the node to promote
157
+ */
158
+ HazelcastFailoverManager.prototype.promoteToOwner = function (address) {
159
+ var addressStr = address.toString();
160
+ var nodeInfo = this.nodes.get(addressStr);
161
+ if (!nodeInfo) {
162
+ this.logger.error('HazelcastFailoverManager', "Cannot promote " + addressStr + " to owner - node not found");
163
+ return;
164
+ }
165
+ this.logger.info('HazelcastFailoverManager', "Promoting " + addressStr + " to owner");
166
+ // Update node state
167
+ nodeInfo.state = NodeState.OWNER;
168
+ nodeInfo.isOwner = true;
169
+ // Update current owner
170
+ this.currentOwner = address;
171
+ // Notify client of ownership change
172
+ this.client.getClusterService().handleOwnershipChange(address, nodeInfo.connection);
173
+ this.logger.info('HazelcastFailoverManager', "Ownership transferred to " + addressStr);
174
+ };
175
+ /**
176
+ * Handles node reconnection
177
+ * @param address The reconnected node address
178
+ * @param connection The new connection
179
+ */
180
+ HazelcastFailoverManager.prototype.handleNodeReconnection = function (address, connection) {
181
+ var addressStr = address.toString();
182
+ var nodeInfo = this.nodes.get(addressStr);
183
+ if (!nodeInfo) {
184
+ // New node joining the cluster
185
+ this.registerNode(address, connection, false);
186
+ return;
187
+ }
188
+ this.logger.info('HazelcastFailoverManager', "Node " + addressStr + " reconnected, previous state: " + nodeInfo.state);
189
+ // Update connection and state
190
+ nodeInfo.connection = connection;
191
+ nodeInfo.lastSeen = Date.now();
192
+ if (nodeInfo.state === NodeState.FAILED) {
193
+ // Failed node recovered - treat as child
194
+ nodeInfo.state = NodeState.CHILD;
195
+ nodeInfo.isOwner = false;
196
+ this.logger.info('HazelcastFailoverManager', "Failed node " + addressStr + " recovered and rejoined as child");
197
+ }
198
+ else if (nodeInfo.state === NodeState.DISCONNECTED) {
199
+ // Disconnected node reconnected
200
+ nodeInfo.state = nodeInfo.isOwner ? NodeState.OWNER : NodeState.CHILD;
201
+ this.logger.info('HazelcastFailoverManager', "Disconnected node " + addressStr + " reconnected");
202
+ }
203
+ // Stop any ongoing failover/reconnection logic
204
+ this.stopFailoverLogic();
205
+ };
206
+ /**
207
+ * Stops all failover and reconnection logic
208
+ */
209
+ HazelcastFailoverManager.prototype.stopFailoverLogic = function () {
210
+ this.logger.info('HazelcastFailoverManager', 'Stopping all failover and reconnection logic - cluster stable');
211
+ // Reset failure counts for all nodes
212
+ this.nodes.forEach(function (nodeInfo) {
213
+ nodeInfo.failureCount = 0;
214
+ });
215
+ };
216
+ /**
217
+ * Gets the current owner address
218
+ * @returns The current owner address or null if no owner
219
+ */
220
+ HazelcastFailoverManager.prototype.getCurrentOwner = function () {
221
+ return this.currentOwner;
222
+ };
223
+ /**
224
+ * Gets all connected nodes
225
+ * @returns Array of connected node addresses
226
+ */
227
+ HazelcastFailoverManager.prototype.getConnectedNodes = function () {
228
+ var connected = [];
229
+ this.nodes.forEach(function (nodeInfo) {
230
+ if (nodeInfo.state === NodeState.CONNECTED ||
231
+ nodeInfo.state === NodeState.OWNER) {
232
+ connected.push(nodeInfo.address);
233
+ }
234
+ });
235
+ return connected;
236
+ };
237
+ /**
238
+ * Gets the cluster state summary
239
+ * @returns Summary of cluster state
240
+ */
241
+ HazelcastFailoverManager.prototype.getClusterStateSummary = function () {
242
+ var summary = {
243
+ totalNodes: this.nodes.size,
244
+ currentOwner: this.currentOwner ? this.currentOwner.toString() : 'none',
245
+ connectedNodes: this.getConnectedNodes().map(function (addr) { return addr.toString(); }),
246
+ nodeStates: {}
247
+ };
248
+ this.nodes.forEach(function (nodeInfo, addressStr) {
249
+ summary.nodeStates[addressStr] = {
250
+ state: nodeInfo.state,
251
+ isOwner: nodeInfo.isOwner,
252
+ failureCount: nodeInfo.failureCount,
253
+ lastSeen: new Date(nodeInfo.lastSeen).toISOString()
254
+ };
255
+ });
256
+ return summary;
257
+ };
258
+ /**
259
+ * Cleans up failed nodes that haven't recovered
260
+ * @param maxFailureAge Maximum age for failed nodes (default: 5 minutes)
261
+ */
262
+ HazelcastFailoverManager.prototype.cleanupFailedNodes = function (maxFailureAge) {
263
+ var _this = this;
264
+ if (maxFailureAge === void 0) { maxFailureAge = 300000; }
265
+ var now = Date.now();
266
+ var toRemove = [];
267
+ this.nodes.forEach(function (nodeInfo, addressStr) {
268
+ if (nodeInfo.state === NodeState.FAILED) {
269
+ var timeSinceFailure = now - nodeInfo.lastSeen;
270
+ if (timeSinceFailure > maxFailureAge) {
271
+ toRemove.push(addressStr);
272
+ }
273
+ }
274
+ });
275
+ if (toRemove.length > 0) {
276
+ this.logger.info('HazelcastFailoverManager', "Cleaning up " + toRemove.length + " failed nodes");
277
+ toRemove.forEach(function (addressStr) {
278
+ _this.nodes.delete(addressStr);
279
+ _this.logger.debug('HazelcastFailoverManager', "Removed failed node: " + addressStr);
280
+ });
281
+ }
282
+ };
283
+ return HazelcastFailoverManager;
284
+ }());
285
+ exports.HazelcastFailoverManager = HazelcastFailoverManager;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@celerispay/hazelcast-client",
3
- "version": "3.12.5-8",
3
+ "version": "3.12.7-2",
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": {
@@ -38,18 +38,19 @@
38
38
  },
39
39
  "homepage": "https://github.com/celerispay/hazelcast-nodejs-client#readme",
40
40
  "dependencies": {
41
- "bluebird": "3.5.1",
42
- "long": "3.2.0",
43
- "winston": "2.3.1"
41
+ "bluebird": "^3.7.2",
42
+ "long": "^4.0.0",
43
+ "safe-buffer": "^5.2.1"
44
44
  },
45
45
  "devDependencies": {
46
+ "@types/bluebird": "^3.5.21",
47
+ "@types/long": "^3.0.32",
46
48
  "@types/node": "8.0.0",
47
49
  "mocha": "3.2.0",
48
50
  "sinon": "4.0.0",
49
51
  "tslint": "5.7.0",
50
52
  "typedoc": "^0.16.11",
51
- "typescript": "2.8.4",
52
- "winston": "2.3.1"
53
+ "typescript": "2.8.4"
53
54
  },
54
55
  "typings": "./lib/index"
55
56
  }