mongodb 3.5.5 → 3.5.9
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 +61 -0
- package/lib/bulk/common.js +4 -11
- package/lib/bulk/unordered.js +8 -0
- package/lib/change_stream.js +194 -151
- package/lib/cmap/connection.js +6 -4
- package/lib/cmap/connection_pool.js +11 -3
- package/lib/collection.js +1 -1
- package/lib/core/connection/apm.js +1 -1
- package/lib/core/connection/pool.js +2 -1
- package/lib/core/cursor.js +16 -29
- package/lib/core/error.js +11 -4
- package/lib/core/index.js +0 -1
- package/lib/core/sdam/monitor.js +51 -58
- package/lib/core/sdam/server.js +8 -2
- package/lib/core/sdam/server_description.js +2 -1
- package/lib/core/sdam/server_selection.js +29 -37
- package/lib/core/sessions.js +25 -14
- package/lib/core/topologies/replset.js +4 -4
- package/lib/core/uri_parser.js +2 -2
- package/lib/core/utils.js +13 -23
- package/lib/core/wireprotocol/get_more.js +6 -1
- package/lib/cursor.js +11 -38
- package/lib/error.js +26 -33
- package/lib/mongo_client.js +21 -2
- package/lib/operations/connect.js +11 -0
- package/lib/operations/cursor_ops.js +2 -3
- package/lib/operations/db_ops.js +0 -361
- package/lib/topologies/native_topology.js +12 -2
- package/lib/utils.js +96 -1
- package/package.json +6 -5
|
@@ -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
|
@@ -310,7 +310,7 @@ const DEPRECATED_FIND_OPTIONS = ['maxScan', 'fields', 'snapshot'];
|
|
|
310
310
|
* @param {(ReadPreference|string)} [options.readPreference] The preferred read preference (ReadPreference.PRIMARY, ReadPreference.PRIMARY_PREFERRED, ReadPreference.SECONDARY, ReadPreference.SECONDARY_PREFERRED, ReadPreference.NEAREST).
|
|
311
311
|
* @param {boolean} [options.partial=false] Specify if the cursor should return partial results when querying against a sharded system
|
|
312
312
|
* @param {number} [options.maxTimeMS] Number of milliseconds to wait before aborting the query.
|
|
313
|
-
* @param {number} [options.maxAwaitTimeMS] The maximum amount of time for the server to wait on new documents to satisfy a tailable cursor query. Requires `
|
|
313
|
+
* @param {number} [options.maxAwaitTimeMS] The maximum amount of time for the server to wait on new documents to satisfy a tailable cursor query. Requires `tailable` and `awaitData` to be true
|
|
314
314
|
* @param {boolean} [options.noCursorTimeout] The server normally times out idle cursors after an inactivity period (10 minutes) to prevent excess memory use. Set this option to prevent that.
|
|
315
315
|
* @param {object} [options.collation] Specify collation (MongoDB 3.4 or higher) settings for update operation (see 3.4 documentation for available fields).
|
|
316
316
|
* @param {ClientSession} [options.session] optional session to use for this operation
|
|
@@ -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;
|
|
@@ -359,15 +358,6 @@ class CoreCursor extends Readable {
|
|
|
359
358
|
return this.push(this.cursorState.streamOptions.transform(result));
|
|
360
359
|
}
|
|
361
360
|
|
|
362
|
-
// If we provided a map function
|
|
363
|
-
if (
|
|
364
|
-
this.cursorState.transforms &&
|
|
365
|
-
typeof this.cursorState.transforms.doc === 'function' &&
|
|
366
|
-
result != null
|
|
367
|
-
) {
|
|
368
|
-
return this.push(this.cursorState.transforms.doc(result));
|
|
369
|
-
}
|
|
370
|
-
|
|
371
361
|
// Return the result
|
|
372
362
|
this.push(result);
|
|
373
363
|
|
|
@@ -421,7 +411,15 @@ class CoreCursor extends Readable {
|
|
|
421
411
|
batchSize = this.cursorState.limit - this.cursorState.currentLimit;
|
|
422
412
|
}
|
|
423
413
|
|
|
424
|
-
|
|
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
|
+
});
|
|
425
423
|
}
|
|
426
424
|
|
|
427
425
|
_initializeCursor(callback) {
|
|
@@ -442,18 +440,15 @@ class CoreCursor extends Readable {
|
|
|
442
440
|
}
|
|
443
441
|
|
|
444
442
|
function done(err, result) {
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
cursor.cursorState.cursorId.isZero() &&
|
|
448
|
-
cursor._endSession
|
|
449
|
-
) {
|
|
443
|
+
const cursorState = cursor.cursorState;
|
|
444
|
+
if (err || (cursorState.cursorId && cursorState.cursorId.isZero())) {
|
|
450
445
|
cursor._endSession();
|
|
451
446
|
}
|
|
452
447
|
|
|
453
448
|
if (
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
449
|
+
cursorState.documents.length === 0 &&
|
|
450
|
+
cursorState.cursorId &&
|
|
451
|
+
cursorState.cursorId.isZero() &&
|
|
457
452
|
!cursor.cmd.tailable &&
|
|
458
453
|
!cursor.cmd.awaitData
|
|
459
454
|
) {
|
|
@@ -699,8 +694,8 @@ function _setCursorNotifiedImpl(self, callback) {
|
|
|
699
694
|
self.cursorState.documents = [];
|
|
700
695
|
self.cursorState.cursorIndex = 0;
|
|
701
696
|
|
|
702
|
-
if (self.
|
|
703
|
-
self._endSession(
|
|
697
|
+
if (self.cursorState.session) {
|
|
698
|
+
self._endSession(callback);
|
|
704
699
|
return;
|
|
705
700
|
}
|
|
706
701
|
|
|
@@ -783,17 +778,9 @@ function nextFunction(self, callback) {
|
|
|
783
778
|
// Execute the next get more
|
|
784
779
|
self._getMore(function(err, doc, connection) {
|
|
785
780
|
if (err) {
|
|
786
|
-
if (err instanceof MongoError) {
|
|
787
|
-
err[mongoErrorContextSymbol].isGetMore = true;
|
|
788
|
-
}
|
|
789
|
-
|
|
790
781
|
return handleCallback(callback, err);
|
|
791
782
|
}
|
|
792
783
|
|
|
793
|
-
if (self.cursorState.cursorId && self.cursorState.cursorId.isZero() && self._endSession) {
|
|
794
|
-
self._endSession();
|
|
795
|
-
}
|
|
796
|
-
|
|
797
784
|
// Save the returned connection to ensure all getMore's fire over the same connection
|
|
798
785
|
self.connection = connection;
|
|
799
786
|
|
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
|
};
|
|
@@ -89,7 +95,14 @@ class Monitor extends EventEmitter {
|
|
|
89
95
|
return;
|
|
90
96
|
}
|
|
91
97
|
|
|
92
|
-
|
|
98
|
+
// start
|
|
99
|
+
const heartbeatFrequencyMS = this.options.heartbeatFrequencyMS;
|
|
100
|
+
const minHeartbeatFrequencyMS = this.options.minHeartbeatFrequencyMS;
|
|
101
|
+
this[kMonitorId] = makeInterruptableAsyncInterval(monitorServer(this), {
|
|
102
|
+
interval: heartbeatFrequencyMS,
|
|
103
|
+
minInterval: minHeartbeatFrequencyMS,
|
|
104
|
+
immediate: true
|
|
105
|
+
});
|
|
93
106
|
}
|
|
94
107
|
|
|
95
108
|
requestCheck() {
|
|
@@ -97,31 +110,19 @@ class Monitor extends EventEmitter {
|
|
|
97
110
|
return;
|
|
98
111
|
}
|
|
99
112
|
|
|
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);
|
|
113
|
+
this[kMonitorId].wake();
|
|
114
114
|
}
|
|
115
115
|
|
|
116
116
|
close() {
|
|
117
|
-
if (this
|
|
117
|
+
if (isInCloseState(this)) {
|
|
118
118
|
return;
|
|
119
119
|
}
|
|
120
120
|
|
|
121
121
|
stateTransition(this, STATE_CLOSING);
|
|
122
122
|
this[kCancellationToken].emit('cancel');
|
|
123
123
|
if (this[kMonitorId]) {
|
|
124
|
-
|
|
124
|
+
this[kMonitorId].stop();
|
|
125
|
+
this[kMonitorId] = null;
|
|
125
126
|
}
|
|
126
127
|
|
|
127
128
|
if (this[kConnection]) {
|
|
@@ -138,7 +139,7 @@ function checkServer(monitor, callback) {
|
|
|
138
139
|
monitor[kConnection] = undefined;
|
|
139
140
|
}
|
|
140
141
|
|
|
141
|
-
const start =
|
|
142
|
+
const start = now();
|
|
142
143
|
monitor.emit('serverHeartbeatStarted', new ServerHeartbeatStartedEvent(monitor.address));
|
|
143
144
|
|
|
144
145
|
function failureHandler(err) {
|
|
@@ -186,7 +187,7 @@ function checkServer(monitor, callback) {
|
|
|
186
187
|
return;
|
|
187
188
|
}
|
|
188
189
|
|
|
189
|
-
if (monitor
|
|
190
|
+
if (isInCloseState(monitor)) {
|
|
190
191
|
conn.destroy({ force: true });
|
|
191
192
|
failureHandler(new MongoError('monitor was destroyed'));
|
|
192
193
|
return;
|
|
@@ -198,52 +199,44 @@ function checkServer(monitor, callback) {
|
|
|
198
199
|
}
|
|
199
200
|
|
|
200
201
|
function monitorServer(monitor) {
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
202
|
+
return callback => {
|
|
203
|
+
stateTransition(monitor, STATE_MONITORING);
|
|
204
|
+
function done() {
|
|
205
|
+
if (!isInCloseState(monitor)) {
|
|
206
|
+
stateTransition(monitor, STATE_IDLE);
|
|
207
|
+
}
|
|
205
208
|
|
|
206
|
-
|
|
207
|
-
if (e0 == null) {
|
|
208
|
-
rescheduleMonitoring(monitor);
|
|
209
|
-
return;
|
|
209
|
+
callback();
|
|
210
210
|
}
|
|
211
211
|
|
|
212
|
-
//
|
|
213
|
-
|
|
214
|
-
monitor.emit('resetServer', e0);
|
|
215
|
-
rescheduleMonitoring(monitor);
|
|
216
|
-
return;
|
|
217
|
-
}
|
|
212
|
+
// TODO: the next line is a legacy event, remove in v4
|
|
213
|
+
process.nextTick(() => monitor.emit('monitoring', monitor[kServer]));
|
|
218
214
|
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
monitor.emit('resetConnectionPool');
|
|
223
|
-
|
|
224
|
-
checkServer(monitor, e1 => {
|
|
225
|
-
if (e1) {
|
|
226
|
-
monitor.emit('resetServer', e1);
|
|
215
|
+
checkServer(monitor, e0 => {
|
|
216
|
+
if (e0 == null) {
|
|
217
|
+
return done();
|
|
227
218
|
}
|
|
228
219
|
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
220
|
+
// otherwise an error occured on initial discovery, also bail
|
|
221
|
+
if (monitor[kServer].description.type === ServerType.Unknown) {
|
|
222
|
+
monitor.emit('resetServer', e0);
|
|
223
|
+
return done();
|
|
224
|
+
}
|
|
233
225
|
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
}
|
|
226
|
+
// According to the SDAM specification's "Network error during server check" section, if
|
|
227
|
+
// an ismaster call fails we reset the server's pool. If a server was once connected,
|
|
228
|
+
// change its type to `Unknown` only after retrying once.
|
|
229
|
+
monitor.emit('resetConnectionPool');
|
|
239
230
|
|
|
240
|
-
|
|
231
|
+
checkServer(monitor, e1 => {
|
|
232
|
+
if (e1) {
|
|
233
|
+
monitor.emit('resetServer', e1);
|
|
234
|
+
}
|
|
241
235
|
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
}, ms || heartbeatFrequencyMS);
|
|
236
|
+
done();
|
|
237
|
+
});
|
|
238
|
+
});
|
|
239
|
+
};
|
|
247
240
|
}
|
|
248
241
|
|
|
249
242
|
module.exports = {
|
package/lib/core/sdam/server.js
CHANGED
|
@@ -415,6 +415,10 @@ Object.defineProperty(Server.prototype, 'clusterTime', {
|
|
|
415
415
|
});
|
|
416
416
|
|
|
417
417
|
function calculateRoundTripTime(oldRtt, duration) {
|
|
418
|
+
if (oldRtt === -1) {
|
|
419
|
+
return duration;
|
|
420
|
+
}
|
|
421
|
+
|
|
418
422
|
const alpha = 0.2;
|
|
419
423
|
return alpha * duration + (1 - alpha) * oldRtt;
|
|
420
424
|
}
|
|
@@ -463,11 +467,13 @@ function markServerUnknown(server, error) {
|
|
|
463
467
|
}
|
|
464
468
|
|
|
465
469
|
function makeOperationHandler(server, options, callback) {
|
|
470
|
+
const session = options && options.session;
|
|
471
|
+
|
|
466
472
|
return function handleOperationResult(err, result) {
|
|
467
473
|
if (err) {
|
|
468
474
|
if (err instanceof MongoNetworkError) {
|
|
469
|
-
if (
|
|
470
|
-
|
|
475
|
+
if (session && !session.hasEnded) {
|
|
476
|
+
session.serverSession.isDirty = true;
|
|
471
477
|
}
|
|
472
478
|
|
|
473
479
|
if (!isNetworkTimeoutError(err)) {
|
|
@@ -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);
|
|
@@ -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 =
|
|
@@ -186,6 +192,10 @@ function readPreferenceServerSelector(readPreference) {
|
|
|
186
192
|
);
|
|
187
193
|
}
|
|
188
194
|
|
|
195
|
+
if (topologyDescription.type === TopologyType.Unknown) {
|
|
196
|
+
return [];
|
|
197
|
+
}
|
|
198
|
+
|
|
189
199
|
if (
|
|
190
200
|
topologyDescription.type === TopologyType.Single ||
|
|
191
201
|
topologyDescription.type === TopologyType.Sharded
|
|
@@ -193,50 +203,32 @@ function readPreferenceServerSelector(readPreference) {
|
|
|
193
203
|
return latencyWindowReducer(topologyDescription, servers.filter(knownFilter));
|
|
194
204
|
}
|
|
195
205
|
|
|
196
|
-
|
|
206
|
+
const mode = readPreference.mode;
|
|
207
|
+
if (mode === ReadPreference.PRIMARY) {
|
|
197
208
|
return servers.filter(primaryFilter);
|
|
198
209
|
}
|
|
199
210
|
|
|
200
|
-
if (
|
|
201
|
-
return latencyWindowReducer(
|
|
202
|
-
topologyDescription,
|
|
203
|
-
tagSetReducer(
|
|
204
|
-
readPreference,
|
|
205
|
-
maxStalenessReducer(readPreference, topologyDescription, servers)
|
|
206
|
-
)
|
|
207
|
-
).filter(secondaryFilter);
|
|
208
|
-
} else if (readPreference.mode === ReadPreference.NEAREST) {
|
|
209
|
-
return latencyWindowReducer(
|
|
210
|
-
topologyDescription,
|
|
211
|
-
tagSetReducer(
|
|
212
|
-
readPreference,
|
|
213
|
-
maxStalenessReducer(readPreference, topologyDescription, servers)
|
|
214
|
-
)
|
|
215
|
-
).filter(nearestFilter);
|
|
216
|
-
} else if (readPreference.mode === ReadPreference.SECONDARY_PREFERRED) {
|
|
217
|
-
const result = latencyWindowReducer(
|
|
218
|
-
topologyDescription,
|
|
219
|
-
tagSetReducer(
|
|
220
|
-
readPreference,
|
|
221
|
-
maxStalenessReducer(readPreference, topologyDescription, servers)
|
|
222
|
-
)
|
|
223
|
-
).filter(secondaryFilter);
|
|
224
|
-
|
|
225
|
-
return result.length === 0 ? servers.filter(primaryFilter) : result;
|
|
226
|
-
} else if (readPreference.mode === ReadPreference.PRIMARY_PREFERRED) {
|
|
211
|
+
if (mode === ReadPreference.PRIMARY_PREFERRED) {
|
|
227
212
|
const result = servers.filter(primaryFilter);
|
|
228
213
|
if (result.length) {
|
|
229
214
|
return result;
|
|
230
215
|
}
|
|
216
|
+
}
|
|
217
|
+
|
|
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
|
+
);
|
|
231
226
|
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
tagSetReducer(
|
|
235
|
-
readPreference,
|
|
236
|
-
maxStalenessReducer(readPreference, topologyDescription, servers)
|
|
237
|
-
)
|
|
238
|
-
).filter(secondaryFilter);
|
|
227
|
+
if (mode === ReadPreference.SECONDARY_PREFERRED && selectedServers.length === 0) {
|
|
228
|
+
return servers.filter(primaryFilter);
|
|
239
229
|
}
|
|
230
|
+
|
|
231
|
+
return selectedServers;
|
|
240
232
|
};
|
|
241
233
|
}
|
|
242
234
|
|