@tachybase/module-multi-app 1.6.0 → 1.6.1

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.
Files changed (94) hide show
  1. package/dist/externalVersion.js +5 -5
  2. package/dist/node_modules/mariadb/callback.js +43 -8
  3. package/dist/node_modules/mariadb/check-node.js +30 -0
  4. package/dist/node_modules/mariadb/lib/cluster-callback.js +84 -0
  5. package/dist/node_modules/mariadb/lib/cluster.js +446 -0
  6. package/dist/node_modules/mariadb/lib/cmd/batch-bulk.js +576 -177
  7. package/dist/node_modules/mariadb/lib/cmd/change-user.js +54 -44
  8. package/dist/node_modules/mariadb/lib/cmd/class/ok-packet.js +3 -2
  9. package/dist/node_modules/mariadb/lib/cmd/class/prepare-cache-wrapper.js +46 -0
  10. package/dist/node_modules/mariadb/lib/cmd/class/prepare-result-packet.js +141 -0
  11. package/dist/node_modules/mariadb/lib/cmd/class/prepare-wrapper.js +70 -0
  12. package/dist/node_modules/mariadb/lib/cmd/close-prepare.js +38 -0
  13. package/dist/node_modules/mariadb/lib/cmd/column-definition.js +145 -47
  14. package/dist/node_modules/mariadb/lib/cmd/command.js +41 -75
  15. package/dist/node_modules/mariadb/lib/cmd/decoder/binary-decoder.js +282 -0
  16. package/dist/node_modules/mariadb/lib/cmd/decoder/text-decoder.js +210 -0
  17. package/dist/node_modules/mariadb/lib/cmd/{common-binary-cmd.js → encoder/binary-encoder.js} +34 -77
  18. package/dist/node_modules/mariadb/lib/cmd/encoder/text-encoder.js +311 -0
  19. package/dist/node_modules/mariadb/lib/cmd/execute-stream.js +61 -0
  20. package/dist/node_modules/mariadb/lib/cmd/execute.js +338 -0
  21. package/dist/node_modules/mariadb/lib/cmd/handshake/auth/caching-sha2-password-auth.js +25 -62
  22. package/dist/node_modules/mariadb/lib/cmd/handshake/auth/clear-password-auth.js +39 -6
  23. package/dist/node_modules/mariadb/lib/cmd/handshake/auth/ed25519-password-auth.js +48 -16
  24. package/dist/node_modules/mariadb/lib/cmd/handshake/auth/handshake.js +198 -0
  25. package/dist/node_modules/mariadb/lib/cmd/handshake/{initial-handshake.js → auth/initial-handshake.js} +10 -8
  26. package/dist/node_modules/mariadb/lib/cmd/handshake/auth/native-password-auth.js +22 -9
  27. package/dist/node_modules/mariadb/lib/cmd/handshake/auth/pam-password-auth.js +9 -4
  28. package/dist/node_modules/mariadb/lib/cmd/handshake/auth/parsec-auth.js +115 -0
  29. package/dist/node_modules/mariadb/lib/cmd/handshake/auth/plugin-auth.js +12 -5
  30. package/dist/node_modules/mariadb/lib/cmd/handshake/auth/sha256-password-auth.js +44 -33
  31. package/dist/node_modules/mariadb/lib/cmd/handshake/authentication.js +335 -0
  32. package/dist/node_modules/mariadb/lib/cmd/handshake/client-capabilities.js +20 -19
  33. package/dist/node_modules/mariadb/lib/cmd/handshake/ssl-request.js +6 -3
  34. package/dist/node_modules/mariadb/lib/cmd/parser.js +861 -0
  35. package/dist/node_modules/mariadb/lib/cmd/ping.js +17 -18
  36. package/dist/node_modules/mariadb/lib/cmd/prepare.js +170 -0
  37. package/dist/node_modules/mariadb/lib/cmd/query.js +281 -144
  38. package/dist/node_modules/mariadb/lib/cmd/quit.js +9 -6
  39. package/dist/node_modules/mariadb/lib/cmd/reset.js +15 -19
  40. package/dist/node_modules/mariadb/lib/cmd/stream.js +21 -6
  41. package/dist/node_modules/mariadb/lib/config/cluster-options.js +23 -0
  42. package/dist/node_modules/mariadb/lib/config/connection-options.js +196 -132
  43. package/dist/node_modules/mariadb/lib/config/pool-options.js +27 -19
  44. package/dist/node_modules/mariadb/lib/connection-callback.js +492 -120
  45. package/dist/node_modules/mariadb/lib/connection-promise.js +372 -0
  46. package/dist/node_modules/mariadb/lib/connection.js +1739 -1016
  47. package/dist/node_modules/mariadb/lib/const/capabilities.js +36 -30
  48. package/dist/node_modules/mariadb/lib/const/collations.js +972 -36
  49. package/dist/node_modules/mariadb/lib/const/connection_status.js +3 -0
  50. package/dist/node_modules/mariadb/lib/const/error-code.js +35 -11
  51. package/dist/node_modules/mariadb/lib/const/field-detail.js +3 -0
  52. package/dist/node_modules/mariadb/lib/const/field-type.js +7 -4
  53. package/dist/node_modules/mariadb/lib/const/server-status.js +4 -1
  54. package/dist/node_modules/mariadb/lib/const/state-change.js +3 -0
  55. package/dist/node_modules/mariadb/lib/filtered-cluster-callback.js +136 -0
  56. package/dist/node_modules/mariadb/lib/filtered-cluster.js +118 -0
  57. package/dist/node_modules/mariadb/lib/io/compression-input-stream.js +14 -13
  58. package/dist/node_modules/mariadb/lib/io/compression-output-stream.js +21 -18
  59. package/dist/node_modules/mariadb/lib/io/packet-input-stream.js +75 -64
  60. package/dist/node_modules/mariadb/lib/io/packet-node-encoded.js +13 -9
  61. package/dist/node_modules/mariadb/lib/io/packet-node-iconv.js +12 -10
  62. package/dist/node_modules/mariadb/lib/io/packet-output-stream.js +402 -134
  63. package/dist/node_modules/mariadb/lib/io/packet.js +287 -202
  64. package/dist/node_modules/mariadb/lib/lru-prepare-cache.js +84 -0
  65. package/dist/node_modules/mariadb/lib/misc/connection-information.js +15 -32
  66. package/dist/node_modules/mariadb/lib/misc/errors.js +68 -25
  67. package/dist/node_modules/mariadb/lib/misc/parse.js +207 -711
  68. package/dist/node_modules/mariadb/lib/misc/utils.js +34 -62
  69. package/dist/node_modules/mariadb/lib/pool-callback.js +213 -174
  70. package/dist/node_modules/mariadb/lib/pool-promise.js +228 -94
  71. package/dist/node_modules/mariadb/lib/pool.js +951 -0
  72. package/dist/node_modules/mariadb/package.json +1 -1
  73. package/dist/node_modules/mariadb/promise.js +1 -34
  74. package/dist/node_modules/mariadb/types/callback.d.ts +207 -0
  75. package/dist/node_modules/mariadb/types/index.d.ts +94 -674
  76. package/dist/node_modules/mariadb/types/share.d.ts +804 -0
  77. package/dist/node_modules/qs/package.json +1 -1
  78. package/dist/server/actions/apps.js +2 -2
  79. package/dist/server/app-lifecycle.d.ts +1 -1
  80. package/dist/server/app-lifecycle.js +4 -4
  81. package/dist/server/models/application.d.ts +1 -1
  82. package/package.json +7 -7
  83. package/dist/node_modules/mariadb/lib/cmd/batch-rewrite.js +0 -372
  84. package/dist/node_modules/mariadb/lib/cmd/common-text-cmd.js +0 -427
  85. package/dist/node_modules/mariadb/lib/cmd/handshake/client-handshake-response.js +0 -126
  86. package/dist/node_modules/mariadb/lib/cmd/handshake/handshake.js +0 -292
  87. package/dist/node_modules/mariadb/lib/cmd/resultset.js +0 -607
  88. package/dist/node_modules/mariadb/lib/config/pool-cluster-options.js +0 -19
  89. package/dist/node_modules/mariadb/lib/filtered-pool-cluster.js +0 -81
  90. package/dist/node_modules/mariadb/lib/io/bulk-packet.js +0 -590
  91. package/dist/node_modules/mariadb/lib/io/rewrite-packet.js +0 -481
  92. package/dist/node_modules/mariadb/lib/pool-base.js +0 -611
  93. package/dist/node_modules/mariadb/lib/pool-cluster-callback.js +0 -66
  94. package/dist/node_modules/mariadb/lib/pool-cluster.js +0 -407
