mongodb 3.5.7 → 3.5.11
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/HISTORY.md +44 -0
- package/lib/bulk/common.js +4 -11
- package/lib/bulk/unordered.js +8 -0
- package/lib/change_stream.js +194 -145
- package/lib/cmap/connection.js +8 -4
- package/lib/cmap/connection_pool.js +11 -3
- package/lib/collection.js +2 -3
- package/lib/core/connection/apm.js +1 -1
- package/lib/core/connection/pool.js +2 -1
- package/lib/core/cursor.js +46 -59
- package/lib/core/error.js +11 -4
- package/lib/core/index.js +0 -1
- package/lib/core/sdam/monitor.js +53 -61
- package/lib/core/sdam/server.js +5 -2
- package/lib/core/sdam/server_description.js +12 -1
- package/lib/core/sdam/server_selection.js +25 -37
- package/lib/core/sdam/topology.js +4 -26
- package/lib/core/sessions.js +12 -6
- package/lib/core/topologies/read_preference.js +58 -6
- package/lib/core/topologies/replset.js +4 -4
- package/lib/core/utils.js +13 -23
- package/lib/core/wireprotocol/command.js +1 -6
- package/lib/core/wireprotocol/get_more.js +6 -1
- package/lib/cursor.js +10 -32
- package/lib/db.js +3 -3
- package/lib/error.js +26 -33
- package/lib/mongo_client.js +8 -0
- package/lib/operations/collection_ops.js +1 -2
- package/lib/operations/command.js +2 -3
- package/lib/operations/command_v2.js +7 -6
- package/lib/operations/connect.js +11 -0
- package/lib/operations/cursor_ops.js +2 -3
- package/lib/operations/db_ops.js +1 -2
- package/lib/operations/find.js +2 -2
- package/lib/operations/geo_haystack_search.js +2 -2
- package/lib/operations/map_reduce.js +2 -2
- package/lib/operations/operation.js +2 -1
- package/lib/operations/run_command.js +19 -0
- package/lib/topologies/native_topology.js +12 -2
- package/lib/topologies/topology_base.js +4 -4
- package/lib/utils.js +96 -60
- package/package.json +7 -4
|
@@ -198,6 +198,10 @@ class ConnectionPool extends EventEmitter {
|
|
|
198
198
|
return this[kConnections].length;
|
|
199
199
|
}
|
|
200
200
|
|
|
201
|
+
get waitQueueSize() {
|
|
202
|
+
return this[kWaitQueue].length;
|
|
203
|
+
}
|
|
204
|
+
|
|
201
205
|
/**
|
|
202
206
|
* Check a connection out of this pool. The connection will continue to be tracked, but no reference to it
|
|
203
207
|
* will be held by the pool. This means that if a connection is checked out it MUST be checked back in or
|
|
@@ -295,7 +299,7 @@ class ConnectionPool extends EventEmitter {
|
|
|
295
299
|
this[kCancellationToken].emit('cancel');
|
|
296
300
|
|
|
297
301
|
// drain the wait queue
|
|
298
|
-
while (this
|
|
302
|
+
while (this.waitQueueSize) {
|
|
299
303
|
const waitQueueMember = this[kWaitQueue].pop();
|
|
300
304
|
clearTimeout(waitQueueMember.timer);
|
|
301
305
|
if (!waitQueueMember[kCancelled]) {
|
|
@@ -449,13 +453,17 @@ function processWaitQueue(pool) {
|
|
|
449
453
|
return;
|
|
450
454
|
}
|
|
451
455
|
|
|
452
|
-
while (pool
|
|
456
|
+
while (pool.waitQueueSize) {
|
|
453
457
|
const waitQueueMember = pool[kWaitQueue].peekFront();
|
|
454
458
|
if (waitQueueMember[kCancelled]) {
|
|
455
459
|
pool[kWaitQueue].shift();
|
|
456
460
|
continue;
|
|
457
461
|
}
|
|
458
462
|
|
|
463
|
+
if (!pool.availableConnectionCount) {
|
|
464
|
+
break;
|
|
465
|
+
}
|
|
466
|
+
|
|
459
467
|
const connection = pool[kConnections].shift();
|
|
460
468
|
const isStale = connectionIsStale(pool, connection);
|
|
461
469
|
const isIdle = connectionIsIdle(pool, connection);
|
|
@@ -472,7 +480,7 @@ function processWaitQueue(pool) {
|
|
|
472
480
|
}
|
|
473
481
|
|
|
474
482
|
const maxPoolSize = pool.options.maxPoolSize;
|
|
475
|
-
if (pool
|
|
483
|
+
if (pool.waitQueueSize && (maxPoolSize <= 0 || pool.totalConnectionCount < maxPoolSize)) {
|
|
476
484
|
createConnection(pool, (err, connection) => {
|
|
477
485
|
const waitQueueMember = pool[kWaitQueue].shift();
|
|
478
486
|
if (waitQueueMember == null) {
|
package/lib/collection.js
CHANGED
|
@@ -16,7 +16,6 @@ const unordered = require('./bulk/unordered');
|
|
|
16
16
|
const ordered = require('./bulk/ordered');
|
|
17
17
|
const ChangeStream = require('./change_stream');
|
|
18
18
|
const executeLegacyOperation = require('./utils').executeLegacyOperation;
|
|
19
|
-
const resolveReadPreference = require('./utils').resolveReadPreference;
|
|
20
19
|
const WriteConcern = require('./write_concern');
|
|
21
20
|
const ReadConcern = require('./read_concern');
|
|
22
21
|
const MongoDBNamespace = require('./utils').MongoDBNamespace;
|
|
@@ -399,7 +398,7 @@ Collection.prototype.find = deprecateOptions(
|
|
|
399
398
|
newOptions.slaveOk = options.slaveOk != null ? options.slaveOk : this.s.db.slaveOk;
|
|
400
399
|
|
|
401
400
|
// Add read preference if needed
|
|
402
|
-
newOptions.readPreference =
|
|
401
|
+
newOptions.readPreference = ReadPreference.resolve(this, newOptions);
|
|
403
402
|
|
|
404
403
|
// Set slave ok to true if read preference different from primary
|
|
405
404
|
if (
|
|
@@ -1978,7 +1977,7 @@ Collection.prototype.parallelCollectionScan = deprecate(function(options, callba
|
|
|
1978
1977
|
|
|
1979
1978
|
options = Object.assign({}, options);
|
|
1980
1979
|
// Ensure we have the right read preference inheritance
|
|
1981
|
-
options.readPreference =
|
|
1980
|
+
options.readPreference = ReadPreference.resolve(this, options);
|
|
1982
1981
|
|
|
1983
1982
|
// Add a promiseLibrary
|
|
1984
1983
|
options.promiseLibrary = this.s.promiseLibrary;
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
const Msg = require('../connection/msg').Msg;
|
|
3
3
|
const KillCursor = require('../connection/commands').KillCursor;
|
|
4
4
|
const GetMore = require('../connection/commands').GetMore;
|
|
5
|
-
const calculateDurationInMs = require('
|
|
5
|
+
const calculateDurationInMs = require('../../utils').calculateDurationInMs;
|
|
6
6
|
|
|
7
7
|
/** Commands that we want to redact because of the sensitive nature of their contents */
|
|
8
8
|
const SENSITIVE_COMMANDS = new Set([
|
|
@@ -21,6 +21,7 @@ const connect = require('./connect');
|
|
|
21
21
|
const updateSessionFromResponse = require('../sessions').updateSessionFromResponse;
|
|
22
22
|
const eachAsync = require('../utils').eachAsync;
|
|
23
23
|
const makeStateMachine = require('../utils').makeStateMachine;
|
|
24
|
+
const now = require('../../utils').now;
|
|
24
25
|
|
|
25
26
|
const DISCONNECTED = 'disconnected';
|
|
26
27
|
const CONNECTING = 'connecting';
|
|
@@ -898,7 +899,7 @@ Pool.prototype.write = function(command, options, cb) {
|
|
|
898
899
|
if (self.options.monitorCommands) {
|
|
899
900
|
this.emit('commandStarted', new apm.CommandStartedEvent(this, command));
|
|
900
901
|
|
|
901
|
-
operation.started =
|
|
902
|
+
operation.started = now();
|
|
902
903
|
operation.cb = (err, reply) => {
|
|
903
904
|
if (err) {
|
|
904
905
|
self.emit(
|
package/lib/core/cursor.js
CHANGED
|
@@ -4,7 +4,6 @@ const Logger = require('./connection/logger');
|
|
|
4
4
|
const retrieveBSON = require('./connection/utils').retrieveBSON;
|
|
5
5
|
const MongoError = require('./error').MongoError;
|
|
6
6
|
const MongoNetworkError = require('./error').MongoNetworkError;
|
|
7
|
-
const mongoErrorContextSymbol = require('./error').mongoErrorContextSymbol;
|
|
8
7
|
const collationNotSupported = require('./utils').collationNotSupported;
|
|
9
8
|
const ReadPreference = require('./topologies/read_preference');
|
|
10
9
|
const isUnifiedTopology = require('./utils').isUnifiedTopology;
|
|
@@ -412,7 +411,15 @@ class CoreCursor extends Readable {
|
|
|
412
411
|
batchSize = this.cursorState.limit - this.cursorState.currentLimit;
|
|
413
412
|
}
|
|
414
413
|
|
|
415
|
-
|
|
414
|
+
const cursorState = this.cursorState;
|
|
415
|
+
this.server.getMore(this.ns, cursorState, batchSize, this.options, (err, result, conn) => {
|
|
416
|
+
// NOTE: `getMore` modifies `cursorState`, would be very ideal not to do so in the future
|
|
417
|
+
if (err || (cursorState.cursorId && cursorState.cursorId.isZero())) {
|
|
418
|
+
this._endSession();
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
callback(err, result, conn);
|
|
422
|
+
});
|
|
416
423
|
}
|
|
417
424
|
|
|
418
425
|
_initializeCursor(callback) {
|
|
@@ -433,18 +440,15 @@ class CoreCursor extends Readable {
|
|
|
433
440
|
}
|
|
434
441
|
|
|
435
442
|
function done(err, result) {
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
cursor.cursorState.cursorId.isZero() &&
|
|
439
|
-
cursor._endSession
|
|
440
|
-
) {
|
|
443
|
+
const cursorState = cursor.cursorState;
|
|
444
|
+
if (err || (cursorState.cursorId && cursorState.cursorId.isZero())) {
|
|
441
445
|
cursor._endSession();
|
|
442
446
|
}
|
|
443
447
|
|
|
444
448
|
if (
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
449
|
+
cursorState.documents.length === 0 &&
|
|
450
|
+
cursorState.cursorId &&
|
|
451
|
+
cursorState.cursorId.isZero() &&
|
|
448
452
|
!cursor.cmd.tailable &&
|
|
449
453
|
!cursor.cmd.awaitData
|
|
450
454
|
) {
|
|
@@ -460,50 +464,41 @@ class CoreCursor extends Readable {
|
|
|
460
464
|
}
|
|
461
465
|
|
|
462
466
|
const result = r.message;
|
|
463
|
-
if (result.queryFailure) {
|
|
464
|
-
return done(new MongoError(result.documents[0]), null);
|
|
465
|
-
}
|
|
466
467
|
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
result.
|
|
471
|
-
|
|
472
|
-
(typeof result.documents[0].cursor !== 'string' ||
|
|
473
|
-
result.documents[0]['$err'] ||
|
|
474
|
-
result.documents[0]['errmsg'] ||
|
|
475
|
-
Array.isArray(result.documents[0].result))
|
|
476
|
-
) {
|
|
477
|
-
// We have an error document, return the error
|
|
478
|
-
if (result.documents[0]['$err'] || result.documents[0]['errmsg']) {
|
|
479
|
-
return done(new MongoError(result.documents[0]), null);
|
|
468
|
+
if (Array.isArray(result.documents) && result.documents.length === 1) {
|
|
469
|
+
const document = result.documents[0];
|
|
470
|
+
|
|
471
|
+
if (result.queryFailure) {
|
|
472
|
+
return done(new MongoError(document), null);
|
|
480
473
|
}
|
|
481
474
|
|
|
482
|
-
//
|
|
483
|
-
if (
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
cursor.ns = result.documents[0].cursor.ns;
|
|
475
|
+
// Check if we have a command cursor
|
|
476
|
+
if (!cursor.cmd.find || (cursor.cmd.find && cursor.cmd.virtual === false)) {
|
|
477
|
+
// We have an error document, return the error
|
|
478
|
+
if (document.$err || document.errmsg) {
|
|
479
|
+
return done(new MongoError(document), null);
|
|
488
480
|
}
|
|
489
|
-
// Promote id to long if needed
|
|
490
|
-
cursor.cursorState.cursorId = typeof id === 'number' ? Long.fromNumber(id) : id;
|
|
491
|
-
cursor.cursorState.lastCursorId = cursor.cursorState.cursorId;
|
|
492
|
-
cursor.cursorState.operationTime = result.documents[0].operationTime;
|
|
493
|
-
|
|
494
|
-
// If we have a firstBatch set it
|
|
495
|
-
if (Array.isArray(result.documents[0].cursor.firstBatch)) {
|
|
496
|
-
cursor.cursorState.documents = result.documents[0].cursor.firstBatch; //.reverse();
|
|
497
|
-
}
|
|
498
|
-
|
|
499
|
-
// Return after processing command cursor
|
|
500
|
-
return done(null, result);
|
|
501
|
-
}
|
|
502
481
|
|
|
503
|
-
|
|
504
|
-
cursor.
|
|
505
|
-
|
|
506
|
-
|
|
482
|
+
// We have a cursor document
|
|
483
|
+
if (document.cursor != null && typeof document.cursor !== 'string') {
|
|
484
|
+
const id = document.cursor.id;
|
|
485
|
+
// If we have a namespace change set the new namespace for getmores
|
|
486
|
+
if (document.cursor.ns) {
|
|
487
|
+
cursor.ns = document.cursor.ns;
|
|
488
|
+
}
|
|
489
|
+
// Promote id to long if needed
|
|
490
|
+
cursor.cursorState.cursorId = typeof id === 'number' ? Long.fromNumber(id) : id;
|
|
491
|
+
cursor.cursorState.lastCursorId = cursor.cursorState.cursorId;
|
|
492
|
+
cursor.cursorState.operationTime = document.operationTime;
|
|
493
|
+
|
|
494
|
+
// If we have a firstBatch set it
|
|
495
|
+
if (Array.isArray(document.cursor.firstBatch)) {
|
|
496
|
+
cursor.cursorState.documents = document.cursor.firstBatch; //.reverse();
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
// Return after processing command cursor
|
|
500
|
+
return done(null, result);
|
|
501
|
+
}
|
|
507
502
|
}
|
|
508
503
|
}
|
|
509
504
|
|
|
@@ -690,8 +685,8 @@ function _setCursorNotifiedImpl(self, callback) {
|
|
|
690
685
|
self.cursorState.documents = [];
|
|
691
686
|
self.cursorState.cursorIndex = 0;
|
|
692
687
|
|
|
693
|
-
if (self.
|
|
694
|
-
self._endSession(
|
|
688
|
+
if (self.cursorState.session) {
|
|
689
|
+
self._endSession(callback);
|
|
695
690
|
return;
|
|
696
691
|
}
|
|
697
692
|
|
|
@@ -774,17 +769,9 @@ function nextFunction(self, callback) {
|
|
|
774
769
|
// Execute the next get more
|
|
775
770
|
self._getMore(function(err, doc, connection) {
|
|
776
771
|
if (err) {
|
|
777
|
-
if (err instanceof MongoError) {
|
|
778
|
-
err[mongoErrorContextSymbol].isGetMore = true;
|
|
779
|
-
}
|
|
780
|
-
|
|
781
772
|
return handleCallback(callback, err);
|
|
782
773
|
}
|
|
783
774
|
|
|
784
|
-
if (self.cursorState.cursorId && self.cursorState.cursorId.isZero() && self._endSession) {
|
|
785
|
-
self._endSession();
|
|
786
|
-
}
|
|
787
|
-
|
|
788
775
|
// Save the returned connection to ensure all getMore's fire over the same connection
|
|
789
776
|
self.connection = connection;
|
|
790
777
|
|
package/lib/core/error.js
CHANGED
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const mongoErrorContextSymbol = Symbol('mongoErrorContextSymbol');
|
|
4
|
-
|
|
5
3
|
/**
|
|
6
4
|
* Creates a new MongoError
|
|
7
5
|
*
|
|
@@ -21,6 +19,10 @@ class MongoError extends Error {
|
|
|
21
19
|
} else {
|
|
22
20
|
super(message.message || message.errmsg || message.$err || 'n/a');
|
|
23
21
|
for (var name in message) {
|
|
22
|
+
if (name === 'errmsg') {
|
|
23
|
+
continue;
|
|
24
|
+
}
|
|
25
|
+
|
|
24
26
|
this[name] = message[name];
|
|
25
27
|
}
|
|
26
28
|
}
|
|
@@ -29,7 +31,13 @@ class MongoError extends Error {
|
|
|
29
31
|
}
|
|
30
32
|
|
|
31
33
|
this.name = 'MongoError';
|
|
32
|
-
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Legacy name for server error responses
|
|
38
|
+
*/
|
|
39
|
+
get errmsg() {
|
|
40
|
+
return this.message;
|
|
33
41
|
}
|
|
34
42
|
|
|
35
43
|
/**
|
|
@@ -262,7 +270,6 @@ module.exports = {
|
|
|
262
270
|
MongoTimeoutError,
|
|
263
271
|
MongoServerSelectionError,
|
|
264
272
|
MongoWriteConcernError,
|
|
265
|
-
mongoErrorContextSymbol,
|
|
266
273
|
isRetryableError,
|
|
267
274
|
isSDAMUnrecoverableError,
|
|
268
275
|
isNodeShuttingDownError,
|
package/lib/core/index.js
CHANGED
|
@@ -22,7 +22,6 @@ module.exports = {
|
|
|
22
22
|
MongoTimeoutError: require('./error').MongoTimeoutError,
|
|
23
23
|
MongoServerSelectionError: require('./error').MongoServerSelectionError,
|
|
24
24
|
MongoWriteConcernError: require('./error').MongoWriteConcernError,
|
|
25
|
-
mongoErrorContextSymbol: require('./error').mongoErrorContextSymbol,
|
|
26
25
|
// Core
|
|
27
26
|
Connection: require('./connection/connection'),
|
|
28
27
|
Server: require('./topologies/server'),
|
package/lib/core/sdam/monitor.js
CHANGED
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
const ServerType = require('./common').ServerType;
|
|
4
|
-
const calculateDurationInMs = require('../utils').calculateDurationInMs;
|
|
5
4
|
const EventEmitter = require('events');
|
|
6
5
|
const connect = require('../connection/connect');
|
|
7
6
|
const Connection = require('../../cmap/connection').Connection;
|
|
8
7
|
const common = require('./common');
|
|
9
8
|
const makeStateMachine = require('../utils').makeStateMachine;
|
|
10
9
|
const MongoError = require('../error').MongoError;
|
|
10
|
+
const makeInterruptableAsyncInterval = require('../../utils').makeInterruptableAsyncInterval;
|
|
11
|
+
const calculateDurationInMs = require('../../utils').calculateDurationInMs;
|
|
12
|
+
const now = require('../../utils').now;
|
|
11
13
|
|
|
12
14
|
const sdamEvents = require('./events');
|
|
13
15
|
const ServerHeartbeatStartedEvent = sdamEvents.ServerHeartbeatStartedEvent;
|
|
@@ -18,7 +20,6 @@ const kServer = Symbol('server');
|
|
|
18
20
|
const kMonitorId = Symbol('monitorId');
|
|
19
21
|
const kConnection = Symbol('connection');
|
|
20
22
|
const kCancellationToken = Symbol('cancellationToken');
|
|
21
|
-
const kLastCheckTime = Symbol('lastCheckTime');
|
|
22
23
|
|
|
23
24
|
const STATE_CLOSED = common.STATE_CLOSED;
|
|
24
25
|
const STATE_CLOSING = common.STATE_CLOSING;
|
|
@@ -33,6 +34,10 @@ const stateTransition = makeStateMachine({
|
|
|
33
34
|
|
|
34
35
|
const INVALID_REQUEST_CHECK_STATES = new Set([STATE_CLOSING, STATE_CLOSED, STATE_MONITORING]);
|
|
35
36
|
|
|
37
|
+
function isInCloseState(monitor) {
|
|
38
|
+
return monitor.s.state === STATE_CLOSED || monitor.s.state === STATE_CLOSING;
|
|
39
|
+
}
|
|
40
|
+
|
|
36
41
|
class Monitor extends EventEmitter {
|
|
37
42
|
constructor(server, options) {
|
|
38
43
|
super(options);
|
|
@@ -41,6 +46,7 @@ class Monitor extends EventEmitter {
|
|
|
41
46
|
this[kConnection] = undefined;
|
|
42
47
|
this[kCancellationToken] = new EventEmitter();
|
|
43
48
|
this[kCancellationToken].setMaxListeners(Infinity);
|
|
49
|
+
this[kMonitorId] = null;
|
|
44
50
|
this.s = {
|
|
45
51
|
state: STATE_CLOSED
|
|
46
52
|
};
|
|
@@ -60,13 +66,12 @@ class Monitor extends EventEmitter {
|
|
|
60
66
|
});
|
|
61
67
|
|
|
62
68
|
// TODO: refactor this to pull it directly from the pool, requires new ConnectionPool integration
|
|
63
|
-
const addressParts = server.description.address.split(':');
|
|
64
69
|
this.connectOptions = Object.freeze(
|
|
65
70
|
Object.assign(
|
|
66
71
|
{
|
|
67
72
|
id: '<monitor>',
|
|
68
|
-
host:
|
|
69
|
-
port:
|
|
73
|
+
host: server.description.host,
|
|
74
|
+
port: server.description.port,
|
|
70
75
|
bson: server.s.bson,
|
|
71
76
|
connectionType: Connection
|
|
72
77
|
},
|
|
@@ -89,7 +94,14 @@ class Monitor extends EventEmitter {
|
|
|
89
94
|
return;
|
|
90
95
|
}
|
|
91
96
|
|
|
92
|
-
|
|
97
|
+
// start
|
|
98
|
+
const heartbeatFrequencyMS = this.options.heartbeatFrequencyMS;
|
|
99
|
+
const minHeartbeatFrequencyMS = this.options.minHeartbeatFrequencyMS;
|
|
100
|
+
this[kMonitorId] = makeInterruptableAsyncInterval(monitorServer(this), {
|
|
101
|
+
interval: heartbeatFrequencyMS,
|
|
102
|
+
minInterval: minHeartbeatFrequencyMS,
|
|
103
|
+
immediate: true
|
|
104
|
+
});
|
|
93
105
|
}
|
|
94
106
|
|
|
95
107
|
requestCheck() {
|
|
@@ -97,31 +109,19 @@ class Monitor extends EventEmitter {
|
|
|
97
109
|
return;
|
|
98
110
|
}
|
|
99
111
|
|
|
100
|
-
|
|
101
|
-
const minHeartbeatFrequencyMS = this.options.minHeartbeatFrequencyMS;
|
|
102
|
-
const remainingTime = heartbeatFrequencyMS - calculateDurationInMs(this[kLastCheckTime]);
|
|
103
|
-
if (remainingTime > minHeartbeatFrequencyMS && this[kMonitorId]) {
|
|
104
|
-
clearTimeout(this[kMonitorId]);
|
|
105
|
-
rescheduleMonitoring(this, minHeartbeatFrequencyMS);
|
|
106
|
-
return;
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
if (this[kMonitorId]) {
|
|
110
|
-
clearTimeout(this[kMonitorId]);
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
monitorServer(this);
|
|
112
|
+
this[kMonitorId].wake();
|
|
114
113
|
}
|
|
115
114
|
|
|
116
115
|
close() {
|
|
117
|
-
if (this
|
|
116
|
+
if (isInCloseState(this)) {
|
|
118
117
|
return;
|
|
119
118
|
}
|
|
120
119
|
|
|
121
120
|
stateTransition(this, STATE_CLOSING);
|
|
122
121
|
this[kCancellationToken].emit('cancel');
|
|
123
122
|
if (this[kMonitorId]) {
|
|
124
|
-
|
|
123
|
+
this[kMonitorId].stop();
|
|
124
|
+
this[kMonitorId] = null;
|
|
125
125
|
}
|
|
126
126
|
|
|
127
127
|
if (this[kConnection]) {
|
|
@@ -138,7 +138,7 @@ function checkServer(monitor, callback) {
|
|
|
138
138
|
monitor[kConnection] = undefined;
|
|
139
139
|
}
|
|
140
140
|
|
|
141
|
-
const start =
|
|
141
|
+
const start = now();
|
|
142
142
|
monitor.emit('serverHeartbeatStarted', new ServerHeartbeatStartedEvent(monitor.address));
|
|
143
143
|
|
|
144
144
|
function failureHandler(err) {
|
|
@@ -186,7 +186,7 @@ function checkServer(monitor, callback) {
|
|
|
186
186
|
return;
|
|
187
187
|
}
|
|
188
188
|
|
|
189
|
-
if (monitor
|
|
189
|
+
if (isInCloseState(monitor)) {
|
|
190
190
|
conn.destroy({ force: true });
|
|
191
191
|
failureHandler(new MongoError('monitor was destroyed'));
|
|
192
192
|
return;
|
|
@@ -198,52 +198,44 @@ function checkServer(monitor, callback) {
|
|
|
198
198
|
}
|
|
199
199
|
|
|
200
200
|
function monitorServer(monitor) {
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
201
|
+
return callback => {
|
|
202
|
+
stateTransition(monitor, STATE_MONITORING);
|
|
203
|
+
function done() {
|
|
204
|
+
if (!isInCloseState(monitor)) {
|
|
205
|
+
stateTransition(monitor, STATE_IDLE);
|
|
206
|
+
}
|
|
205
207
|
|
|
206
|
-
|
|
207
|
-
if (e0 == null) {
|
|
208
|
-
rescheduleMonitoring(monitor);
|
|
209
|
-
return;
|
|
208
|
+
callback();
|
|
210
209
|
}
|
|
211
210
|
|
|
212
|
-
//
|
|
213
|
-
|
|
214
|
-
monitor.emit('resetServer', e0);
|
|
215
|
-
rescheduleMonitoring(monitor);
|
|
216
|
-
return;
|
|
217
|
-
}
|
|
211
|
+
// TODO: the next line is a legacy event, remove in v4
|
|
212
|
+
process.nextTick(() => monitor.emit('monitoring', monitor[kServer]));
|
|
218
213
|
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
monitor.emit('resetConnectionPool');
|
|
223
|
-
|
|
224
|
-
checkServer(monitor, e1 => {
|
|
225
|
-
if (e1) {
|
|
226
|
-
monitor.emit('resetServer', e1);
|
|
214
|
+
checkServer(monitor, e0 => {
|
|
215
|
+
if (e0 == null) {
|
|
216
|
+
return done();
|
|
227
217
|
}
|
|
228
218
|
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
219
|
+
// otherwise an error occured on initial discovery, also bail
|
|
220
|
+
if (monitor[kServer].description.type === ServerType.Unknown) {
|
|
221
|
+
monitor.emit('resetServer', e0);
|
|
222
|
+
return done();
|
|
223
|
+
}
|
|
233
224
|
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
}
|
|
225
|
+
// According to the SDAM specification's "Network error during server check" section, if
|
|
226
|
+
// an ismaster call fails we reset the server's pool. If a server was once connected,
|
|
227
|
+
// change its type to `Unknown` only after retrying once.
|
|
228
|
+
monitor.emit('resetConnectionPool');
|
|
239
229
|
|
|
240
|
-
|
|
230
|
+
checkServer(monitor, e1 => {
|
|
231
|
+
if (e1) {
|
|
232
|
+
monitor.emit('resetServer', e1);
|
|
233
|
+
}
|
|
241
234
|
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
}, ms || heartbeatFrequencyMS);
|
|
235
|
+
done();
|
|
236
|
+
});
|
|
237
|
+
});
|
|
238
|
+
};
|
|
247
239
|
}
|
|
248
240
|
|
|
249
241
|
module.exports = {
|
package/lib/core/sdam/server.js
CHANGED
|
@@ -110,9 +110,8 @@ class Server extends EventEmitter {
|
|
|
110
110
|
|
|
111
111
|
// create the connection pool
|
|
112
112
|
// NOTE: this used to happen in `connect`, we supported overriding pool options there
|
|
113
|
-
const addressParts = this.description.address.split(':');
|
|
114
113
|
const poolOptions = Object.assign(
|
|
115
|
-
{ host:
|
|
114
|
+
{ host: this.description.host, port: this.description.port, bson: this.s.bson },
|
|
116
115
|
options
|
|
117
116
|
);
|
|
118
117
|
|
|
@@ -415,6 +414,10 @@ Object.defineProperty(Server.prototype, 'clusterTime', {
|
|
|
415
414
|
});
|
|
416
415
|
|
|
417
416
|
function calculateRoundTripTime(oldRtt, duration) {
|
|
417
|
+
if (oldRtt === -1) {
|
|
418
|
+
return duration;
|
|
419
|
+
}
|
|
420
|
+
|
|
418
421
|
const alpha = 0.2;
|
|
419
422
|
return alpha * duration + (1 - alpha) * oldRtt;
|
|
420
423
|
}
|
|
@@ -4,6 +4,7 @@ const arrayStrictEqual = require('../utils').arrayStrictEqual;
|
|
|
4
4
|
const tagsStrictEqual = require('../utils').tagsStrictEqual;
|
|
5
5
|
const errorStrictEqual = require('../utils').errorStrictEqual;
|
|
6
6
|
const ServerType = require('./common').ServerType;
|
|
7
|
+
const now = require('../../utils').now;
|
|
7
8
|
|
|
8
9
|
const WRITABLE_SERVER_TYPES = new Set([
|
|
9
10
|
ServerType.RSPrimary,
|
|
@@ -70,7 +71,7 @@ class ServerDescription {
|
|
|
70
71
|
this.address = address;
|
|
71
72
|
this.error = options.error;
|
|
72
73
|
this.roundTripTime = options.roundTripTime || -1;
|
|
73
|
-
this.lastUpdateTime =
|
|
74
|
+
this.lastUpdateTime = now();
|
|
74
75
|
this.lastWriteDate = ismaster.lastWrite ? ismaster.lastWrite.lastWriteDate : null;
|
|
75
76
|
this.opTime = ismaster.lastWrite ? ismaster.lastWrite.opTime : null;
|
|
76
77
|
this.type = parseServerType(ismaster);
|
|
@@ -112,6 +113,16 @@ class ServerDescription {
|
|
|
112
113
|
return WRITABLE_SERVER_TYPES.has(this.type);
|
|
113
114
|
}
|
|
114
115
|
|
|
116
|
+
get host() {
|
|
117
|
+
const chopLength = `:${this.port}`.length;
|
|
118
|
+
return this.address.slice(0, -chopLength);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
get port() {
|
|
122
|
+
const port = this.address.split(':').pop();
|
|
123
|
+
return port ? Number.parseInt(port, 10) : port;
|
|
124
|
+
}
|
|
125
|
+
|
|
115
126
|
/**
|
|
116
127
|
* Determines if another `ServerDescription` is equal to this one per the rules defined
|
|
117
128
|
* in the {@link https://github.com/mongodb/specifications/blob/master/source/server-discovery-and-monitoring/server-discovery-and-monitoring.rst#serverdescription|SDAM spec}
|
|
@@ -48,7 +48,7 @@ function maxStalenessReducer(readPreference, topologyDescription, servers) {
|
|
|
48
48
|
}
|
|
49
49
|
|
|
50
50
|
if (topologyDescription.type === TopologyType.ReplicaSetWithPrimary) {
|
|
51
|
-
const primary = servers.filter(primaryFilter)[0];
|
|
51
|
+
const primary = Array.from(topologyDescription.servers.values()).filter(primaryFilter)[0];
|
|
52
52
|
return servers.reduce((result, server) => {
|
|
53
53
|
const stalenessMS =
|
|
54
54
|
server.lastUpdateTime -
|
|
@@ -60,7 +60,13 @@ function maxStalenessReducer(readPreference, topologyDescription, servers) {
|
|
|
60
60
|
if (staleness <= readPreference.maxStalenessSeconds) result.push(server);
|
|
61
61
|
return result;
|
|
62
62
|
}, []);
|
|
63
|
-
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
if (topologyDescription.type === TopologyType.ReplicaSetNoPrimary) {
|
|
66
|
+
if (servers.length === 0) {
|
|
67
|
+
return servers;
|
|
68
|
+
}
|
|
69
|
+
|
|
64
70
|
const sMax = servers.reduce((max, s) => (s.lastWriteDate > max.lastWriteDate ? s : max));
|
|
65
71
|
return servers.reduce((result, server) => {
|
|
66
72
|
const stalenessMS =
|
|
@@ -197,50 +203,32 @@ function readPreferenceServerSelector(readPreference) {
|
|
|
197
203
|
return latencyWindowReducer(topologyDescription, servers.filter(knownFilter));
|
|
198
204
|
}
|
|
199
205
|
|
|
200
|
-
|
|
206
|
+
const mode = readPreference.mode;
|
|
207
|
+
if (mode === ReadPreference.PRIMARY) {
|
|
201
208
|
return servers.filter(primaryFilter);
|
|
202
209
|
}
|
|
203
210
|
|
|
204
|
-
if (
|
|
205
|
-
return latencyWindowReducer(
|
|
206
|
-
topologyDescription,
|
|
207
|
-
tagSetReducer(
|
|
208
|
-
readPreference,
|
|
209
|
-
maxStalenessReducer(readPreference, topologyDescription, servers)
|
|
210
|
-
)
|
|
211
|
-
).filter(secondaryFilter);
|
|
212
|
-
} else if (readPreference.mode === ReadPreference.NEAREST) {
|
|
213
|
-
return latencyWindowReducer(
|
|
214
|
-
topologyDescription,
|
|
215
|
-
tagSetReducer(
|
|
216
|
-
readPreference,
|
|
217
|
-
maxStalenessReducer(readPreference, topologyDescription, servers)
|
|
218
|
-
)
|
|
219
|
-
).filter(nearestFilter);
|
|
220
|
-
} else if (readPreference.mode === ReadPreference.SECONDARY_PREFERRED) {
|
|
221
|
-
const result = latencyWindowReducer(
|
|
222
|
-
topologyDescription,
|
|
223
|
-
tagSetReducer(
|
|
224
|
-
readPreference,
|
|
225
|
-
maxStalenessReducer(readPreference, topologyDescription, servers)
|
|
226
|
-
)
|
|
227
|
-
).filter(secondaryFilter);
|
|
228
|
-
|
|
229
|
-
return result.length === 0 ? servers.filter(primaryFilter) : result;
|
|
230
|
-
} else if (readPreference.mode === ReadPreference.PRIMARY_PREFERRED) {
|
|
211
|
+
if (mode === ReadPreference.PRIMARY_PREFERRED) {
|
|
231
212
|
const result = servers.filter(primaryFilter);
|
|
232
213
|
if (result.length) {
|
|
233
214
|
return result;
|
|
234
215
|
}
|
|
216
|
+
}
|
|
235
217
|
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
)
|
|
242
|
-
)
|
|
218
|
+
const filter = mode === ReadPreference.NEAREST ? nearestFilter : secondaryFilter;
|
|
219
|
+
const selectedServers = latencyWindowReducer(
|
|
220
|
+
topologyDescription,
|
|
221
|
+
tagSetReducer(
|
|
222
|
+
readPreference,
|
|
223
|
+
maxStalenessReducer(readPreference, topologyDescription, servers.filter(filter))
|
|
224
|
+
)
|
|
225
|
+
);
|
|
226
|
+
|
|
227
|
+
if (mode === ReadPreference.SECONDARY_PREFERRED && selectedServers.length === 0) {
|
|
228
|
+
return servers.filter(primaryFilter);
|
|
243
229
|
}
|
|
230
|
+
|
|
231
|
+
return selectedServers;
|
|
244
232
|
};
|
|
245
233
|
}
|
|
246
234
|
|