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.
Files changed (45) hide show
  1. package/HISTORY.md +72 -0
  2. package/index.js +1 -0
  3. package/lib/bulk/common.js +1 -1
  4. package/lib/cmap/connection.js +369 -0
  5. package/lib/cmap/connection_pool.js +593 -0
  6. package/lib/cmap/errors.js +35 -0
  7. package/lib/cmap/events.js +154 -0
  8. package/lib/{core/cmap → cmap}/message_stream.js +19 -17
  9. package/lib/cmap/stream_description.js +45 -0
  10. package/lib/core/auth/scram.js +1 -1
  11. package/lib/core/connection/apm.js +24 -7
  12. package/lib/core/connection/connect.js +55 -26
  13. package/lib/core/connection/pool.js +3 -16
  14. package/lib/core/error.js +27 -10
  15. package/lib/core/index.js +1 -0
  16. package/lib/core/sdam/events.js +124 -0
  17. package/lib/core/sdam/monitor.js +251 -0
  18. package/lib/core/sdam/server.js +148 -198
  19. package/lib/core/sdam/server_description.js +6 -4
  20. package/lib/core/sdam/server_selection.js +0 -91
  21. package/lib/core/sdam/topology.js +162 -136
  22. package/lib/core/sdam/topology_description.js +10 -8
  23. package/lib/core/sessions.js +16 -3
  24. package/lib/core/topologies/mongos.js +5 -13
  25. package/lib/core/topologies/replset.js +5 -10
  26. package/lib/core/topologies/server.js +9 -4
  27. package/lib/core/topologies/shared.js +0 -60
  28. package/lib/core/transactions.js +18 -7
  29. package/lib/core/uri_parser.js +3 -0
  30. package/lib/core/utils.js +84 -18
  31. package/lib/core/wireprotocol/command.js +2 -9
  32. package/lib/gridfs-stream/upload.js +1 -1
  33. package/lib/mongo_client.js +6 -6
  34. package/lib/operations/connect.js +118 -49
  35. package/lib/operations/execute_operation.js +31 -40
  36. package/lib/operations/find_one.js +13 -9
  37. package/lib/topologies/mongos.js +2 -9
  38. package/lib/topologies/native_topology.js +2 -6
  39. package/lib/topologies/replset.js +2 -9
  40. package/lib/topologies/server.js +2 -9
  41. package/lib/topologies/topology_base.js +4 -25
  42. package/lib/utils.js +11 -2
  43. package/package.json +3 -2
  44. package/lib/core/cmap/connection.js +0 -220
  45. package/lib/core/sdam/monitoring.js +0 -241
package/HISTORY.md CHANGED
@@ -2,6 +2,78 @@
2
2
 
3
3
  All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
4
4
 