@@ -1,14 +1,14 @@
1
1
  module.exports = {
2
2
  "react-i18next": "16.2.1",
3
- "@tachybase/client": "1.6.0",
4
- "@tego/server": "1.3.52",
3
+ "@tachybase/client": "1.6.1",
4
+ "@tego/server": "1.6.0-alpha.9",
5
5
  "lodash": "4.17.21",
6
6
  "mysql2": "3.14.3",
7
7
  "pg": "8.16.3",
8
8
  "react": "18.3.1",
9
- "@tachybase/schema": "1.3.52",
9
+ "@tachybase/schema": "1.6.0-alpha.9",
10
10
  "antd": "5.22.5",
11
11
  "react-router-dom": "6.28.1",
12
- "@tego/client": "1.3.52",
13
- "@ant-design/icons": "5.6.1"
12
+ "@tego/client": "1.6.0-alpha.9",
13
+ "@ant-design/icons": "6.1.0"
14
14
  };
@@ -1,14 +1,18 @@
1
+ // SPDX-License-Identifier: LGPL-2.1-or-later
2
+ // Copyright (c) 2015-2024 MariaDB Corporation Ab
3
+
1
4
  'use strict';
2
- const pkg = require('./package.json');
3
- require('please-upgrade-node')(pkg);
5
+
6
+ require('./check-node');
4
7
 
