mongodb 3.4.0 → 3.5.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/HISTORY.md +72 -0
- package/index.js +1 -0
- package/lib/bulk/common.js +1 -1
- package/lib/cmap/connection.js +369 -0
- package/lib/cmap/connection_pool.js +593 -0
- package/lib/cmap/errors.js +35 -0
- package/lib/cmap/events.js +154 -0
- package/lib/{core/cmap → cmap}/message_stream.js +19 -17
- package/lib/cmap/stream_description.js +45 -0
- package/lib/core/auth/scram.js +1 -1
- package/lib/core/connection/apm.js +24 -7
- package/lib/core/connection/connect.js +55 -26
- package/lib/core/connection/pool.js +3 -16
- package/lib/core/error.js +27 -10
- package/lib/core/index.js +1 -0
- package/lib/core/sdam/events.js +124 -0
- package/lib/core/sdam/monitor.js +251 -0
- package/lib/core/sdam/server.js +148 -198
- package/lib/core/sdam/server_description.js +6 -4
- package/lib/core/sdam/server_selection.js +0 -91
- package/lib/core/sdam/topology.js +162 -136
- package/lib/core/sdam/topology_description.js +10 -8
- package/lib/core/sessions.js +16 -3
- package/lib/core/topologies/mongos.js +5 -13
- package/lib/core/topologies/replset.js +5 -10
- package/lib/core/topologies/server.js +9 -4
- package/lib/core/topologies/shared.js +0 -60
- package/lib/core/transactions.js +18 -7
- package/lib/core/uri_parser.js +3 -0
- package/lib/core/utils.js +84 -18
- package/lib/core/wireprotocol/command.js +2 -9
- package/lib/gridfs-stream/upload.js +1 -1
- package/lib/mongo_client.js +6 -6
- package/lib/operations/connect.js +118 -49
- package/lib/operations/execute_operation.js +31 -40
- package/lib/operations/find_one.js +13 -9
- package/lib/topologies/mongos.js +2 -9
- package/lib/topologies/native_topology.js +2 -6
- package/lib/topologies/replset.js +2 -9
- package/lib/topologies/server.js +2 -9
- package/lib/topologies/topology_base.js +4 -25
- package/lib/utils.js +11 -2
- package/package.json +3 -2
- package/lib/core/cmap/connection.js +0 -220
- package/lib/core/sdam/monitoring.js +0 -241
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* The base class for all monitoring events published from the connection pool
|
|
5
|
+
*
|
|
6
|
+
* @property {number} time A timestamp when the event was created
|
|
7
|
+
* @property {string} address The address (host/port pair) of the pool
|
|
8
|
+
*/
|
|
9
|
+
class ConnectionPoolMonitoringEvent {
|
|
10
|
+
constructor(pool) {
|
|
11
|
+
this.time = new Date();
|
|
12
|
+
this.address = pool.address;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* An event published when a connection pool is created
|
|
18
|
+
*
|
|
19
|
+
* @property {Object} options The options used to create this connection pool
|
|
20
|
+
*/
|
|
21
|
+
class ConnectionPoolCreatedEvent extends ConnectionPoolMonitoringEvent {
|
|
22
|
+
constructor(pool) {
|
|
23
|
+
super(pool);
|
|
24
|
+
this.options = pool.options;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* An event published when a connection pool is closed
|
|
30
|
+
*/
|
|
31
|
+
class ConnectionPoolClosedEvent extends ConnectionPoolMonitoringEvent {
|
|
32
|
+
constructor(pool) {
|
|
33
|
+
super(pool);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* An event published when a connection pool creates a new connection
|
|
39
|
+
*
|
|
40
|
+
* @property {number} connectionId A monotonically increasing, per-pool id for the newly created connection
|
|
41
|
+
*/
|
|
42
|
+
class ConnectionCreatedEvent extends ConnectionPoolMonitoringEvent {
|
|
43
|
+
constructor(pool, connection) {
|
|
44
|
+
super(pool);
|
|
45
|
+
this.connectionId = connection.id;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* An event published when a connection is ready for use
|
|
51
|
+
*
|
|
52
|
+
* @property {number} connectionId The id of the connection
|
|
53
|
+
*/
|
|
54
|
+
class ConnectionReadyEvent extends ConnectionPoolMonitoringEvent {
|
|
55
|
+
constructor(pool, connection) {
|
|
56
|
+
super(pool);
|
|
57
|
+
this.connectionId = connection.id;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* An event published when a connection is closed
|
|
63
|
+
*
|
|
64
|
+
* @property {number} connectionId The id of the connection
|
|
65
|
+
* @property {string} reason The reason the connection was closed
|
|
66
|
+
*/
|
|
67
|
+
class ConnectionClosedEvent extends ConnectionPoolMonitoringEvent {
|
|
68
|
+
constructor(pool, connection, reason) {
|
|
69
|
+
super(pool);
|
|
70
|
+
this.connectionId = connection.id;
|
|
71
|
+
this.reason = reason || 'unknown';
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* An event published when a request to check a connection out begins
|
|
77
|
+
*/
|
|
78
|
+
class ConnectionCheckOutStartedEvent extends ConnectionPoolMonitoringEvent {
|
|
79
|
+
constructor(pool) {
|
|
80
|
+
super(pool);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* An event published when a request to check a connection out fails
|
|
86
|
+
*
|
|
87
|
+
* @property {string} reason The reason the attempt to check out failed
|
|
88
|
+
*/
|
|
89
|
+
class ConnectionCheckOutFailedEvent extends ConnectionPoolMonitoringEvent {
|
|
90
|
+
constructor(pool, reason) {
|
|
91
|
+
super(pool);
|
|
92
|
+
this.reason = reason;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* An event published when a connection is checked out of the connection pool
|
|
98
|
+
*
|
|
99
|
+
* @property {number} connectionId The id of the connection
|
|
100
|
+
*/
|
|
101
|
+
class ConnectionCheckedOutEvent extends ConnectionPoolMonitoringEvent {
|
|
102
|
+
constructor(pool, connection) {
|
|
103
|
+
super(pool);
|
|
104
|
+
this.connectionId = connection.id;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* An event published when a connection is checked into the connection pool
|
|
110
|
+
*
|
|
111
|
+
* @property {number} connectionId The id of the connection
|
|
112
|
+
*/
|
|
113
|
+
class ConnectionCheckedInEvent extends ConnectionPoolMonitoringEvent {
|
|
114
|
+
constructor(pool, connection) {
|
|
115
|
+
super(pool);
|
|
116
|
+
this.connectionId = connection.id;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* An event published when a connection pool is cleared
|
|
122
|
+
*/
|
|
123
|
+
class ConnectionPoolClearedEvent extends ConnectionPoolMonitoringEvent {
|
|
124
|
+
constructor(pool) {
|
|
125
|
+
super(pool);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
const CMAP_EVENT_NAMES = [
|
|
130
|
+
'connectionPoolCreated',
|
|
131
|
+
'connectionPoolClosed',
|
|
132
|
+
'connectionCreated',
|
|
133
|
+
'connectionReady',
|
|
134
|
+
'connectionClosed',
|
|
135
|
+
'connectionCheckOutStarted',
|
|
136
|
+
'connectionCheckOutFailed',
|
|
137
|
+
'connectionCheckedOut',
|
|
138
|
+
'connectionCheckedIn',
|
|
139
|
+
'connectionPoolCleared'
|
|
140
|
+
];
|
|
141
|
+
|
|
142
|
+
module.exports = {
|
|
143
|
+
CMAP_EVENT_NAMES,
|
|
144
|
+
ConnectionPoolCreatedEvent,
|
|
145
|
+
ConnectionPoolClosedEvent,
|
|
146
|
+
ConnectionCreatedEvent,
|
|
147
|
+
ConnectionReadyEvent,
|
|
148
|
+
ConnectionClosedEvent,
|
|
149
|
+
ConnectionCheckOutStartedEvent,
|
|
150
|
+
ConnectionCheckOutFailedEvent,
|
|
151
|
+
ConnectionCheckedOutEvent,
|
|
152
|
+
ConnectionCheckedInEvent,
|
|
153
|
+
ConnectionPoolClearedEvent
|
|
154
|
+
};
|
|
@@ -2,20 +2,20 @@
|
|
|
2
2
|
|
|
3
3
|
const Duplex = require('stream').Duplex;
|
|
4
4
|
const BufferList = require('bl');
|
|
5
|
-
const MongoParseError = require('../error').MongoParseError;
|
|
6
|
-
const decompress = require('../wireprotocol/compression').decompress;
|
|
7
|
-
const Response = require('../connection/commands').Response;
|
|
8
|
-
const BinMsg = require('../connection/msg').BinMsg;
|
|
9
|
-
const MongoError = require('../error').MongoError;
|
|
10
|
-
const OP_COMPRESSED = require('../wireprotocol/shared').opcodes.OP_COMPRESSED;
|
|
11
|
-
const OP_MSG = require('../wireprotocol/shared').opcodes.OP_MSG;
|
|
12
|
-
const MESSAGE_HEADER_SIZE = require('../wireprotocol/shared').MESSAGE_HEADER_SIZE;
|
|
13
|
-
const COMPRESSION_DETAILS_SIZE = require('../wireprotocol/shared').COMPRESSION_DETAILS_SIZE;
|
|
14
|
-
const opcodes = require('../wireprotocol/shared').opcodes;
|
|
15
|
-
const compress = require('../wireprotocol/compression').compress;
|
|
16
|
-
const compressorIDs = require('../wireprotocol/compression').compressorIDs;
|
|
17
|
-
const uncompressibleCommands = require('../wireprotocol/compression').uncompressibleCommands;
|
|
18
|
-
const Msg = require('../connection/msg').Msg;
|
|
5
|
+
const MongoParseError = require('../core/error').MongoParseError;
|
|
6
|
+
const decompress = require('../core/wireprotocol/compression').decompress;
|
|
7
|
+
const Response = require('../core/connection/commands').Response;
|
|
8
|
+
const BinMsg = require('../core/connection/msg').BinMsg;
|
|
9
|
+
const MongoError = require('../core/error').MongoError;
|
|
10
|
+
const OP_COMPRESSED = require('../core/wireprotocol/shared').opcodes.OP_COMPRESSED;
|
|
11
|
+
const OP_MSG = require('../core/wireprotocol/shared').opcodes.OP_MSG;
|
|
12
|
+
const MESSAGE_HEADER_SIZE = require('../core/wireprotocol/shared').MESSAGE_HEADER_SIZE;
|
|
13
|
+
const COMPRESSION_DETAILS_SIZE = require('../core/wireprotocol/shared').COMPRESSION_DETAILS_SIZE;
|
|
14
|
+
const opcodes = require('../core/wireprotocol/shared').opcodes;
|
|
15
|
+
const compress = require('../core/wireprotocol/compression').compress;
|
|
16
|
+
const compressorIDs = require('../core/wireprotocol/compression').compressorIDs;
|
|
17
|
+
const uncompressibleCommands = require('../core/wireprotocol/compression').uncompressibleCommands;
|
|
18
|
+
const Msg = require('../core/connection/msg').Msg;
|
|
19
19
|
|
|
20
20
|
const kDefaultMaxBsonMessageSize = 1024 * 1024 * 16 * 4;
|
|
21
21
|
const kBuffer = Symbol('buffer');
|
|
@@ -61,8 +61,9 @@ class MessageStream extends Duplex {
|
|
|
61
61
|
}
|
|
62
62
|
|
|
63
63
|
const messageBuffer = buffer.slice(0, sizeOfMessage);
|
|
64
|
-
processMessage(this, messageBuffer, callback);
|
|
65
64
|
buffer.consume(sizeOfMessage);
|
|
65
|
+
|
|
66
|
+
processMessage(this, messageBuffer, callback);
|
|
66
67
|
}
|
|
67
68
|
}
|
|
68
69
|
|
|
@@ -76,7 +77,8 @@ class MessageStream extends Duplex {
|
|
|
76
77
|
// TODO: agreed compressor should live in `StreamDescription`
|
|
77
78
|
const shouldCompress = operationDescription && !!operationDescription.agreedCompressor;
|
|
78
79
|
if (!shouldCompress || !canCompress(command)) {
|
|
79
|
-
|
|
80
|
+
const data = command.toBin();
|
|
81
|
+
this.push(Array.isArray(data) ? Buffer.concat(data) : data);
|
|
80
82
|
return;
|
|
81
83
|
}
|
|
82
84
|
|
|
@@ -125,7 +127,7 @@ function canCompress(command) {
|
|
|
125
127
|
|
|
126
128
|
function processMessage(stream, message, callback) {
|
|
127
129
|
const messageHeader = {
|
|
128
|
-
|
|
130
|
+
length: message.readInt32LE(0),
|
|
129
131
|
requestId: message.readInt32LE(4),
|
|
130
132
|
responseTo: message.readInt32LE(8),
|
|
131
133
|
opCode: message.readInt32LE(12)
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
const parseServerType = require('../core/sdam/server_description').parseServerType;
|
|
3
|
+
|
|
4
|
+
const RESPONSE_FIELDS = [
|
|
5
|
+
'minWireVersion',
|
|
6
|
+
'maxWireVersion',
|
|
7
|
+
'maxBsonObjectSize',
|
|
8
|
+
'maxMessageSizeBytes',
|
|
9
|
+
'maxWriteBatchSize',
|
|
10
|
+
'__nodejs_mock_server__'
|
|
11
|
+
];
|
|
12
|
+
|
|
13
|
+
class StreamDescription {
|
|
14
|
+
constructor(address, options) {
|
|
15
|
+
this.address = address;
|
|
16
|
+
this.type = parseServerType(null);
|
|
17
|
+
this.minWireVersion = undefined;
|
|
18
|
+
this.maxWireVersion = undefined;
|
|
19
|
+
this.maxBsonObjectSize = 16777216;
|
|
20
|
+
this.maxMessageSizeBytes = 48000000;
|
|
21
|
+
this.maxWriteBatchSize = 100000;
|
|
22
|
+
this.compressors =
|
|
23
|
+
options && options.compression && Array.isArray(options.compression.compressors)
|
|
24
|
+
? options.compression.compressors
|
|
25
|
+
: [];
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
receiveResponse(response) {
|
|
29
|
+
this.type = parseServerType(response);
|
|
30
|
+
|
|
31
|
+
RESPONSE_FIELDS.forEach(field => {
|
|
32
|
+
if (typeof response[field] !== 'undefined') {
|
|
33
|
+
this[field] = response[field];
|
|
34
|
+
}
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
if (response.compression) {
|
|
38
|
+
this.compressor = this.compressors.filter(c => response.compression.indexOf(c) !== -1)[0];
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
module.exports = {
|
|
44
|
+
StreamDescription
|
|
45
|
+
};
|
package/lib/core/auth/scram.js
CHANGED
|
@@ -236,7 +236,7 @@ class ScramSHA extends AuthProvider {
|
|
|
236
236
|
};
|
|
237
237
|
|
|
238
238
|
sendAuthCommand(connection, `${db}.$cmd`, saslContinueCmd, (err, r) => {
|
|
239
|
-
if (r && typeof r.ok === 'number' && r.ok === 0) {
|
|
239
|
+
if (err || (r && typeof r.ok === 'number' && r.ok === 0)) {
|
|
240
240
|
callback(err, r);
|
|
241
241
|
return;
|
|
242
242
|
}
|
|
@@ -23,8 +23,9 @@ const namespace = command => command.ns;
|
|
|
23
23
|
const databaseName = command => command.ns.split('.')[0];
|
|
24
24
|
const collectionName = command => command.ns.split('.')[1];
|
|
25
25
|
const generateConnectionId = pool =>
|
|
26
|
-
pool.options ? `${pool.options.host}:${pool.options.port}` : pool.
|
|
26
|
+
pool.options ? `${pool.options.host}:${pool.options.port}` : pool.address;
|
|
27
27
|
const maybeRedact = (commandName, result) => (SENSITIVE_COMMANDS.has(commandName) ? {} : result);
|
|
28
|
+
const isLegacyPool = pool => pool.s && pool.queue;
|
|
28
29
|
|
|
29
30
|
const LEGACY_FIND_QUERY_MAP = {
|
|
30
31
|
$query: 'filter',
|
|
@@ -151,6 +152,22 @@ const extractReply = (command, reply) => {
|
|
|
151
152
|
return reply && reply.result ? reply.result : reply;
|
|
152
153
|
};
|
|
153
154
|
|
|
155
|
+
const extractConnectionDetails = pool => {
|
|
156
|
+
if (isLegacyPool(pool)) {
|
|
157
|
+
return {
|
|
158
|
+
connectionId: generateConnectionId(pool)
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// APM in the modern pool is done at the `Connection` level, so we rename it here for
|
|
163
|
+
// readability.
|
|
164
|
+
const connection = pool;
|
|
165
|
+
return {
|
|
166
|
+
address: connection.address,
|
|
167
|
+
connectionId: connection.id
|
|
168
|
+
};
|
|
169
|
+
};
|
|
170
|
+
|
|
154
171
|
/** An event indicating the start of a given command */
|
|
155
172
|
class CommandStartedEvent {
|
|
156
173
|
/**
|
|
@@ -162,6 +179,7 @@ class CommandStartedEvent {
|
|
|
162
179
|
constructor(pool, command) {
|
|
163
180
|
const cmd = extractCommand(command);
|
|
164
181
|
const commandName = extractCommandName(cmd);
|
|
182
|
+
const connectionDetails = extractConnectionDetails(pool);
|
|
165
183
|
|
|
166
184
|
// NOTE: remove in major revision, this is not spec behavior
|
|
167
185
|
if (SENSITIVE_COMMANDS.has(commandName)) {
|
|
@@ -169,8 +187,7 @@ class CommandStartedEvent {
|
|
|
169
187
|
this.commandObj[commandName] = true;
|
|
170
188
|
}
|
|
171
189
|
|
|
172
|
-
Object.assign(this, {
|
|
173
|
-
connectionId: generateConnectionId(pool),
|
|
190
|
+
Object.assign(this, connectionDetails, {
|
|
174
191
|
requestId: command.requestId,
|
|
175
192
|
databaseName: databaseName(command),
|
|
176
193
|
commandName,
|
|
@@ -192,9 +209,9 @@ class CommandSucceededEvent {
|
|
|
192
209
|
constructor(pool, command, reply, started) {
|
|
193
210
|
const cmd = extractCommand(command);
|
|
194
211
|
const commandName = extractCommandName(cmd);
|
|
212
|
+
const connectionDetails = extractConnectionDetails(pool);
|
|
195
213
|
|
|
196
|
-
Object.assign(this, {
|
|
197
|
-
connectionId: generateConnectionId(pool),
|
|
214
|
+
Object.assign(this, connectionDetails, {
|
|
198
215
|
requestId: command.requestId,
|
|
199
216
|
commandName,
|
|
200
217
|
duration: calculateDurationInMs(started),
|
|
@@ -216,9 +233,9 @@ class CommandFailedEvent {
|
|
|
216
233
|
constructor(pool, command, error, started) {
|
|
217
234
|
const cmd = extractCommand(command);
|
|
218
235
|
const commandName = extractCommandName(cmd);
|
|
236
|
+
const connectionDetails = extractConnectionDetails(pool);
|
|
219
237
|
|
|
220
|
-
Object.assign(this, {
|
|
221
|
-
connectionId: generateConnectionId(pool),
|
|
238
|
+
Object.assign(this, connectionDetails, {
|
|
222
239
|
requestId: command.requestId,
|
|
223
240
|
commandName,
|
|
224
241
|
duration: calculateDurationInMs(started),
|
|
@@ -3,11 +3,11 @@ const net = require('net');
|
|
|
3
3
|
const tls = require('tls');
|
|
4
4
|
const Connection = require('./connection');
|
|
5
5
|
const Query = require('./commands').Query;
|
|
6
|
-
const createClientInfo = require('../topologies/shared').createClientInfo;
|
|
7
6
|
const MongoError = require('../error').MongoError;
|
|
8
7
|
const MongoNetworkError = require('../error').MongoNetworkError;
|
|
9
8
|
const defaultAuthProviders = require('../auth/defaultAuthProviders').defaultAuthProviders;
|
|
10
9
|
const WIRE_CONSTANTS = require('../wireprotocol/constants');
|
|
10
|
+
const makeClientMetadata = require('../utils').makeClientMetadata;
|
|
11
11
|
const MAX_SUPPORTED_WIRE_VERSION = WIRE_CONSTANTS.MAX_SUPPORTED_WIRE_VERSION;
|
|
12
12
|
const MAX_SUPPORTED_SERVER_VERSION = WIRE_CONSTANTS.MAX_SUPPORTED_SERVER_VERSION;
|
|
13
13
|
const MIN_SUPPORTED_WIRE_VERSION = WIRE_CONSTANTS.MIN_SUPPORTED_WIRE_VERSION;
|
|
@@ -36,6 +36,10 @@ function connect(options, cancellationToken, callback) {
|
|
|
36
36
|
});
|
|
37
37
|
}
|
|
38
38
|
|
|
39
|
+
function isModernConnectionType(conn) {
|
|
40
|
+
return typeof conn.command === 'function';
|
|
41
|
+
}
|
|
42
|
+
|
|
39
43
|
function getSaslSupportedMechs(options) {
|
|
40
44
|
if (!(options && options.credentials)) {
|
|
41
45
|
return {};
|
|
@@ -101,42 +105,51 @@ function performInitialHandshake(conn, options, _callback) {
|
|
|
101
105
|
const handshakeDoc = Object.assign(
|
|
102
106
|
{
|
|
103
107
|
ismaster: true,
|
|
104
|
-
client:
|
|
108
|
+
client: options.metadata || makeClientMetadata(options),
|
|
105
109
|
compression: compressors
|
|
106
110
|
},
|
|
107
111
|
getSaslSupportedMechs(options)
|
|
108
112
|
);
|
|
109
113
|
|
|
114
|
+
const handshakeOptions = Object.assign({}, options);
|
|
115
|
+
|
|
116
|
+
// The handshake technically is a monitoring check, so its socket timeout should be connectTimeoutMS
|
|
117
|
+
if (options.connectTimeoutMS || options.connectionTimeout) {
|
|
118
|
+
handshakeOptions.socketTimeout = options.connectTimeoutMS || options.connectionTimeout;
|
|
119
|
+
}
|
|
120
|
+
|
|
110
121
|
const start = new Date().getTime();
|
|
111
|
-
runCommand(conn, 'admin.$cmd', handshakeDoc,
|
|
122
|
+
runCommand(conn, 'admin.$cmd', handshakeDoc, handshakeOptions, (err, ismaster) => {
|
|
112
123
|
if (err) {
|
|
113
|
-
callback(err
|
|
124
|
+
callback(err);
|
|
114
125
|
return;
|
|
115
126
|
}
|
|
116
127
|
|
|
117
128
|
if (ismaster.ok === 0) {
|
|
118
|
-
callback(new MongoError(ismaster)
|
|
129
|
+
callback(new MongoError(ismaster));
|
|
119
130
|
return;
|
|
120
131
|
}
|
|
121
132
|
|
|
122
133
|
const supportedServerErr = checkSupportedServer(ismaster, options);
|
|
123
134
|
if (supportedServerErr) {
|
|
124
|
-
callback(supportedServerErr
|
|
135
|
+
callback(supportedServerErr);
|
|
125
136
|
return;
|
|
126
137
|
}
|
|
127
138
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
139
|
+
if (!isModernConnectionType(conn)) {
|
|
140
|
+
// resolve compression
|
|
141
|
+
if (ismaster.compression) {
|
|
142
|
+
const agreedCompressors = compressors.filter(
|
|
143
|
+
compressor => ismaster.compression.indexOf(compressor) !== -1
|
|
144
|
+
);
|
|
133
145
|
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
146
|
+
if (agreedCompressors.length) {
|
|
147
|
+
conn.agreedCompressor = agreedCompressors[0];
|
|
148
|
+
}
|
|
137
149
|
|
|
138
|
-
|
|
139
|
-
|
|
150
|
+
if (options.compression && options.compression.zlibCompressionLevel) {
|
|
151
|
+
conn.zlibCompressionLevel = options.compression.zlibCompressionLevel;
|
|
152
|
+
}
|
|
140
153
|
}
|
|
141
154
|
}
|
|
142
155
|
|
|
@@ -153,7 +166,7 @@ function performInitialHandshake(conn, options, _callback) {
|
|
|
153
166
|
return;
|
|
154
167
|
}
|
|
155
168
|
|
|
156
|
-
callback(
|
|
169
|
+
callback(undefined, conn);
|
|
157
170
|
});
|
|
158
171
|
}
|
|
159
172
|
|
|
@@ -229,7 +242,11 @@ function makeConnection(family, options, cancellationToken, _callback) {
|
|
|
229
242
|
typeof options.keepAliveInitialDelay === 'number' ? options.keepAliveInitialDelay : 300000;
|
|
230
243
|
const noDelay = typeof options.noDelay === 'boolean' ? options.noDelay : true;
|
|
231
244
|
const connectionTimeout =
|
|
232
|
-
typeof options.connectionTimeout === 'number'
|
|
245
|
+
typeof options.connectionTimeout === 'number'
|
|
246
|
+
? options.connectionTimeout
|
|
247
|
+
: typeof options.connectTimeoutMS === 'number'
|
|
248
|
+
? options.connectTimeoutMS
|
|
249
|
+
: 30000;
|
|
233
250
|
const socketTimeout = typeof options.socketTimeout === 'number' ? options.socketTimeout : 360000;
|
|
234
251
|
const rejectUnauthorized =
|
|
235
252
|
typeof options.rejectUnauthorized === 'boolean' ? options.rejectUnauthorized : true;
|
|
@@ -264,6 +281,7 @@ function makeConnection(family, options, cancellationToken, _callback) {
|
|
|
264
281
|
socket.setTimeout(connectionTimeout);
|
|
265
282
|
socket.setNoDelay(noDelay);
|
|
266
283
|
|
|
284
|
+
const connectEvent = useSsl ? 'secureConnect' : 'connect';
|
|
267
285
|
let cancellationHandler;
|
|
268
286
|
function errorHandler(eventName) {
|
|
269
287
|
return err => {
|
|
@@ -272,7 +290,7 @@ function makeConnection(family, options, cancellationToken, _callback) {
|
|
|
272
290
|
cancellationToken.removeListener('cancel', cancellationHandler);
|
|
273
291
|
}
|
|
274
292
|
|
|
275
|
-
socket.removeListener(
|
|
293
|
+
socket.removeListener(connectEvent, connectHandler);
|
|
276
294
|
callback(connectionFailureError(eventName, err));
|
|
277
295
|
};
|
|
278
296
|
}
|
|
@@ -297,17 +315,28 @@ function makeConnection(family, options, cancellationToken, _callback) {
|
|
|
297
315
|
cancellationToken.once('cancel', cancellationHandler);
|
|
298
316
|
}
|
|
299
317
|
|
|
300
|
-
socket.once(
|
|
318
|
+
socket.once(connectEvent, connectHandler);
|
|
301
319
|
}
|
|
302
320
|
|
|
303
321
|
const CONNECTION_ERROR_EVENTS = ['error', 'close', 'timeout', 'parseError'];
|
|
304
322
|
function runCommand(conn, ns, command, options, callback) {
|
|
305
|
-
if (typeof
|
|
306
|
-
|
|
323
|
+
if (typeof options === 'function') (callback = options), (options = {});
|
|
324
|
+
|
|
325
|
+
// are we using the new connection type? if so, no need to simulate a rpc `command` method
|
|
326
|
+
if (isModernConnectionType(conn)) {
|
|
327
|
+
conn.command(ns, command, options, (err, result) => {
|
|
328
|
+
if (err) {
|
|
329
|
+
callback(err);
|
|
330
|
+
return;
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
// NODE-2382: raw wire protocol messages, or command results should not be used anymore
|
|
334
|
+
callback(undefined, result.result);
|
|
335
|
+
});
|
|
336
|
+
|
|
307
337
|
return;
|
|
308
338
|
}
|
|
309
339
|
|
|
310
|
-
if (typeof options === 'function') (callback = options), (options = {});
|
|
311
340
|
const socketTimeout = typeof options.socketTimeout === 'number' ? options.socketTimeout : 360000;
|
|
312
341
|
const bson = conn.options.bson;
|
|
313
342
|
const query = new Query(bson, ns, command, {
|
|
@@ -333,7 +362,7 @@ function runCommand(conn, ns, command, options, callback) {
|
|
|
333
362
|
// ignore all future errors
|
|
334
363
|
conn.on('error', noop);
|
|
335
364
|
|
|
336
|
-
_callback(err
|
|
365
|
+
_callback(err);
|
|
337
366
|
}
|
|
338
367
|
|
|
339
368
|
function messageHandler(msg) {
|
|
@@ -346,7 +375,7 @@ function runCommand(conn, ns, command, options, callback) {
|
|
|
346
375
|
conn.removeListener('message', messageHandler);
|
|
347
376
|
|
|
348
377
|
msg.parse({ promoteValues: true });
|
|
349
|
-
_callback(
|
|
378
|
+
_callback(undefined, msg.documents[0]);
|
|
350
379
|
}
|
|
351
380
|
|
|
352
381
|
conn.setSocketTimeout(socketTimeout);
|
|
@@ -365,7 +394,7 @@ function authenticate(conn, credentials, callback) {
|
|
|
365
394
|
const provider = AUTH_PROVIDERS[mechanism];
|
|
366
395
|
provider.auth(runCommand, [conn], credentials, err => {
|
|
367
396
|
if (err) return callback(err);
|
|
368
|
-
callback(
|
|
397
|
+
callback(undefined, conn);
|
|
369
398
|
});
|
|
370
399
|
}
|
|
371
400
|
|
|
@@ -255,22 +255,6 @@ function connectionFailureHandler(pool, event, err, conn) {
|
|
|
255
255
|
// Remove the connection
|
|
256
256
|
removeConnection(pool, conn);
|
|
257
257
|
|
|
258
|
-
if (
|
|
259
|
-
pool.state !== DRAINING &&
|
|
260
|
-
pool.state !== DESTROYED &&
|
|
261
|
-
pool.options.legacyCompatMode === false
|
|
262
|
-
) {
|
|
263
|
-
// since an error/close/timeout means pool invalidation in a
|
|
264
|
-
// pre-CMAP world, we will issue a custom `drain` event here to
|
|
265
|
-
// signal that the server should be recycled
|
|
266
|
-
stateTransition(pool, DRAINING);
|
|
267
|
-
pool.emit('drain', err);
|
|
268
|
-
|
|
269
|
-
// wait to flush work items so this server isn't selected again immediately
|
|
270
|
-
process.nextTick(() => conn.flush(err));
|
|
271
|
-
return;
|
|
272
|
-
}
|
|
273
|
-
|
|
274
258
|
// flush remaining work items
|
|
275
259
|
conn.flush(err);
|
|
276
260
|
}
|
|
@@ -641,6 +625,9 @@ function destroy(self, connections, options, callback) {
|
|
|
641
625
|
conn.removeAllListeners(eventName);
|
|
642
626
|
}
|
|
643
627
|
|
|
628
|
+
// ignore any errors during destruction
|
|
629
|
+
conn.on('error', () => {});
|
|
630
|
+
|
|
644
631
|
conn.destroy(options, cb);
|
|
645
632
|
},
|
|
646
633
|
err => {
|
package/lib/core/error.js
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
const mongoErrorContextSymbol = Symbol('mongoErrorContextSymbol');
|
|
4
|
-
const maxWireVersion = require('./utils').maxWireVersion;
|
|
5
4
|
|
|
6
5
|
/**
|
|
7
6
|
* Creates a new MongoError
|
|
@@ -95,14 +94,35 @@ class MongoParseError extends MongoError {
|
|
|
95
94
|
*/
|
|
96
95
|
class MongoTimeoutError extends MongoError {
|
|
97
96
|
constructor(message, reason) {
|
|
98
|
-
|
|
97
|
+
if (reason && reason.error) {
|
|
98
|
+
super(reason.error.message || reason.error);
|
|
99
|
+
} else {
|
|
100
|
+
super(message);
|
|
101
|
+
}
|
|
102
|
+
|
|
99
103
|
this.name = 'MongoTimeoutError';
|
|
100
|
-
if (reason
|
|
104
|
+
if (reason) {
|
|
101
105
|
this.reason = reason;
|
|
102
106
|
}
|
|
103
107
|
}
|
|
104
108
|
}
|
|
105
109
|
|
|
110
|
+
/**
|
|
111
|
+
* An error signifying a client-side server selection error
|
|
112
|
+
*
|
|
113
|
+
* @param {Error|string|object} message The error message
|
|
114
|
+
* @param {string|object} [reason] The reason the timeout occured
|
|
115
|
+
* @property {string} message The error message
|
|
116
|
+
* @property {string} [reason] An optional reason context for the timeout, generally an error saved during flow of monitoring and selecting servers
|
|
117
|
+
* @extends MongoError
|
|
118
|
+
*/
|
|
119
|
+
class MongoServerSelectionError extends MongoTimeoutError {
|
|
120
|
+
constructor(message, reason) {
|
|
121
|
+
super(message, reason);
|
|
122
|
+
this.name = 'MongoServerSelectionError';
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
106
126
|
function makeWriteConcernResultObject(input) {
|
|
107
127
|
const output = Object.assign({}, input);
|
|
108
128
|
|
|
@@ -216,9 +236,8 @@ function isNodeShuttingDownError(err) {
|
|
|
216
236
|
* @ignore
|
|
217
237
|
* @see https://github.com/mongodb/specifications/blob/master/source/server-discovery-and-monitoring/server-discovery-and-monitoring.rst#not-master-and-node-is-recovering
|
|
218
238
|
* @param {MongoError|Error} error
|
|
219
|
-
* @param {Server} server
|
|
220
239
|
*/
|
|
221
|
-
function isSDAMUnrecoverableError(error
|
|
240
|
+
function isSDAMUnrecoverableError(error) {
|
|
222
241
|
// NOTE: null check is here for a strictly pre-CMAP world, a timeout or
|
|
223
242
|
// close event are considered unrecoverable
|
|
224
243
|
if (error instanceof MongoParseError || error == null) {
|
|
@@ -226,10 +245,6 @@ function isSDAMUnrecoverableError(error, server) {
|
|
|
226
245
|
}
|
|
227
246
|
|
|
228
247
|
if (isRecoveringError(error) || isNotMasterError(error)) {
|
|
229
|
-
if (maxWireVersion(server) >= 8 && !isNodeShuttingDownError(error)) {
|
|
230
|
-
return false;
|
|
231
|
-
}
|
|
232
|
-
|
|
233
248
|
return true;
|
|
234
249
|
}
|
|
235
250
|
|
|
@@ -241,8 +256,10 @@ module.exports = {
|
|
|
241
256
|
MongoNetworkError,
|
|
242
257
|
MongoParseError,
|
|
243
258
|
MongoTimeoutError,
|
|
259
|
+
MongoServerSelectionError,
|
|
244
260
|
MongoWriteConcernError,
|
|
245
261
|
mongoErrorContextSymbol,
|
|
246
262
|
isRetryableError,
|
|
247
|
-
isSDAMUnrecoverableError
|
|
263
|
+
isSDAMUnrecoverableError,
|
|
264
|
+
isNodeShuttingDownError
|
|
248
265
|
};
|
package/lib/core/index.js
CHANGED
|
@@ -20,6 +20,7 @@ module.exports = {
|
|
|
20
20
|
MongoNetworkError: require('./error').MongoNetworkError,
|
|
21
21
|
MongoParseError: require('./error').MongoParseError,
|
|
22
22
|
MongoTimeoutError: require('./error').MongoTimeoutError,
|
|
23
|
+
MongoServerSelectionError: require('./error').MongoServerSelectionError,
|
|
23
24
|
MongoWriteConcernError: require('./error').MongoWriteConcernError,
|
|
24
25
|
mongoErrorContextSymbol: require('./error').mongoErrorContextSymbol,
|
|
25
26
|
// Core
|