@tachybase/module-multi-app 1.6.0 → 1.6.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/README.md +34 -34
- package/README.zh-CN.md +34 -34
- package/client.d.ts +1 -1
- package/client.js +1 -1
- package/dist/externalVersion.js +5 -5
- package/dist/locale/en-US.json +48 -48
- package/dist/locale/es-ES.json +9 -9
- package/dist/locale/ko_KR.json +11 -11
- package/dist/locale/pt-BR.json +9 -9
- package/dist/locale/zh-CN.json +58 -58
- package/dist/node_modules/mariadb/callback.js +43 -8
- package/dist/node_modules/mariadb/check-node.js +30 -0
- package/dist/node_modules/mariadb/lib/cluster-callback.js +84 -0
- package/dist/node_modules/mariadb/lib/cluster.js +446 -0
- package/dist/node_modules/mariadb/lib/cmd/batch-bulk.js +576 -177
- package/dist/node_modules/mariadb/lib/cmd/change-user.js +54 -44
- package/dist/node_modules/mariadb/lib/cmd/class/ok-packet.js +3 -2
- package/dist/node_modules/mariadb/lib/cmd/class/prepare-cache-wrapper.js +46 -0
- package/dist/node_modules/mariadb/lib/cmd/class/prepare-result-packet.js +141 -0
- package/dist/node_modules/mariadb/lib/cmd/class/prepare-wrapper.js +70 -0
- package/dist/node_modules/mariadb/lib/cmd/close-prepare.js +38 -0
- package/dist/node_modules/mariadb/lib/cmd/column-definition.js +145 -47
- package/dist/node_modules/mariadb/lib/cmd/command.js +41 -75
- package/dist/node_modules/mariadb/lib/cmd/decoder/binary-decoder.js +282 -0
- package/dist/node_modules/mariadb/lib/cmd/decoder/text-decoder.js +210 -0
- package/dist/node_modules/mariadb/lib/cmd/{common-binary-cmd.js → encoder/binary-encoder.js} +34 -77
- package/dist/node_modules/mariadb/lib/cmd/encoder/text-encoder.js +311 -0
- package/dist/node_modules/mariadb/lib/cmd/execute-stream.js +61 -0
- package/dist/node_modules/mariadb/lib/cmd/execute.js +338 -0
- package/dist/node_modules/mariadb/lib/cmd/handshake/auth/caching-sha2-password-auth.js +25 -62
- package/dist/node_modules/mariadb/lib/cmd/handshake/auth/clear-password-auth.js +39 -6
- package/dist/node_modules/mariadb/lib/cmd/handshake/auth/ed25519-password-auth.js +48 -16
- package/dist/node_modules/mariadb/lib/cmd/handshake/auth/handshake.js +198 -0
- package/dist/node_modules/mariadb/lib/cmd/handshake/{initial-handshake.js → auth/initial-handshake.js} +10 -8
- package/dist/node_modules/mariadb/lib/cmd/handshake/auth/native-password-auth.js +22 -9
- package/dist/node_modules/mariadb/lib/cmd/handshake/auth/pam-password-auth.js +9 -4
- package/dist/node_modules/mariadb/lib/cmd/handshake/auth/parsec-auth.js +115 -0
- package/dist/node_modules/mariadb/lib/cmd/handshake/auth/plugin-auth.js +12 -5
- package/dist/node_modules/mariadb/lib/cmd/handshake/auth/sha256-password-auth.js +44 -33
- package/dist/node_modules/mariadb/lib/cmd/handshake/authentication.js +335 -0
- package/dist/node_modules/mariadb/lib/cmd/handshake/client-capabilities.js +20 -19
- package/dist/node_modules/mariadb/lib/cmd/handshake/ssl-request.js +6 -3
- package/dist/node_modules/mariadb/lib/cmd/parser.js +861 -0
- package/dist/node_modules/mariadb/lib/cmd/ping.js +17 -18
- package/dist/node_modules/mariadb/lib/cmd/prepare.js +170 -0
- package/dist/node_modules/mariadb/lib/cmd/query.js +281 -144
- package/dist/node_modules/mariadb/lib/cmd/quit.js +9 -6
- package/dist/node_modules/mariadb/lib/cmd/reset.js +15 -19
- package/dist/node_modules/mariadb/lib/cmd/stream.js +21 -6
- package/dist/node_modules/mariadb/lib/config/cluster-options.js +23 -0
- package/dist/node_modules/mariadb/lib/config/connection-options.js +196 -132
- package/dist/node_modules/mariadb/lib/config/pool-options.js +27 -19
- package/dist/node_modules/mariadb/lib/connection-callback.js +492 -120
- package/dist/node_modules/mariadb/lib/connection-promise.js +372 -0
- package/dist/node_modules/mariadb/lib/connection.js +1739 -1016
- package/dist/node_modules/mariadb/lib/const/capabilities.js +36 -30
- package/dist/node_modules/mariadb/lib/const/collations.js +972 -36
- package/dist/node_modules/mariadb/lib/const/connection_status.js +3 -0
- package/dist/node_modules/mariadb/lib/const/error-code.js +35 -11
- package/dist/node_modules/mariadb/lib/const/field-detail.js +3 -0
- package/dist/node_modules/mariadb/lib/const/field-type.js +7 -4
- package/dist/node_modules/mariadb/lib/const/server-status.js +4 -1
- package/dist/node_modules/mariadb/lib/const/state-change.js +3 -0
- package/dist/node_modules/mariadb/lib/filtered-cluster-callback.js +136 -0
- package/dist/node_modules/mariadb/lib/filtered-cluster.js +118 -0
- package/dist/node_modules/mariadb/lib/io/compression-input-stream.js +14 -13
- package/dist/node_modules/mariadb/lib/io/compression-output-stream.js +21 -18
- package/dist/node_modules/mariadb/lib/io/packet-input-stream.js +75 -64
- package/dist/node_modules/mariadb/lib/io/packet-node-encoded.js +13 -9
- package/dist/node_modules/mariadb/lib/io/packet-node-iconv.js +12 -10
- package/dist/node_modules/mariadb/lib/io/packet-output-stream.js +402 -134
- package/dist/node_modules/mariadb/lib/io/packet.js +287 -202
- package/dist/node_modules/mariadb/lib/lru-prepare-cache.js +84 -0
- package/dist/node_modules/mariadb/lib/misc/connection-information.js +15 -32
- package/dist/node_modules/mariadb/lib/misc/errors.js +68 -25
- package/dist/node_modules/mariadb/lib/misc/parse.js +207 -711
- package/dist/node_modules/mariadb/lib/misc/utils.js +34 -62
- package/dist/node_modules/mariadb/lib/pool-callback.js +213 -174
- package/dist/node_modules/mariadb/lib/pool-promise.js +228 -94
- package/dist/node_modules/mariadb/lib/pool.js +951 -0
- package/dist/node_modules/mariadb/package.json +1 -1
- package/dist/node_modules/mariadb/promise.js +1 -34
- package/dist/node_modules/mariadb/types/callback.d.ts +207 -0
- package/dist/node_modules/mariadb/types/index.d.ts +94 -674
- package/dist/node_modules/mariadb/types/share.d.ts +804 -0
- package/dist/node_modules/qs/package.json +1 -1
- package/dist/server/actions/apps.js +2 -2
- package/dist/server/app-lifecycle.d.ts +1 -1
- package/dist/server/app-lifecycle.js +4 -4
- package/dist/server/models/application.d.ts +1 -1
- package/package.json +7 -7
- package/server.d.ts +2 -2
- package/server.js +1 -1
- package/dist/node_modules/mariadb/lib/cmd/batch-rewrite.js +0 -372
- package/dist/node_modules/mariadb/lib/cmd/common-text-cmd.js +0 -427
- package/dist/node_modules/mariadb/lib/cmd/handshake/client-handshake-response.js +0 -126
- package/dist/node_modules/mariadb/lib/cmd/handshake/handshake.js +0 -292
- package/dist/node_modules/mariadb/lib/cmd/resultset.js +0 -607
- package/dist/node_modules/mariadb/lib/config/pool-cluster-options.js +0 -19
- package/dist/node_modules/mariadb/lib/filtered-pool-cluster.js +0 -81
- package/dist/node_modules/mariadb/lib/io/bulk-packet.js +0 -590
- package/dist/node_modules/mariadb/lib/io/rewrite-packet.js +0 -481
- package/dist/node_modules/mariadb/lib/pool-base.js +0 -611
- package/dist/node_modules/mariadb/lib/pool-cluster-callback.js +0 -66
- package/dist/node_modules/mariadb/lib/pool-cluster.js +0 -407
|
@@ -0,0 +1,446 @@
|
|
|
1
|
+
// SPDX-License-Identifier: LGPL-2.1-or-later
|
|
2
|
+
// Copyright (c) 2015-2025 MariaDB Corporation Ab
|
|
3
|
+
|
|
4
|
+
'use strict';
|
|
5
|
+
|
|
6
|
+
const ClusterOptions = require('./config/cluster-options');
|
|
7
|
+
const PoolOptions = require('./config/pool-options');
|
|
8
|
+
const PoolCallback = require('./pool-callback');
|
|
9
|
+
const PoolPromise = require('./pool-promise');
|
|
10
|
+
const FilteredCluster = require('./filtered-cluster');
|
|
11
|
+
const FilteredClusterCallback = require('./filtered-cluster-callback');
|
|
12
|
+
const EventEmitter = require('events');
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Create a new Cluster.
|
|
16
|
+
* Cluster handle pools with patterns and handle failover / distributed load
|
|
17
|
+
* according to selectors (round-robin / random / ordered )
|
|
18
|
+
*
|
|
19
|
+
* @param args cluster arguments. see pool-cluster-options.
|
|
20
|
+
* @constructor
|
|
21
|
+
*/
|
|
22
|
+
class Cluster extends EventEmitter {
|
|
23
|
+
#opts;
|
|
24
|
+
#nodes = {};
|
|
25
|
+
#cachedPatterns = {};
|
|
26
|
+
#nodeCounter = 0;
|
|
27
|
+
|
|
28
|
+
constructor(args) {
|
|
29
|
+
super();
|
|
30
|
+
this.#opts = new ClusterOptions(args);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Add a new pool node to the cluster.
|
|
35
|
+
*
|
|
36
|
+
* @param id identifier
|
|
37
|
+
* @param config pool configuration
|
|
38
|
+
*/
|
|
39
|
+
add(id, config) {
|
|
40
|
+
let identifier;
|
|
41
|
+
if (typeof id === 'string' || id instanceof String) {
|
|
42
|
+
identifier = id;
|
|
43
|
+
if (this.#nodes[identifier]) throw new Error(`Node identifier '${identifier}' already exist !`);
|
|
44
|
+
} else {
|
|
45
|
+
identifier = 'PoolNode-' + this.#nodeCounter++;
|
|
46
|
+
config = id;
|
|
47
|
+
}
|
|
48
|
+
const options = new PoolOptions(config);
|
|
49
|
+
this.#nodes[identifier] = this._createPool(options);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* End cluster (and underlying pools).
|
|
54
|
+
*
|
|
55
|
+
* @return {Promise<any[]>}
|
|
56
|
+
*/
|
|
57
|
+
end() {
|
|
58
|
+
const cluster = this;
|
|
59
|
+
this.#cachedPatterns = {};
|
|
60
|
+
const poolEndPromise = [];
|
|
61
|
+
Object.keys(this.#nodes).forEach((pool) => {
|
|
62
|
+
const res = cluster.#nodes[pool].end();
|
|
63
|
+
if (res) poolEndPromise.push(res);
|
|
64
|
+
});
|
|
65
|
+
this.#nodes = null;
|
|
66
|
+
return Promise.all(poolEndPromise);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
of(pattern, selector) {
|
|
70
|
+
return new FilteredCluster(this, pattern, selector);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
_ofCallback(pattern, selector) {
|
|
74
|
+
return new FilteredClusterCallback(this, pattern, selector);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Remove nodes according to pattern.
|
|
79
|
+
*
|
|
80
|
+
* @param pattern pattern
|
|
81
|
+
*/
|
|
82
|
+
remove(pattern) {
|
|
83
|
+
if (!pattern) throw new Error('pattern parameter in Cluster.remove(pattern) is mandatory');
|
|
84
|
+
|
|
85
|
+
const regex = RegExp(pattern);
|
|
86
|
+
Object.keys(this.#nodes).forEach(
|
|
87
|
+
function (key) {
|
|
88
|
+
if (regex.test(key)) {
|
|
89
|
+
this.#nodes[key].end();
|
|
90
|
+
delete this.#nodes[key];
|
|
91
|
+
this.#cachedPatterns = {};
|
|
92
|
+
}
|
|
93
|
+
}.bind(this)
|
|
94
|
+
);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Get connection from an available pools matching pattern, according to selector
|
|
99
|
+
*
|
|
100
|
+
* @param pattern pattern filter (not mandatory)
|
|
101
|
+
* @param selector node selector ('RR','RANDOM' or 'ORDER')
|
|
102
|
+
* @return {Promise}
|
|
103
|
+
*/
|
|
104
|
+
getConnection(pattern, selector) {
|
|
105
|
+
return this._getConnection(pattern, selector, undefined, undefined, undefined);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Force using callback methods.
|
|
110
|
+
*/
|
|
111
|
+
_setCallback() {
|
|
112
|
+
this.getConnection = this._getConnectionCallback;
|
|
113
|
+
this._createPool = this._createPoolCallback;
|
|
114
|
+
this.of = this._ofCallback;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Get connection from an available pools matching pattern, according to selector
|
|
119
|
+
* with additional parameter to avoid reusing failing node
|
|
120
|
+
*
|
|
121
|
+
* @param pattern pattern filter (not mandatory)
|
|
122
|
+
* @param selector node selector ('RR','RANDOM' or 'ORDER')
|
|
123
|
+
* @param avoidNodeKey failing node
|
|
124
|
+
* @param lastError last error
|
|
125
|
+
* @param remainingRetry remaining possible retry
|
|
126
|
+
* @return {Promise}
|
|
127
|
+
* @private
|
|
128
|
+
*/
|
|
129
|
+
_getConnection(pattern, selector, remainingRetry, avoidNodeKey, lastError) {
|
|
130
|
+
const matchingNodeList = this._matchingNodes(pattern || /^/);
|
|
131
|
+
|
|
132
|
+
if (matchingNodeList.length === 0) {
|
|
133
|
+
if (Object.keys(this.#nodes).length === 0 && !lastError) {
|
|
134
|
+
return Promise.reject(
|
|
135
|
+
new Error('No node have been added to cluster or nodes have been removed due to too much connection error')
|
|
136
|
+
);
|
|
137
|
+
}
|
|
138
|
+
if (avoidNodeKey === undefined) return Promise.reject(new Error(`No node found for pattern '${pattern}'`));
|
|
139
|
+
const errMsg = `No Connection available for '${pattern}'${
|
|
140
|
+
lastError ? '. Last connection error was: ' + lastError.message : ''
|
|
141
|
+
}`;
|
|
142
|
+
return Promise.reject(new Error(errMsg));
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
if (remainingRetry === undefined) remainingRetry = matchingNodeList.length;
|
|
146
|
+
const retry = --remainingRetry >= 0 ? this._getConnection.bind(this, pattern, selector, remainingRetry) : null;
|
|
147
|
+
|
|
148
|
+
try {
|
|
149
|
+
const nodeKey = this._selectPool(matchingNodeList, selector, avoidNodeKey);
|
|
150
|
+
return this._handleConnectionError(matchingNodeList, nodeKey, retry);
|
|
151
|
+
} catch (e) {
|
|
152
|
+
return Promise.reject(e);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
_createPool(options) {
|
|
157
|
+
const pool = new PoolPromise(options);
|
|
158
|
+
pool.on('error', (err) => {});
|
|
159
|
+
return pool;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
_createPoolCallback(options) {
|
|
163
|
+
const pool = new PoolCallback(options);
|
|
164
|
+
pool.on('error', (err) => {});
|
|
165
|
+
return pool;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* Get connection from an available pools matching pattern, according to selector
|
|
170
|
+
* with additional parameter to avoid reusing failing node
|
|
171
|
+
*
|
|
172
|
+
* @param pattern pattern filter (not mandatory)
|
|
173
|
+
* @param selector node selector ('RR','RANDOM' or 'ORDER')
|
|
174
|
+
* @param callback callback function
|
|
175
|
+
* @param remainingRetry remaining retry
|
|
176
|
+
* @param avoidNodeKey failing node
|
|
177
|
+
* @param lastError last error
|
|
178
|
+
* @private
|
|
179
|
+
*/
|
|
180
|
+
_getConnectionCallback(pattern, selector, callback, remainingRetry, avoidNodeKey, lastError) {
|
|
181
|
+
const matchingNodeList = this._matchingNodes(pattern || /^/);
|
|
182
|
+
|
|
183
|
+
if (matchingNodeList.length === 0) {
|
|
184
|
+
if (Object.keys(this.#nodes).length === 0 && !lastError) {
|
|
185
|
+
callback(
|
|
186
|
+
new Error('No node have been added to cluster or nodes have been removed due to too much connection error')
|
|
187
|
+
);
|
|
188
|
+
return;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
if (avoidNodeKey === undefined) callback(new Error(`No node found for pattern '${pattern}'`));
|
|
192
|
+
const errMsg = `No Connection available for '${pattern}'${
|
|
193
|
+
lastError ? '. Last connection error was: ' + lastError.message : ''
|
|
194
|
+
}`;
|
|
195
|
+
callback(new Error(errMsg));
|
|
196
|
+
return;
|
|
197
|
+
}
|
|
198
|
+
if (remainingRetry === undefined) remainingRetry = matchingNodeList.length;
|
|
199
|
+
const retry =
|
|
200
|
+
--remainingRetry >= 0
|
|
201
|
+
? this._getConnectionCallback.bind(this, pattern, selector, callback, remainingRetry)
|
|
202
|
+
: null;
|
|
203
|
+
try {
|
|
204
|
+
const nodeKey = this._selectPool(matchingNodeList, selector, avoidNodeKey);
|
|
205
|
+
this._handleConnectionCallbackError(matchingNodeList, nodeKey, retry, callback);
|
|
206
|
+
} catch (e) {
|
|
207
|
+
callback(e);
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* Selecting nodes according to pattern.
|
|
213
|
+
*
|
|
214
|
+
* @param pattern pattern
|
|
215
|
+
* @return {*}
|
|
216
|
+
* @private
|
|
217
|
+
*/
|
|
218
|
+
_matchingNodes(pattern) {
|
|
219
|
+
if (this.#cachedPatterns[pattern]) return this.#cachedPatterns[pattern];
|
|
220
|
+
|
|
221
|
+
const regex = RegExp(pattern);
|
|
222
|
+
const matchingNodeList = [];
|
|
223
|
+
Object.keys(this.#nodes).forEach((key) => {
|
|
224
|
+
if (regex.test(key)) {
|
|
225
|
+
matchingNodeList.push(key);
|
|
226
|
+
}
|
|
227
|
+
});
|
|
228
|
+
|
|
229
|
+
this.#cachedPatterns[pattern] = matchingNodeList;
|
|
230
|
+
return matchingNodeList;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
/**
|
|
234
|
+
* Select the next node to be chosen in the nodeList according to selector and failed nodes.
|
|
235
|
+
*
|
|
236
|
+
* @param nodeList current node list
|
|
237
|
+
* @param selectorParam selector
|
|
238
|
+
* @param avoidNodeKey last failing node to avoid selecting this one.
|
|
239
|
+
* @return {Promise}
|
|
240
|
+
* @private
|
|
241
|
+
*/
|
|
242
|
+
_selectPool(nodeList, selectorParam, avoidNodeKey) {
|
|
243
|
+
const selector = selectorParam || this.#opts.defaultSelector;
|
|
244
|
+
|
|
245
|
+
let selectorFct;
|
|
246
|
+
switch (selector) {
|
|
247
|
+
case 'RR':
|
|
248
|
+
selectorFct = roundRobinSelector;
|
|
249
|
+
break;
|
|
250
|
+
|
|
251
|
+
case 'RANDOM':
|
|
252
|
+
selectorFct = randomSelector;
|
|
253
|
+
break;
|
|
254
|
+
|
|
255
|
+
case 'ORDER':
|
|
256
|
+
selectorFct = orderedSelector;
|
|
257
|
+
break;
|
|
258
|
+
|
|
259
|
+
default:
|
|
260
|
+
throw new Error(`Wrong selector value '${selector}'. Possible values are 'RR','RANDOM' or 'ORDER'`);
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
let nodeIdx = 0;
|
|
264
|
+
let nodeKey = selectorFct(nodeList, nodeIdx);
|
|
265
|
+
// first loop : search for node not blacklisted AND not the avoided key
|
|
266
|
+
while (
|
|
267
|
+
(avoidNodeKey === nodeKey ||
|
|
268
|
+
(this.#nodes[nodeKey].blacklistedUntil && this.#nodes[nodeKey].blacklistedUntil > Date.now())) &&
|
|
269
|
+
nodeIdx < nodeList.length - 1
|
|
270
|
+
) {
|
|
271
|
+
nodeIdx++;
|
|
272
|
+
nodeKey = selectorFct(nodeList, nodeIdx);
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
if (avoidNodeKey === nodeKey) {
|
|
276
|
+
// second loop, search even in blacklisted node in order to choose a different node than to be avoided
|
|
277
|
+
nodeIdx = 0;
|
|
278
|
+
while (avoidNodeKey === nodeKey && nodeIdx < nodeList.length - 1) {
|
|
279
|
+
nodeIdx++;
|
|
280
|
+
nodeKey = selectorFct(nodeList, nodeIdx);
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
return nodeKey;
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
/**
|
|
288
|
+
* Handle node blacklisting and potential removal after a connection error
|
|
289
|
+
*
|
|
290
|
+
* @param {string} nodeKey - The key of the node that failed
|
|
291
|
+
* @param {Array<string>} nodeList - List of available nodes
|
|
292
|
+
* @returns {void}
|
|
293
|
+
* @private
|
|
294
|
+
*/
|
|
295
|
+
_handleNodeFailure(nodeKey, nodeList) {
|
|
296
|
+
const node = this.#nodes[nodeKey];
|
|
297
|
+
if (!node) return;
|
|
298
|
+
|
|
299
|
+
const cluster = this;
|
|
300
|
+
|
|
301
|
+
// Increment error count and blacklist node temporarily
|
|
302
|
+
node.errorCount = node.errorCount ? node.errorCount + 1 : 1;
|
|
303
|
+
node.blacklistedUntil = Date.now() + cluster.#opts.restoreNodeTimeout;
|
|
304
|
+
|
|
305
|
+
// Check if node should be removed due to excessive errors
|
|
306
|
+
if (
|
|
307
|
+
cluster.#opts.removeNodeErrorCount &&
|
|
308
|
+
node.errorCount >= cluster.#opts.removeNodeErrorCount &&
|
|
309
|
+
cluster.#nodes[nodeKey]
|
|
310
|
+
) {
|
|
311
|
+
delete cluster.#nodes[nodeKey];
|
|
312
|
+
cluster.#cachedPatterns = {};
|
|
313
|
+
delete nodeList.lastRrIdx;
|
|
314
|
+
setImmediate(cluster.emit.bind(cluster, 'remove', nodeKey));
|
|
315
|
+
|
|
316
|
+
if (node instanceof PoolCallback) {
|
|
317
|
+
node.end(() => {
|
|
318
|
+
// Intentionally ignore error during cleanup
|
|
319
|
+
});
|
|
320
|
+
} else {
|
|
321
|
+
node.end().catch((err) => {
|
|
322
|
+
// Intentionally ignore error during cleanup
|
|
323
|
+
});
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
/**
|
|
329
|
+
* Connect, or if fail handle retry / set timeout error
|
|
330
|
+
*
|
|
331
|
+
* @param nodeList current node list
|
|
332
|
+
* @param nodeKey node name to connect
|
|
333
|
+
* @param retryFct retry function
|
|
334
|
+
* @return {Promise}
|
|
335
|
+
* @private
|
|
336
|
+
*/
|
|
337
|
+
_handleConnectionError(nodeList, nodeKey, retryFct) {
|
|
338
|
+
const cluster = this;
|
|
339
|
+
const node = this.#nodes[nodeKey];
|
|
340
|
+
|
|
341
|
+
return node
|
|
342
|
+
.getConnection()
|
|
343
|
+
.then((conn) => {
|
|
344
|
+
// Connection successful, reset error state
|
|
345
|
+
node.blacklistedUntil = null;
|
|
346
|
+
node.errorCount = 0;
|
|
347
|
+
return conn;
|
|
348
|
+
})
|
|
349
|
+
.catch((err) => {
|
|
350
|
+
this._handleNodeFailure(nodeKey, nodeList);
|
|
351
|
+
|
|
352
|
+
if (nodeList.length !== 0 && cluster.#opts.canRetry && retryFct) {
|
|
353
|
+
return retryFct(nodeKey, err);
|
|
354
|
+
}
|
|
355
|
+
return Promise.reject(err);
|
|
356
|
+
});
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
/**
|
|
360
|
+
* Connect, or if fail handle retry / set timeout error
|
|
361
|
+
*
|
|
362
|
+
* @param nodeList current node list
|
|
363
|
+
* @param nodeKey node name to connect
|
|
364
|
+
* @param retryFct retry function
|
|
365
|
+
* @param callback callback function
|
|
366
|
+
* @private
|
|
367
|
+
*/
|
|
368
|
+
_handleConnectionCallbackError(nodeList, nodeKey, retryFct, callback) {
|
|
369
|
+
const cluster = this;
|
|
370
|
+
const node = this.#nodes[nodeKey];
|
|
371
|
+
|
|
372
|
+
node.getConnection((err, conn) => {
|
|
373
|
+
if (err) {
|
|
374
|
+
this._handleNodeFailure(nodeKey, nodeList);
|
|
375
|
+
|
|
376
|
+
if (nodeList.length !== 0 && cluster.#opts.canRetry && retryFct) {
|
|
377
|
+
return retryFct(nodeKey, err);
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
callback(err);
|
|
381
|
+
} else {
|
|
382
|
+
// Connection successful, reset error state
|
|
383
|
+
node.blacklistedUntil = null;
|
|
384
|
+
node.errorCount = 0;
|
|
385
|
+
callback(null, conn);
|
|
386
|
+
}
|
|
387
|
+
});
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
//*****************************************************************
|
|
391
|
+
// internal public testing methods
|
|
392
|
+
//*****************************************************************
|
|
393
|
+
|
|
394
|
+
get __tests() {
|
|
395
|
+
return new TestMethods(this.#nodes);
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
class TestMethods {
|
|
400
|
+
#nodes;
|
|
401
|
+
|
|
402
|
+
constructor(nodes) {
|
|
403
|
+
this.#nodes = nodes;
|
|
404
|
+
}
|
|
405
|
+
getNodes() {
|
|
406
|
+
return this.#nodes;
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
/**
|
|
411
|
+
* Round robin selector: using nodes one after the other.
|
|
412
|
+
*
|
|
413
|
+
* @param nodeList node list
|
|
414
|
+
* @return {String}
|
|
415
|
+
*/
|
|
416
|
+
const roundRobinSelector = (nodeList) => {
|
|
417
|
+
let lastRoundRobin = nodeList.lastRrIdx;
|
|
418
|
+
if (lastRoundRobin === undefined) lastRoundRobin = -1;
|
|
419
|
+
if (++lastRoundRobin >= nodeList.length) lastRoundRobin = 0;
|
|
420
|
+
nodeList.lastRrIdx = lastRoundRobin;
|
|
421
|
+
return nodeList[lastRoundRobin];
|
|
422
|
+
};
|
|
423
|
+
|
|
424
|
+
/**
|
|
425
|
+
* Random selector: use a random node.
|
|
426
|
+
*
|
|
427
|
+
* @param {Array<string>} nodeList - List of available nodes
|
|
428
|
+
* @return {String} - Selected node key
|
|
429
|
+
*/
|
|
430
|
+
const randomSelector = (nodeList) => {
|
|
431
|
+
const randomIdx = Math.floor(Math.random() * nodeList.length);
|
|
432
|
+
return nodeList[randomIdx];
|
|
433
|
+
};
|
|
434
|
+
|
|
435
|
+
/**
|
|
436
|
+
* Ordered selector: always use the nodes in sequence, unless failing.
|
|
437
|
+
*
|
|
438
|
+
* @param nodeList node list
|
|
439
|
+
* @param retry sequence number if last node is tagged has failing
|
|
440
|
+
* @return {String}
|
|
441
|
+
*/
|
|
442
|
+
const orderedSelector = (nodeList, retry) => {
|
|
443
|
+
return nodeList[retry];
|
|
444
|
+
};
|
|
445
|
+
|
|
446
|
+
module.exports = Cluster;
|