5
8
  const ConnectionCallback = require('./lib/connection-callback');
6
- const PoolClusterCallback = require('./lib/pool-cluster-callback');
9
+ const ClusterCallback = require('./lib/cluster-callback');
7
10
  const PoolCallback = require('./lib/pool-callback');
8
11
 
9
12
  const ConnOptions = require('./lib/config/connection-options');
10
13
  const PoolOptions = require('./lib/config/pool-options');
11
- const PoolClusterOptions = require('./lib/config/pool-cluster-options');
14
+ const ClusterOptions = require('./lib/config/cluster-options');
15
+ const Connection = require('./lib/connection');
12
16
 
13
17
  module.exports.version = require('./package.json').version;
14
18
  module.exports.SqlError = require('./lib/misc/errors').SqlError;
@@ -25,17 +29,48 @@ module.exports.defaultOptions = function defaultOptions(opts) {
25
29
  };
26
30
 
27
31
  module.exports.createConnection = function createConnection(opts) {
28
- return new ConnectionCallback(new ConnOptions(opts));
32
+ const conn = new Connection(new ConnOptions(opts));
33
+ const connCallback = new ConnectionCallback(conn);
34
+ conn
35
+ .connect()
36
+ .then(
37
+ function () {
38
+ conn.emit('connect');
39
+ }.bind(conn)
40
+ )
41
+ .catch(conn.emit.bind(conn, 'connect'));
42
+ return connCallback;
29
43
  };
30
44
 
31
45
  exports.createPool = function createPool(opts) {
32
46
  const options = new PoolOptions(opts);
33
47
  const pool = new PoolCallback(options);
34
- pool.initialize();
48
+ // adding a default error handler to avoid exiting application on connection error.
49
+ pool.on('error', (err) => {});
35
50
  return pool;
36
51
  };
37
52
 
38
53
  exports.createPoolCluster = function createPoolCluster(opts) {
39
- const options = new PoolClusterOptions(opts);
40
- return new PoolClusterCallback(options);
54
+ const options = new ClusterOptions(opts);
55
+ return new ClusterCallback(options);
56
+ };
57
+
58
+ module.exports.importFile = function importFile(opts, callback) {
59
+ const cb = callback ? callback : () => {};
60
+ try {
61
+ const options = new ConnOptions(opts);
62
+ const conn = new Connection(options);
63
+ conn
64
+ .connect()
65
+ .then(() => {
66
+ return new Promise(conn.importFile.bind(conn, Object.assign({ skipDbCheck: true }, opts)));
67
+ })
68
+ .then(() => cb())
69
+ .catch((err) => cb(err))
70
+ .finally(() => {
71
+ new Promise(conn.end.bind(conn, {})).catch(console.log);
72
+ });
73
+ } catch (err) {
74
+ cb(err);
75
+ }
41
76
  };
