mysql2 3.11.5 → 3.12.0

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.
@@ -13,17 +13,30 @@ const EventEmitter = require('events').EventEmitter;
13
13
  const makeSelector = {
14
14
  RR() {
15
15
  let index = 0;
16
- return clusterIds => clusterIds[index++ % clusterIds.length];
16
+ return (clusterIds) => clusterIds[index++ % clusterIds.length];
17
17
  },
18
18
  RANDOM() {
19
- return clusterIds =>
19
+ return (clusterIds) =>
20
20
  clusterIds[Math.floor(Math.random() * clusterIds.length)];
21
21
  },
22
22
  ORDER() {
23
- return clusterIds => clusterIds[0];
23
+ return (clusterIds) => clusterIds[0];
24
24
  }
25
25
  };
26
26
 
27
+ const getMonotonicMilliseconds = function () {
28
+ let ms;
29
+
30
+ if (typeof process.hrtime === 'function') {
31
+ ms = process.hrtime();
32
+ ms = ms[0] * 1e3 + ms[1] * 1e-6;
33
+ } else {
34
+ ms = process.uptime() * 1000;
35
+ }
36
+
37
+ return Math.floor(ms);
38
+ };
39
+
27
40
  class PoolNamespace {
28
41
  constructor(cluster, pattern, selector) {
29
42
  this._cluster = cluster;
@@ -34,15 +47,28 @@ class PoolNamespace {
34
47
  getConnection(cb) {
35
48
  const clusterNode = this._getClusterNode();
36
49
  if (clusterNode === null) {
37
- return cb(new Error('Pool does Not exists.'));
50
+ let err = new Error('Pool does Not exist.');
51
+ err.code = 'POOL_NOEXIST';
52
+
53
+ if (this._cluster._findNodeIds(this._pattern, true).length !== 0) {
54
+ err = new Error('Pool does Not have online node.');
55
+ err.code = 'POOL_NONEONLINE';
56
+ }
57
+
58
+ return cb(err);
38
59
  }
39
60
  return this._cluster._getConnection(clusterNode, (err, connection) => {
40
61
  if (err) {
62
+ if (
63
+ this._cluster._canRetry &&
64
+ this._cluster._findNodeIds(this._pattern).length !== 0
65
+ ) {
66
+ this._cluster.emit('warn', err);
67
+ return this.getConnection(cb);
68
+ }
69
+
41
70
  return cb(err);
42
71
  }
43
- if (connection === 'retry') {
44
- return this.getConnection(cb);
45
- }
46
72
  return cb(null, connection);
47
73
  });
48
74
  }
@@ -79,9 +105,9 @@ class PoolNamespace {
79
105
 
80
106
  /**
81
107
  * pool cluster execute
82
- * @param {*} sql
83
- * @param {*} values
84
- * @param {*} cb
108
+ * @param {*} sql
109
+ * @param {*} values
110
+ * @param {*} cb
85
111
  */
86
112
  execute(sql, values, cb) {
87
113
  if (typeof values === 'function') {
@@ -123,6 +149,7 @@ class PoolCluster extends EventEmitter {
123
149
  this._canRetry =
124
150
  typeof config.canRetry === 'undefined' ? true : config.canRetry;
125
151
  this._removeNodeErrorCount = config.removeNodeErrorCount || 5;
152
+ this._restoreNodeTimeout = config.restoreNodeTimeout || 0;
126
153
  this._defaultSelector = config.defaultSelector || 'RR';
127
154
  this._closed = false;
128
155
  this._lastId = 0;
@@ -155,13 +182,26 @@ class PoolCluster extends EventEmitter {
155
182
  this._nodes[id] = {
156
183
  id: id,
157
184
  errorCount: 0,
158
- pool: new Pool({ config: new PoolConfig(config) })
185
+ pool: new Pool({ config: new PoolConfig(config) }),
186
+ _offlineUntil: 0
159
187
  };
160
188
  this._serviceableNodeIds.push(id);
161
189
  this._clearFindCaches();
162
190
  }
163
191
  }
164
192
 
193
+ remove(pattern) {
194
+ const foundNodeIds = this._findNodeIds(pattern, true);
195
+
196
+ for (let i = 0; i < foundNodeIds.length; i++) {
197
+ const node = this._getNode(foundNodeIds[i]);
198
+
199
+ if (node) {
200
+ this._removeNode(node);
201
+ }
202
+ }
203
+ }
204
+
165
205
  getConnection(pattern, selector, cb) {
166
206
  let namespace;
167
207
  if (typeof pattern === 'function') {
@@ -181,7 +221,7 @@ class PoolCluster extends EventEmitter {
181
221
  const cb =
182
222
  callback !== undefined
183
223
  ? callback
184
- : err => {
224
+ : (err) => {
185
225
  if (err) {
186
226
  throw err;
187
227
  }
@@ -190,11 +230,12 @@ class PoolCluster extends EventEmitter {
190
230
  process.nextTick(cb);
191
231
  return;
192
232
  }
233
+
193
234
  this._closed = true;
194
235
 
195
236
  let calledBack = false;
196
237
  let waitingClose = 0;
197
- const onEnd = err => {
238
+ const onEnd = (err) => {
198
239
  if (!calledBack && (err || --waitingClose <= 0)) {
199
240
  calledBack = true;
200
241
  return cb(err);
@@ -205,31 +246,51 @@ class PoolCluster extends EventEmitter {
205
246
  waitingClose++;
206
247
  this._nodes[id].pool.end(onEnd);
207
248
  }
249
+
208
250
  if (waitingClose === 0) {
209
251
  process.nextTick(onEnd);
210
252
  }
211
253
  }
212
254
 
213
- _findNodeIds(pattern) {
214
- if (typeof this._findCaches[pattern] !== 'undefined') {
215
- return this._findCaches[pattern];
216
- }
217
- let foundNodeIds;
218
- if (pattern === '*') {
219
- // all
220
- foundNodeIds = this._serviceableNodeIds;
221
- } else if (this._serviceableNodeIds.indexOf(pattern) !== -1) {
222
- // one
223
- foundNodeIds = [pattern];
224
- } else {
225
- // wild matching
226
- const keyword = pattern.substring(pattern.length - 1, 0);
227
- foundNodeIds = this._serviceableNodeIds.filter(id =>
228
- id.startsWith(keyword)
229
- );
255
+ _findNodeIds(pattern, includeOffline) {
256
+ let currentTime = 0;
257
+ let foundNodeIds = this._findCaches[pattern];
258
+
259
+ if (typeof this._findCaches[pattern] === 'undefined') {
260
+ if (pattern === '*') {
261
+ // all
262
+ foundNodeIds = this._serviceableNodeIds;
263
+ } else if (this._serviceableNodeIds.indexOf(pattern) !== -1) {
264
+ // one
265
+ foundNodeIds = [pattern];
266
+ } else {
267
+ // wild matching
268
+ const keyword = pattern.substring(pattern.length - 1, 0);
269
+ foundNodeIds = this._serviceableNodeIds.filter((id) =>
270
+ id.startsWith(keyword)
271
+ );
272
+ }
230
273
  }
274
+
231
275
  this._findCaches[pattern] = foundNodeIds;
232
- return foundNodeIds;
276
+
277
+ if (includeOffline) {
278
+ return foundNodeIds;
279
+ }
280
+
281
+ return foundNodeIds.filter((nodeId) => {
282
+ const node = this._getNode(nodeId);
283
+
284
+ if (!node._offlineUntil) {
285
+ return true;
286
+ }
287
+
288
+ if (!currentTime) {
289
+ currentTime = getMonotonicMilliseconds();
290
+ }
291
+
292
+ return node._offlineUntil <= currentTime;
293
+ });
233
294
  }
234
295
 
235
296
  _getNode(id) {
@@ -237,21 +298,39 @@ class PoolCluster extends EventEmitter {
237
298
  }
238
299
 
239
300
  _increaseErrorCount(node) {
240
- if (++node.errorCount >= this._removeNodeErrorCount) {
241
- const index = this._serviceableNodeIds.indexOf(node.id);
242
- if (index !== -1) {
243
- this._serviceableNodeIds.splice(index, 1);
244
- delete this._nodes[node.id];
245
- this._clearFindCaches();
246
- node.pool.end();
247
- this.emit('remove', node.id);
248
- }
301
+ const errorCount = ++node.errorCount;
302
+
303
+ if (this._removeNodeErrorCount > errorCount) {
304
+ return;
249
305
  }
306
+
307
+ if (this._restoreNodeTimeout > 0) {
308
+ node._offlineUntil =
309
+ getMonotonicMilliseconds() + this._restoreNodeTimeout;
310
+ this.emit('offline', node.id);
311
+ return;
312
+ }
313
+
314
+ this._removeNode(node);
315
+ this.emit('remove', node.id);
250
316
  }
251
317
 
252
318
  _decreaseErrorCount(node) {
253
- if (node.errorCount > 0) {
254
- --node.errorCount;
319
+ let errorCount = node.errorCount;
320
+
321
+ if (errorCount > this._removeNodeErrorCount) {
322
+ errorCount = this._removeNodeErrorCount;
323
+ }
324
+
325
+ if (errorCount < 1) {
326
+ errorCount = 1;
327
+ }
328
+
329
+ node.errorCount = errorCount - 1;
330
+
331
+ if (node._offlineUntil) {
332
+ node._offlineUntil = 0;
333
+ this.emit('online', node.id);
255
334
  }
256
335
  }
257
336
 
@@ -259,13 +338,6 @@ class PoolCluster extends EventEmitter {
259
338
  node.pool.getConnection((err, connection) => {
260
339
  if (err) {
261
340
  this._increaseErrorCount(node);
262
- if (this._canRetry) {
263
- // REVIEW: this seems wrong?
264
- this.emit('warn', err);
265
- // eslint-disable-next-line no-console
266
- console.warn(`[Error] PoolCluster : ${err}`);
267
- return cb(null, 'retry');
268
- }
269
341
  return cb(err);
270
342
  }
271
343
  this._decreaseErrorCount(node);
@@ -275,6 +347,16 @@ class PoolCluster extends EventEmitter {
275
347
  });
276
348
  }
277
349
 
350
+ _removeNode(node) {
351
+ const index = this._serviceableNodeIds.indexOf(node.id);
352
+ if (index !== -1) {
353
+ this._serviceableNodeIds.splice(index, 1);
354
+ delete this._nodes[node.id];
355
+ this._clearFindCaches();
356
+ node.pool.end();
357
+ }
358
+ }
359
+
278
360
  _clearFindCaches() {
279
361
  this._findCaches = {};
280
362
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mysql2",
3
- "version": "3.11.5",
3
+ "version": "3.12.0",
4
4
  "description": "fast mysql driver. Implements core protocol, prepared statements, ssl and compression in native JS",
5
5
  "main": "index.js",
6
6
  "typings": "typings/mysql/index",
package/promise.js CHANGED
@@ -60,7 +60,7 @@ class PromisePoolCluster extends EventEmitter {
60
60
  super();
61
61
  this.poolCluster = poolCluster;
62
62
  this.Promise = thePromise || Promise;
63
- inheritEvents(poolCluster, this, ['warn', 'remove']);
63
+ inheritEvents(poolCluster, this, ['warn', 'remove' , 'online', 'offline']);
64
64
  }
65
65
 
66
66
  getConnection(pattern, selector) {
@@ -156,7 +156,7 @@ class PromisePoolCluster extends EventEmitter {
156
156
  })(func);
157
157
  }
158
158
  }
159
- })(['add']);
159
+ })(['add', 'remove']);
160
160
 
161
161
  function createPromisePoolCluster(opts) {
162
162
  const corePoolCluster = createPoolCluster(opts);
@@ -52,6 +52,8 @@ declare class PoolCluster extends EventEmitter {
52
52
  add(group: string, connectionUri: string): void;
53
53
  add(group: string, config: PoolOptions): void;
54
54
 
55
+ remove(pattern: string): void;
56
+
55
57
  end(): void;
56
58
 
57
59
  getConnection(
@@ -79,6 +81,8 @@ declare class PoolCluster extends EventEmitter {
79
81
  of(pattern: string, selector?: string): PoolNamespace;
80
82
 
81
83
  on(event: string, listener: (...args: any[]) => void): this;
84
+ on(event: 'online', listener: (nodeId: number) => void): this;
85
+ on(event: 'offline', listener: (nodeId: number) => void): this;
82
86
  on(event: 'remove', listener: (nodeId: number) => void): this;
83
87
  on(event: 'warn', listener: (err: Error) => void): this;
84
88
  }