5
+ <a name="3.5.2"></a>
6
+ ## [3.5.2](https://github.com/mongodb/node-mongodb-native/compare/v3.5.1...v3.5.2) (2020-01-20)
7
+
8
+
9
+ ### Bug Fixes
10
+
11
+ * properly handle err messages in MongoDB 2.6 servers ([0f4ab38](https://github.com/mongodb/node-mongodb-native/commit/0f4ab38))
12
+ * **topology:** always emit SDAM unrecoverable errors ([57f158f](https://github.com/mongodb/node-mongodb-native/commit/57f158f))
13
+
14
+
15
+
16
+ <a name="3.5.1"></a>
17
+ ## [3.5.1](https://github.com/mongodb/node-mongodb-native/compare/v3.5.0...v3.5.1) (2020-01-17)
18
+
19
+
20
+ ### Bug Fixes
21
+
22
+ * **cmap:** accept all node TLS options as pool options ([5995d1d](https://github.com/mongodb/node-mongodb-native/commit/5995d1d))
23
+ * **cmap:** error wait queue members on failed connection creation ([d13b153](https://github.com/mongodb/node-mongodb-native/commit/d13b153))
24
+ * **connect:** listen to `secureConnect` for tls connections ([f8bdb8d](https://github.com/mongodb/node-mongodb-native/commit/f8bdb8d))
25
+ * **transactions:** use options helper to resolve read preference ([9698a76](https://github.com/mongodb/node-mongodb-native/commit/9698a76))
26
+ * **uri_parser:** TLS uri variants imply `ssl=true` ([c8d182e](https://github.com/mongodb/node-mongodb-native/commit/c8d182e))
27
+
28
+
29
+
30
+ <a name="3.5.0"></a>
31
+ # [3.5.0](https://github.com/mongodb/node-mongodb-native/compare/v3.4.1...v3.5.0) (2020-01-14)
32
+
33
+
34
+ ### Bug Fixes
35
+
36
+ * copy `ssl` option to pool connection options ([563ced6](https://github.com/mongodb/node-mongodb-native/commit/563ced6))
37
+ * destroy connections marked as closed on checkIn / checkOut ([2bd17a6](https://github.com/mongodb/node-mongodb-native/commit/2bd17a6))
38
+ * ensure sync errors are thrown, and don't callback twice ([cca5b49](https://github.com/mongodb/node-mongodb-native/commit/cca5b49))
39
+ * ignore connection errors during pool destruction ([b8805dc](https://github.com/mongodb/node-mongodb-native/commit/b8805dc))
40
+ * not all message payloads are arrays of Buffer ([e4df5f4](https://github.com/mongodb/node-mongodb-native/commit/e4df5f4))
41
+ * recover on network error during initial connect ([a13dc68](https://github.com/mongodb/node-mongodb-native/commit/a13dc68))
42
+ * remove servers with me mismatch in `updateRsFromPrimary` ([95a772e](https://github.com/mongodb/node-mongodb-native/commit/95a772e))
43
+ * report the correct platform in client metadata ([35d0274](https://github.com/mongodb/node-mongodb-native/commit/35d0274))
44
+ * reschedule monitoring before emitting heartbeat events ([7fcbeb5](https://github.com/mongodb/node-mongodb-native/commit/7fcbeb5))
45
+ * socket timeout for handshake should be `connectTimeoutMS` ([c83af9a](https://github.com/mongodb/node-mongodb-native/commit/c83af9a))
46
+ * timed out streams should be destroyed on `timeout` event ([5319ff9](https://github.com/mongodb/node-mongodb-native/commit/5319ff9))
47
+ * use remote address for stream identifier ([f13c20b](https://github.com/mongodb/node-mongodb-native/commit/f13c20b))
48
+ * used weighted RTT calculation for server selection ([d446be5](https://github.com/mongodb/node-mongodb-native/commit/d446be5))
49
+ * **execute-operation:** don't swallow synchronous errors ([0a2d4e9](https://github.com/mongodb/node-mongodb-native/commit/0a2d4e9))
50
+ * **gridfs:** make a copy of chunk before writing to server ([b4ec5b8](https://github.com/mongodb/node-mongodb-native/commit/b4ec5b8))
51
+
52
+
53
+ ### Features
54
+
55
+ * add a `withConnection` helper to the connection pool ([d59dced](https://github.com/mongodb/node-mongodb-native/commit/d59dced))
56
+ * include `connectionId` for APM with new CMAP connection pool ([9bd360c](https://github.com/mongodb/node-mongodb-native/commit/9bd360c))
57
+ * integrate CMAP connection pool into unified topology ([9dd3939](https://github.com/mongodb/node-mongodb-native/commit/9dd3939))
58
+ * introduce `MongoServerSelectionError` ([0cf7ec9](https://github.com/mongodb/node-mongodb-native/commit/0cf7ec9))
59
+ * introduce a class for tracking stream specific attributes ([f6bf82c](https://github.com/mongodb/node-mongodb-native/commit/f6bf82c))
60
+ * introduce a new `Monitor` type for server monitoring ([2bfe2a1](https://github.com/mongodb/node-mongodb-native/commit/2bfe2a1))
61
+ * relay all CMAP events to MongoClient ([1aea4de](https://github.com/mongodb/node-mongodb-native/commit/1aea4de))
62
+ * support socket timeouts on a per-connection level ([93e8ad0](https://github.com/mongodb/node-mongodb-native/commit/93e8ad0))
63
+
64
+
65
+
66
+ <a name="3.4.1"></a>
67
+ ## [3.4.1](https://github.com/mongodb/node-mongodb-native/compare/v3.4.0...v3.4.1) (2019-12-19)
68
+
69
+
70
+ ### Bug Fixes
71
+
72
+ * **bulk:** use original indexes as map for current op index ([20800ac](https://github.com/mongodb/node-mongodb-native/commit/20800ac))
73
+ * always check for network errors during SCRAM conversation ([e46a70e](https://github.com/mongodb/node-mongodb-native/commit/e46a70e))
74
+
75
+
76
+
5
77
  <a name="3.4.0"></a>
6
78
  # [3.4.0](https://github.com/mongodb/node-mongodb-native/compare/v3.3.5...v3.4.0) (2019-12-10)
7
79
 
package/index.js CHANGED
@@ -11,6 +11,7 @@ const connect = require('./lib/mongo_client').connect;
11
11
  connect.MongoError = core.MongoError;
12
12
  connect.MongoNetworkError = core.MongoNetworkError;
13
13
  connect.MongoTimeoutError = core.MongoTimeoutError;
14
+ connect.MongoServerSelectionError = core.MongoServerSelectionError;
14
15
  connect.MongoParseError = core.MongoParseError;
15
16
  connect.MongoWriteConcernError = core.MongoWriteConcernError;
16
17
  connect.MongoBulkWriteError = require('./lib/bulk/common').BulkWriteError;
@@ -475,7 +475,7 @@ function mergeBatchResults(batch, bulkResult, err, result) {
475
475
  if (Array.isArray(result.writeErrors)) {
476
476
  for (let i = 0; i < result.writeErrors.length; i++) {
477
477
  const writeError = {
478
- index: batch.originalIndexes[i],
478
+ index: batch.originalIndexes[result.writeErrors[i].index],
479
479
  code: result.writeErrors[i].code,
480
480
  errmsg: result.writeErrors[i].errmsg,
481
481
  op: batch.operations[result.writeErrors[i].index]
@@ -0,0 +1,369 @@
1
+ 'use strict';
2
+
3
+ const EventEmitter = require('events');
4
+ const MessageStream = require('./message_stream');
5
+ const MongoError = require('../core/error').MongoError;
6
+ const MongoNetworkError = require('../core/error').MongoNetworkError;
7
+ const MongoWriteConcernError = require('../core/error').MongoWriteConcernError;
8
+ const CommandResult = require('../core/connection/command_result');
9
+ const StreamDescription = require('./stream_description').StreamDescription;
10
+ const wp = require('../core/wireprotocol');
11
+ const apm = require('../core/connection/apm');
12
+ const updateSessionFromResponse = require('../core/sessions').updateSessionFromResponse;
13
+ const uuidV4 = require('../core/utils').uuidV4;
14
+
15
+ const kStream = Symbol('stream');
16
+ const kQueue = Symbol('queue');
17
+ const kMessageStream = Symbol('messageStream');
18
+ const kGeneration = Symbol('generation');
19
+ const kLastUseTime = Symbol('lastUseTime');
20
+ const kClusterTime = Symbol('clusterTime');
21
+ const kDescription = Symbol('description');
22
+ const kIsMaster = Symbol('ismaster');
23
+ const kAutoEncrypter = Symbol('autoEncrypter');
24
+
25
+ class Connection extends EventEmitter {
26
+ constructor(stream, options) {
27
+ super(options);
28
+
29
+ this.id = options.id;
30
+ this.address = streamIdentifier(stream);
31
+ this.bson = options.bson;
32
+ this.socketTimeout = typeof options.socketTimeout === 'number' ? options.socketTimeout : 360000;
33
+ this.monitorCommands =
34
+ typeof options.monitorCommands === 'boolean' ? options.monitorCommands : false;
35
+ this.closed = false;
36
+ this.destroyed = false;
37
+
38
+ this[kDescription] = new StreamDescription(this.address, options);
39
+ this[kGeneration] = options.generation;
40
+ this[kLastUseTime] = Date.now();
41
+
42
+ // retain a reference to an `AutoEncrypter` if present
43
+ if (options.autoEncrypter) {
44
+ this[kAutoEncrypter] = options.autoEncrypter;
45
+ }
46
+
47
+ // setup parser stream and message handling
48
+ this[kQueue] = new Map();
49
+ this[kMessageStream] = new MessageStream(options);
50
+ this[kMessageStream].on('message', messageHandler(this));
51
+ this[kStream] = stream;
52
+ stream.on('error', () => {
53
+ /* ignore errors, listen to `close` instead */
54
+ });
55
+
56
+ stream.on('close', () => {
57
+ if (this.closed) {
58
+ return;
59
+ }
60
+
61
+ this.closed = true;
62
+ this[kQueue].forEach(op =>
63
+ op.cb(new MongoNetworkError(`connection ${this.id} to ${this.address} closed`))
64
+ );
65
+ this[kQueue].clear();
66
+
67
+ this.emit('close');
68
+ });
69
+
70
+ stream.on('timeout', () => {
71
+ if (this.closed) {
72
+ return;
73
+ }
74
+
75
+ stream.destroy();
76
+ this.closed = true;
77
+ this[kQueue].forEach(op =>
78
+ op.cb(new MongoNetworkError(`connection ${this.id} to ${this.address} timed out`))
79
+ );
80
+ this[kQueue].clear();
81
+
82
+ this.emit('close');
83
+ });
84
+
85
+ // hook the message stream up to the passed in stream
86
+ stream.pipe(this[kMessageStream]);
87
+ this[kMessageStream].pipe(stream);
88
+ }
89
+
90
+ get description() {
91
+ return this[kDescription];
92
+ }
93
+
94
+ get ismaster() {
95
+ return this[kIsMaster];
96
+ }
97
+
98
+ // the `connect` method stores the result of the handshake ismaster on the connection
99
+ set ismaster(response) {
100
+ this[kDescription].receiveResponse(response);
101
+
102
+ // TODO: remove this, and only use the `StreamDescription` in the future
103
+ this[kIsMaster] = response;
104
+ }
105
+
106
+ get generation() {
107
+ return this[kGeneration] || 0;
108
+ }
109
+
110
+ get idleTime() {
111
+ return Date.now() - this[kLastUseTime];
112
+ }
113
+
114
+ get clusterTime() {
115
+ return this[kClusterTime];
116
+ }
117
+
118
+ get stream() {
119
+ return this[kStream];
120
+ }
121
+
122
+ markAvailable() {
123
+ this[kLastUseTime] = Date.now();
124
+ }
125
+
126
+ destroy(options, callback) {
127
+ if (typeof options === 'function') {
128
+ callback = options;
129
+ options = {};
130
+ }
131
+
132
+ options = Object.assign({ force: false }, options);
133
+ if (this[kStream] == null || this.destroyed) {
134
+ this.destroyed = true;
135
+ if (typeof callback === 'function') {
136
+ callback();
137
+ }
138
+
139
+ return;
140
+ }
141
+
142
+ if (options.force) {
143
+ this[kStream].destroy();
144
+ this.destroyed = true;
145
+ if (typeof callback === 'function') {
146
+ callback();
147
+ }
148
+
149
+ return;
150
+ }
151
+
152
+ this[kStream].end(err => {
153
+ this.destroyed = true;
154
+ if (typeof callback === 'function') {
155
+ callback(err);
156
+ }
157
+ });
158
+ }
159
+
160
+ // Wire protocol methods
161
+ command(ns, cmd, options, callback) {
162
+ wp.command(makeServerTrampoline(this), ns, cmd, options, callback);
163
+ }
164
+
165
+ query(ns, cmd, cursorState, options, callback) {
166
+ wp.query(makeServerTrampoline(this), ns, cmd, cursorState, options, callback);
167
+ }
168
+
169
+ getMore(ns, cursorState, batchSize, options, callback) {
170
+ wp.getMore(makeServerTrampoline(this), ns, cursorState, batchSize, options, callback);
171
+ }
172
+
173
+ killCursors(ns, cursorState, callback) {
174
+ wp.killCursors(makeServerTrampoline(this), ns, cursorState, callback);
175
+ }
176
+
177
+ insert(ns, ops, options, callback) {
178
+ wp.insert(makeServerTrampoline(this), ns, ops, options, callback);
179
+ }
180
+
181
+ update(ns, ops, options, callback) {
182
+ wp.update(makeServerTrampoline(this), ns, ops, options, callback);
183
+ }
184
+
185
+ remove(ns, ops, options, callback) {
186
+ wp.remove(makeServerTrampoline(this), ns, ops, options, callback);
187
+ }
188
+ }
189
+
190
+ /// This lets us emulate a legacy `Server` instance so we can work with the existing wire
191
+ /// protocol methods. Eventually, the operation executor will return a `Connection` to execute
192
+ /// against.
193
+ function makeServerTrampoline(connection) {
194
+ const server = {
195
+ description: connection.description,
196
+ clusterTime: connection[kClusterTime],
197
+ s: {
198
+ bson: connection.bson,
199
+ pool: { write: write.bind(connection), isConnected: () => true }
200
+ }
201
+ };
202
+
203
+ if (connection[kAutoEncrypter]) {
204
+ server.autoEncrypter = connection[kAutoEncrypter];
205
+ }
206
+
207
+ return server;
208
+ }
209
+
210
+ function messageHandler(conn) {
211
+ return function messageHandler(message) {
212
+ // always emit the message, in case we are streaming
213
+ conn.emit('message', message);
214
+ if (!conn[kQueue].has(message.responseTo)) {
215
+ return;
216
+ }
217
+
218
+ const operationDescription = conn[kQueue].get(message.responseTo);
219
+ conn[kQueue].delete(message.responseTo);
220
+
221
+ const callback = operationDescription.cb;
222
+ if (operationDescription.socketTimeoutOverride) {
223
+ conn[kStream].setTimeout(conn.socketTimeout);
224
+ }
225
+
226
+ try {
227
+ // Pass in the entire description because it has BSON parsing options
228
+ message.parse(operationDescription);
229
+ } catch (err) {
230
+ callback(new MongoError(err));
231
+ return;
232
+ }
233
+
234
+ if (message.documents[0]) {
235
+ const document = message.documents[0];
236
+ const session = operationDescription.session;
237
+ if (session) {
238
+ updateSessionFromResponse(session, document);
239
+ }
240
+
241
+ if (document.$clusterTime) {
242
+ conn[kClusterTime] = document.$clusterTime;
243
+ conn.emit('clusterTimeReceived', document.$clusterTime);
244
+ }
245
+
246
+ if (operationDescription.command) {
247
+ if (document.writeConcernError) {
248
+ callback(new MongoWriteConcernError(document.writeConcernError, document));
249
+ return;
250
+ }
251
+
252
+ if (document.ok === 0 || document.$err || document.errmsg || document.code) {
253
+ callback(new MongoError(document));
254
+ return;
255
+ }
256
+ }
257
+ }
258
+
259
+ // NODE-2382: reenable in our glorious non-leaky abstraction future
260
+ // callback(null, operationDescription.fullResult ? message : message.documents[0]);
261
+
262
+ callback(
263
+ undefined,
264
+ new CommandResult(
265
+ operationDescription.fullResult ? message : message.documents[0],
266
+ conn,
267
+ message
268
+ )
269
+ );
270
+ };
271
+ }
272
+
273
+ function streamIdentifier(stream) {
274
+ if (typeof stream.address === 'function') {
275
+ return `${stream.remoteAddress}:${stream.remotePort}`;
276
+ }
277
+
278
+ return uuidV4().toString('hex');
279
+ }
280
+
281
+ // Not meant to be called directly, the wire protocol methods call this assuming it is a `Pool` instance
282
+ function write(command, options, callback) {
283
+ if (typeof options === 'function') {
284
+ callback = options;
285
+ }
286
+
287
+ options = options || {};
288
+ const operationDescription = {
289
+ requestId: command.requestId,
290
+ cb: callback,
291
+ session: options.session,
292
+ fullResult: typeof options.fullResult === 'boolean' ? options.fullResult : false,
293
+ noResponse: typeof options.noResponse === 'boolean' ? options.noResponse : false,
294
+ documentsReturnedIn: options.documentsReturnedIn,
295
+ command: !!options.command,
296
+
297
+ // for BSON parsing
298
+ promoteLongs: typeof options.promoteLongs === 'boolean' ? options.promoteLongs : true,
299
+ promoteValues: typeof options.promoteValues === 'boolean' ? options.promoteValues : true,
300
+ promoteBuffers: typeof options.promoteBuffers === 'boolean' ? options.promoteBuffers : false,
301
+ raw: typeof options.raw === 'boolean' ? options.raw : false
302
+ };
303
+
304
+ if (this[kDescription] && this[kDescription].compressor) {
305
+ operationDescription.agreedCompressor = this[kDescription].compressor;
306
+
307
+ if (this[kDescription].zlibCompressionLevel) {
308
+ operationDescription.zlibCompressionLevel = this[kDescription].zlibCompressionLevel;
309
+ }
310
+ }
311
+
312
+ if (typeof options.socketTimeout === 'number') {
313
+ operationDescription.socketTimeoutOverride = true;
314
+ this[kStream].setTimeout(options.socketTimeout);
315
+ }
316
+
317
+ // if command monitoring is enabled we need to modify the callback here
318
+ if (this.monitorCommands) {
319
+ this.emit('commandStarted', new apm.CommandStartedEvent(this, command));
320
+
321
+ operationDescription.started = process.hrtime();
322
+ operationDescription.cb = (err, reply) => {
323
+ if (err) {
324
+ this.emit(
325
+ 'commandFailed',
326
+ new apm.CommandFailedEvent(this, command, err, operationDescription.started)
327
+ );
328
+ } else {
329
+ if (reply && reply.result && (reply.result.ok === 0 || reply.result.$err)) {
330
+ this.emit(
331
+ 'commandFailed',
332
+ new apm.CommandFailedEvent(this, command, reply.result, operationDescription.started)
333
+ );
334
+ } else {
335
+ this.emit(
336
+ 'commandSucceeded',
337
+ new apm.CommandSucceededEvent(this, command, reply, operationDescription.started)
338
+ );
339
+ }
340
+ }
341
+
342
+ if (typeof callback === 'function') {
343
+ callback(err, reply);
344
+ }
345
+ };
346
+ }
347
+
348
+ if (!operationDescription.noResponse) {
349
+ this[kQueue].set(operationDescription.requestId, operationDescription);
350
+ }
351
+
352
+ try {
353
+ this[kMessageStream].writeCommand(command, operationDescription);
354
+ } catch (e) {
355
+ if (!operationDescription.noResponse) {
356
+ this[kQueue].delete(operationDescription.requestId);
357
+ operationDescription.cb(e);
358
+ return;
359
+ }
360
+ }
361
+
362
+ if (operationDescription.noResponse) {
363
+ operationDescription.cb();
364
+ }
365
+ }
366
+
367
+ module.exports = {
368
+ Connection
369
+ };