@@ -0,0 +1,30 @@
1
+ // SPDX-License-Identifier: LGPL-2.1-or-later
2
+ // Copyright (c) 2015-2024 MariaDB Corporation Ab
3
+
4
+ 'use strict';
5
+
6
+ const hasMinVersion = function (nodeVersionStr, connectorRequirement) {
7
+ const versNode = nodeVersionStr.split('.');
8
+ const versReq = connectorRequirement.split('.');
9
+
10
+ const majorNode = Number(versNode[0]);
11
+ const majorReq = Number(versReq[0]);
12
+ if (majorNode > majorReq) return true;
13
+ if (majorNode < majorReq) return false;
14
+
15
+ if (versReq.length === 1) return true;
16
+
17
+ const minorNode = Number(versNode[1]);
18
+ const minorReq = Number(versReq[1]);
19
+ return minorNode >= minorReq;
20
+ };
21
+
22
+ module.exports.hasMinVersion = hasMinVersion;
23
+
24
+ const requirement = require('./package.json').engines.node;
25
+ const connectorRequirement = requirement.replace('>=', '').trim();
26
+ const currentNodeVersion = process.version.replace('v', '');
27
+ if (!hasMinVersion(currentNodeVersion, connectorRequirement)) {
28
+ console.error(`please upgrade node: mariadb requires at least version ${connectorRequirement}`);
29
+ process.exit(1);
30
+ }
@@ -0,0 +1,84 @@
1
+ // SPDX-License-Identifier: LGPL-2.1-or-later
2
+ // Copyright (c) 2015-2024 MariaDB Corporation Ab
3
+
4
+ 'use strict';
5
+
6
+ const Cluster = require('./cluster');
7
+
8
+ /**
9
+ * Create a new Cluster.
10
+ * Cluster handle pools with patterns and handle failover / distributed load
11
+ * according to selectors (round-robin / random / ordered )
12
+ *
13
+ * @param args cluster arguments. see pool-cluster-options.
14
+ * @constructor
15
+ */
16
+ class ClusterCallback {
17
+ #cluster;
18
+ constructor(args) {
19
+ this.#cluster = new Cluster(args);
20
+ this.#cluster._setCallback();
21
+ this.on = this.#cluster.on.bind(this.#cluster);
22
+ this.once = this.#cluster.once.bind(this.#cluster);
23
+ }
24
+
25
+ /**
26
+ * End cluster (and underlying pools).
27
+ *
28
+ * @param callback - not mandatory
29
+ */
30
+ end(callback) {
31
+ if (callback && typeof callback !== 'function') {
32
+ throw new Error('callback parameter must be a function');
33
+ }
34
+ const endingFct = callback ? callback : () => {};
35
+
36
+ this.#cluster
37
+ .end()
38
+ .then(() => {
39
+ endingFct();
40
+ })
41
+ .catch(endingFct);
42
+ }
43
+
44
+ /**
45
+ * Get connection from an available pools matching pattern, according to selector
46
+ *
47
+ * @param pattern pattern filter (not mandatory)
48
+ * @param selector node selector ('RR','RANDOM' or 'ORDER')
49
+ * @param callback callback function
50
+ */
51
+ getConnection(pattern, selector, callback) {
52
+ let pat = pattern,
53
+ sel = selector,
54
+ cal = callback;
55
+ if (typeof pattern === 'function') {
56
+ pat = null;
57
+ sel = null;
58
+ cal = pattern;
59
+ } else if (typeof selector === 'function') {
60
+ sel = null;
61
+ cal = selector;
62
+ }
63
+ const endingFct = cal ? cal : (err, conn) => {};
64
+ this.#cluster.getConnection(pat, sel, endingFct);
65
+ }
66
+
67
+ add(id, config) {
68
+ this.#cluster.add(id, config);
69
+ }
70
+
71
+ of(pattern, selector) {
72
+ return this.#cluster.of(pattern, selector);
73
+ }
74
+
75
+ remove(pattern) {
76
+ this.#cluster.remove(pattern);
77
+ }
78
+
79
+ get __tests() {
80
+ return this.#cluster.__tests;
81
+ }
82
+ }
83
+
84
+ module.exports = ClusterCallback;
@@ -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;