mongodb 4.5.0 → 4.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/admin.js +5 -5
- package/lib/admin.js.map +1 -1
- package/lib/bulk/common.js +7 -4
- package/lib/bulk/common.js.map +1 -1
- package/lib/change_stream.js +264 -258
- package/lib/change_stream.js.map +1 -1
- package/lib/cmap/auth/mongodb_aws.js +3 -0
- package/lib/cmap/auth/mongodb_aws.js.map +1 -1
- package/lib/cmap/auth/scram.js +12 -1
- package/lib/cmap/auth/scram.js.map +1 -1
- package/lib/cmap/commands.js +12 -11
- package/lib/cmap/commands.js.map +1 -1
- package/lib/cmap/connect.js +8 -1
- package/lib/cmap/connect.js.map +1 -1
- package/lib/cmap/connection.js +9 -60
- package/lib/cmap/connection.js.map +1 -1
- package/lib/cmap/connection_pool.js +70 -57
- package/lib/cmap/connection_pool.js.map +1 -1
- package/lib/cmap/connection_pool_events.js.map +1 -1
- package/lib/cmap/message_stream.js +39 -6
- package/lib/cmap/message_stream.js.map +1 -1
- package/lib/cmap/wire_protocol/compression.js +18 -2
- package/lib/cmap/wire_protocol/compression.js.map +1 -1
- package/lib/cmap/wire_protocol/constants.js +2 -2
- package/lib/cmap/wire_protocol/shared.js +3 -0
- package/lib/cmap/wire_protocol/shared.js.map +1 -1
- package/lib/collection.js +62 -31
- package/lib/collection.js.map +1 -1
- package/lib/connection_string.js +39 -11
- package/lib/connection_string.js.map +1 -1
- package/lib/constants.js +8 -1
- package/lib/constants.js.map +1 -1
- package/lib/cursor/abstract_cursor.js +16 -11
- package/lib/cursor/abstract_cursor.js.map +1 -1
- package/lib/cursor/aggregation_cursor.js +5 -5
- package/lib/cursor/aggregation_cursor.js.map +1 -1
- package/lib/cursor/find_cursor.js +12 -7
- package/lib/cursor/find_cursor.js.map +1 -1
- package/lib/db.js +21 -14
- package/lib/db.js.map +1 -1
- package/lib/deps.js +6 -1
- package/lib/deps.js.map +1 -1
- package/lib/encrypter.js +13 -3
- package/lib/encrypter.js.map +1 -1
- package/lib/error.js +37 -24
- package/lib/error.js.map +1 -1
- package/lib/gridfs/upload.js +1 -1
- package/lib/gridfs/upload.js.map +1 -1
- package/lib/index.js +4 -3
- package/lib/index.js.map +1 -1
- package/lib/mongo_client.js +18 -0
- package/lib/mongo_client.js.map +1 -1
- package/lib/operations/command.js +0 -3
- package/lib/operations/command.js.map +1 -1
- package/lib/operations/connect.js +1 -0
- package/lib/operations/connect.js.map +1 -1
- package/lib/operations/create_collection.js +56 -17
- package/lib/operations/create_collection.js.map +1 -1
- package/lib/operations/drop.js +48 -7
- package/lib/operations/drop.js.map +1 -1
- package/lib/operations/estimated_document_count.js +1 -20
- package/lib/operations/estimated_document_count.js.map +1 -1
- package/lib/operations/execute_operation.js +16 -9
- package/lib/operations/execute_operation.js.map +1 -1
- package/lib/operations/find.js +0 -51
- package/lib/operations/find.js.map +1 -1
- package/lib/operations/indexes.js +2 -2
- package/lib/operations/indexes.js.map +1 -1
- package/lib/operations/insert.js +5 -1
- package/lib/operations/insert.js.map +1 -1
- package/lib/operations/list_collections.js +2 -2
- package/lib/operations/list_collections.js.map +1 -1
- package/lib/sdam/monitor.js +10 -3
- package/lib/sdam/monitor.js.map +1 -1
- package/lib/sdam/server.js +29 -21
- package/lib/sdam/server.js.map +1 -1
- package/lib/sdam/srv_polling.js +2 -1
- package/lib/sdam/srv_polling.js.map +1 -1
- package/lib/sdam/topology.js +25 -24
- package/lib/sdam/topology.js.map +1 -1
- package/lib/sdam/topology_description.js +2 -1
- package/lib/sdam/topology_description.js.map +1 -1
- package/lib/sessions.js +29 -17
- package/lib/sessions.js.map +1 -1
- package/lib/utils.js +3 -2
- package/lib/utils.js.map +1 -1
- package/mongodb.d.ts +521 -88
- package/package.json +12 -11
- package/src/admin.ts +9 -5
- package/src/bulk/common.ts +7 -4
- package/src/change_stream.ts +761 -440
- package/src/cmap/auth/mongodb_aws.ts +5 -0
- package/src/cmap/auth/scram.ts +11 -1
- package/src/cmap/commands.ts +23 -22
- package/src/cmap/connect.ts +13 -2
- package/src/cmap/connection.ts +12 -74
- package/src/cmap/connection_pool.ts +90 -74
- package/src/cmap/connection_pool_events.ts +1 -1
- package/src/cmap/message_stream.ts +41 -7
- package/src/cmap/wire_protocol/compression.ts +27 -3
- package/src/cmap/wire_protocol/constants.ts +2 -2
- package/src/cmap/wire_protocol/shared.ts +5 -1
- package/src/collection.ts +74 -36
- package/src/connection_string.ts +49 -14
- package/src/constants.ts +6 -0
- package/src/cursor/abstract_cursor.ts +18 -15
- package/src/cursor/aggregation_cursor.ts +6 -6
- package/src/cursor/find_cursor.ts +16 -8
- package/src/db.ts +31 -20
- package/src/deps.ts +65 -7
- package/src/encrypter.ts +12 -3
- package/src/error.ts +41 -27
- package/src/gridfs/upload.ts +1 -1
- package/src/index.ts +23 -0
- package/src/mongo_client.ts +36 -11
- package/src/mongo_types.ts +1 -1
- package/src/operations/command.ts +0 -4
- package/src/operations/connect.ts +1 -0
- package/src/operations/create_collection.ts +95 -21
- package/src/operations/drop.ts +66 -6
- package/src/operations/estimated_document_count.ts +2 -29
- package/src/operations/execute_operation.ts +27 -27
- package/src/operations/find.ts +0 -80
- package/src/operations/indexes.ts +3 -9
- package/src/operations/insert.ts +8 -1
- package/src/operations/list_collections.ts +3 -3
- package/src/sdam/monitor.ts +10 -0
- package/src/sdam/server.ts +39 -36
- package/src/sdam/srv_polling.ts +1 -0
- package/src/sdam/topology.ts +36 -27
- package/src/sdam/topology_description.ts +2 -1
- package/src/sessions.ts +31 -20
- package/src/transactions.ts +1 -1
- package/src/utils.ts +3 -2
package/lib/change_stream.js
CHANGED
|
@@ -2,7 +2,9 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.ChangeStreamCursor = exports.ChangeStream = void 0;
|
|
4
4
|
const Denque = require("denque");
|
|
5
|
+
const timers_1 = require("timers");
|
|
5
6
|
const collection_1 = require("./collection");
|
|
7
|
+
const constants_1 = require("./constants");
|
|
6
8
|
const abstract_cursor_1 = require("./cursor/abstract_cursor");
|
|
7
9
|
const db_1 = require("./db");
|
|
8
10
|
const error_1 = require("./error");
|
|
@@ -23,21 +25,17 @@ const CHANGE_STREAM_OPTIONS = [
|
|
|
23
25
|
'resumeAfter',
|
|
24
26
|
'startAfter',
|
|
25
27
|
'startAtOperationTime',
|
|
26
|
-
'fullDocument'
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
'batchSize',
|
|
30
|
-
'maxAwaitTimeMS',
|
|
31
|
-
'collation',
|
|
32
|
-
'readPreference',
|
|
33
|
-
'comment',
|
|
34
|
-
...CHANGE_STREAM_OPTIONS
|
|
28
|
+
'fullDocument',
|
|
29
|
+
'fullDocumentBeforeChange',
|
|
30
|
+
'showExpandedEvents'
|
|
35
31
|
];
|
|
36
32
|
const CHANGE_DOMAIN_TYPES = {
|
|
37
33
|
COLLECTION: Symbol('Collection'),
|
|
38
34
|
DATABASE: Symbol('Database'),
|
|
39
35
|
CLUSTER: Symbol('Cluster')
|
|
40
36
|
};
|
|
37
|
+
const SELECTION_TIMEOUT = 30000;
|
|
38
|
+
const CHANGE_STREAM_EVENTS = [constants_1.RESUME_TOKEN_CHANGED, constants_1.END, constants_1.CLOSE];
|
|
41
39
|
const NO_RESUME_TOKEN_ERROR = 'A change stream document has been received that lacks a resume token (_id).';
|
|
42
40
|
const NO_CURSOR_ERROR = 'ChangeStream has no cursor';
|
|
43
41
|
const CHANGESTREAM_CLOSED_ERROR = 'ChangeStream is closed';
|
|
@@ -75,13 +73,13 @@ class ChangeStream extends mongo_types_1.TypedEventEmitter {
|
|
|
75
73
|
}
|
|
76
74
|
this[kResumeQueue] = new Denque();
|
|
77
75
|
// Create contained Change Stream cursor
|
|
78
|
-
this.cursor =
|
|
76
|
+
this.cursor = this._createChangeStreamCursor(options);
|
|
79
77
|
this[kClosed] = false;
|
|
80
78
|
this[kMode] = false;
|
|
81
79
|
// Listen for any `change` listeners being added to ChangeStream
|
|
82
80
|
this.on('newListener', eventName => {
|
|
83
81
|
if (eventName === 'change' && this.cursor && this.listenerCount('change') === 0) {
|
|
84
|
-
|
|
82
|
+
this._streamEvents(this.cursor);
|
|
85
83
|
}
|
|
86
84
|
});
|
|
87
85
|
this.on('removeListener', eventName => {
|
|
@@ -101,9 +99,9 @@ class ChangeStream extends mongo_types_1.TypedEventEmitter {
|
|
|
101
99
|
return (_a = this.cursor) === null || _a === void 0 ? void 0 : _a.resumeToken;
|
|
102
100
|
}
|
|
103
101
|
hasNext(callback) {
|
|
104
|
-
|
|
102
|
+
this._setIsIterator();
|
|
105
103
|
return (0, utils_1.maybePromise)(callback, cb => {
|
|
106
|
-
|
|
104
|
+
this._getCursor((err, cursor) => {
|
|
107
105
|
if (err || !cursor)
|
|
108
106
|
return cb(err); // failed to resume, raise an error
|
|
109
107
|
cursor.hasNext(cb);
|
|
@@ -111,18 +109,18 @@ class ChangeStream extends mongo_types_1.TypedEventEmitter {
|
|
|
111
109
|
});
|
|
112
110
|
}
|
|
113
111
|
next(callback) {
|
|
114
|
-
|
|
112
|
+
this._setIsIterator();
|
|
115
113
|
return (0, utils_1.maybePromise)(callback, cb => {
|
|
116
|
-
|
|
114
|
+
this._getCursor((err, cursor) => {
|
|
117
115
|
if (err || !cursor)
|
|
118
116
|
return cb(err); // failed to resume, raise an error
|
|
119
117
|
cursor.next((error, change) => {
|
|
120
118
|
if (error) {
|
|
121
119
|
this[kResumeQueue].push(() => this.next(cb));
|
|
122
|
-
|
|
120
|
+
this._processError(error, cb);
|
|
123
121
|
return;
|
|
124
122
|
}
|
|
125
|
-
|
|
123
|
+
this._processNewChange(change !== null && change !== void 0 ? change : null, cb);
|
|
126
124
|
});
|
|
127
125
|
});
|
|
128
126
|
});
|
|
@@ -141,7 +139,7 @@ class ChangeStream extends mongo_types_1.TypedEventEmitter {
|
|
|
141
139
|
}
|
|
142
140
|
const cursor = this.cursor;
|
|
143
141
|
return cursor.close(err => {
|
|
144
|
-
|
|
142
|
+
this._endStream();
|
|
145
143
|
this.cursor = undefined;
|
|
146
144
|
return cb(err);
|
|
147
145
|
});
|
|
@@ -158,45 +156,257 @@ class ChangeStream extends mongo_types_1.TypedEventEmitter {
|
|
|
158
156
|
return this.cursor.stream(options);
|
|
159
157
|
}
|
|
160
158
|
tryNext(callback) {
|
|
161
|
-
|
|
159
|
+
this._setIsIterator();
|
|
162
160
|
return (0, utils_1.maybePromise)(callback, cb => {
|
|
163
|
-
|
|
161
|
+
this._getCursor((err, cursor) => {
|
|
164
162
|
if (err || !cursor)
|
|
165
163
|
return cb(err); // failed to resume, raise an error
|
|
166
164
|
return cursor.tryNext(cb);
|
|
167
165
|
});
|
|
168
166
|
});
|
|
169
167
|
}
|
|
168
|
+
/** @internal */
|
|
169
|
+
_setIsEmitter() {
|
|
170
|
+
if (this[kMode] === 'iterator') {
|
|
171
|
+
// TODO(NODE-3485): Replace with MongoChangeStreamModeError
|
|
172
|
+
throw new error_1.MongoAPIError('ChangeStream cannot be used as an EventEmitter after being used as an iterator');
|
|
173
|
+
}
|
|
174
|
+
this[kMode] = 'emitter';
|
|
175
|
+
}
|
|
176
|
+
/** @internal */
|
|
177
|
+
_setIsIterator() {
|
|
178
|
+
if (this[kMode] === 'emitter') {
|
|
179
|
+
// TODO(NODE-3485): Replace with MongoChangeStreamModeError
|
|
180
|
+
throw new error_1.MongoAPIError('ChangeStream cannot be used as an iterator after being used as an EventEmitter');
|
|
181
|
+
}
|
|
182
|
+
this[kMode] = 'iterator';
|
|
183
|
+
}
|
|
184
|
+
/**
|
|
185
|
+
* Create a new change stream cursor based on self's configuration
|
|
186
|
+
* @internal
|
|
187
|
+
*/
|
|
188
|
+
_createChangeStreamCursor(options) {
|
|
189
|
+
const changeStreamStageOptions = (0, utils_1.filterOptions)(options, CHANGE_STREAM_OPTIONS);
|
|
190
|
+
if (this.type === CHANGE_DOMAIN_TYPES.CLUSTER) {
|
|
191
|
+
changeStreamStageOptions.allChangesForCluster = true;
|
|
192
|
+
}
|
|
193
|
+
const pipeline = [{ $changeStream: changeStreamStageOptions }, ...this.pipeline];
|
|
194
|
+
const client = this.type === CHANGE_DOMAIN_TYPES.CLUSTER
|
|
195
|
+
? this.parent
|
|
196
|
+
: this.type === CHANGE_DOMAIN_TYPES.DATABASE
|
|
197
|
+
? this.parent.s.client
|
|
198
|
+
: this.type === CHANGE_DOMAIN_TYPES.COLLECTION
|
|
199
|
+
? this.parent.s.db.s.client
|
|
200
|
+
: null;
|
|
201
|
+
if (client == null) {
|
|
202
|
+
// This should never happen because of the assertion in the constructor
|
|
203
|
+
throw new error_1.MongoRuntimeError(`Changestream type should only be one of cluster, database, collection. Found ${this.type.toString()}`);
|
|
204
|
+
}
|
|
205
|
+
const changeStreamCursor = new ChangeStreamCursor(client, this.namespace, pipeline, options);
|
|
206
|
+
for (const event of CHANGE_STREAM_EVENTS) {
|
|
207
|
+
changeStreamCursor.on(event, e => this.emit(event, e));
|
|
208
|
+
}
|
|
209
|
+
if (this.listenerCount(ChangeStream.CHANGE) > 0) {
|
|
210
|
+
this._streamEvents(changeStreamCursor);
|
|
211
|
+
}
|
|
212
|
+
return changeStreamCursor;
|
|
213
|
+
}
|
|
214
|
+
/**
|
|
215
|
+
* This method performs a basic server selection loop, satisfying the requirements of
|
|
216
|
+
* ChangeStream resumability until the new SDAM layer can be used.
|
|
217
|
+
* @internal
|
|
218
|
+
*/
|
|
219
|
+
_waitForTopologyConnected(topology, options, callback) {
|
|
220
|
+
(0, timers_1.setTimeout)(() => {
|
|
221
|
+
if (options && options.start == null) {
|
|
222
|
+
options.start = (0, utils_1.now)();
|
|
223
|
+
}
|
|
224
|
+
const start = options.start || (0, utils_1.now)();
|
|
225
|
+
const timeout = options.timeout || SELECTION_TIMEOUT;
|
|
226
|
+
if (topology.isConnected()) {
|
|
227
|
+
return callback();
|
|
228
|
+
}
|
|
229
|
+
if ((0, utils_1.calculateDurationInMs)(start) > timeout) {
|
|
230
|
+
// TODO(NODE-3497): Replace with MongoNetworkTimeoutError
|
|
231
|
+
return callback(new error_1.MongoRuntimeError('Timed out waiting for connection'));
|
|
232
|
+
}
|
|
233
|
+
this._waitForTopologyConnected(topology, options, callback);
|
|
234
|
+
}, 500); // this is an arbitrary wait time to allow SDAM to transition
|
|
235
|
+
}
|
|
236
|
+
/** @internal */
|
|
237
|
+
_closeWithError(error, callback) {
|
|
238
|
+
if (!callback) {
|
|
239
|
+
this.emit(ChangeStream.ERROR, error);
|
|
240
|
+
}
|
|
241
|
+
this.close(() => callback && callback(error));
|
|
242
|
+
}
|
|
243
|
+
/** @internal */
|
|
244
|
+
_streamEvents(cursor) {
|
|
245
|
+
var _a;
|
|
246
|
+
this._setIsEmitter();
|
|
247
|
+
const stream = (_a = this[kCursorStream]) !== null && _a !== void 0 ? _a : cursor.stream();
|
|
248
|
+
this[kCursorStream] = stream;
|
|
249
|
+
stream.on('data', change => this._processNewChange(change));
|
|
250
|
+
stream.on('error', error => this._processError(error));
|
|
251
|
+
}
|
|
252
|
+
/** @internal */
|
|
253
|
+
_endStream() {
|
|
254
|
+
const cursorStream = this[kCursorStream];
|
|
255
|
+
if (cursorStream) {
|
|
256
|
+
['data', 'close', 'end', 'error'].forEach(event => cursorStream.removeAllListeners(event));
|
|
257
|
+
cursorStream.destroy();
|
|
258
|
+
}
|
|
259
|
+
this[kCursorStream] = undefined;
|
|
260
|
+
}
|
|
261
|
+
/** @internal */
|
|
262
|
+
_processNewChange(change, callback) {
|
|
263
|
+
var _a;
|
|
264
|
+
if (this[kClosed]) {
|
|
265
|
+
// TODO(NODE-3485): Replace with MongoChangeStreamClosedError
|
|
266
|
+
if (callback)
|
|
267
|
+
callback(new error_1.MongoAPIError(CHANGESTREAM_CLOSED_ERROR));
|
|
268
|
+
return;
|
|
269
|
+
}
|
|
270
|
+
// a null change means the cursor has been notified, implicitly closing the change stream
|
|
271
|
+
if (change == null) {
|
|
272
|
+
// TODO(NODE-3485): Replace with MongoChangeStreamClosedError
|
|
273
|
+
return this._closeWithError(new error_1.MongoRuntimeError(CHANGESTREAM_CLOSED_ERROR), callback);
|
|
274
|
+
}
|
|
275
|
+
if (change && !change._id) {
|
|
276
|
+
return this._closeWithError(new error_1.MongoChangeStreamError(NO_RESUME_TOKEN_ERROR), callback);
|
|
277
|
+
}
|
|
278
|
+
// cache the resume token
|
|
279
|
+
(_a = this.cursor) === null || _a === void 0 ? void 0 : _a.cacheResumeToken(change._id);
|
|
280
|
+
// wipe the startAtOperationTime if there was one so that there won't be a conflict
|
|
281
|
+
// between resumeToken and startAtOperationTime if we need to reconnect the cursor
|
|
282
|
+
this.options.startAtOperationTime = undefined;
|
|
283
|
+
// Return the change
|
|
284
|
+
if (!callback)
|
|
285
|
+
return this.emit(ChangeStream.CHANGE, change);
|
|
286
|
+
return callback(undefined, change);
|
|
287
|
+
}
|
|
288
|
+
/** @internal */
|
|
289
|
+
_processError(error, callback) {
|
|
290
|
+
const cursor = this.cursor;
|
|
291
|
+
// If the change stream has been closed explicitly, do not process error.
|
|
292
|
+
if (this[kClosed]) {
|
|
293
|
+
// TODO(NODE-3485): Replace with MongoChangeStreamClosedError
|
|
294
|
+
if (callback)
|
|
295
|
+
callback(new error_1.MongoAPIError(CHANGESTREAM_CLOSED_ERROR));
|
|
296
|
+
return;
|
|
297
|
+
}
|
|
298
|
+
// if the resume succeeds, continue with the new cursor
|
|
299
|
+
const resumeWithCursor = (newCursor) => {
|
|
300
|
+
this.cursor = newCursor;
|
|
301
|
+
this._processResumeQueue();
|
|
302
|
+
};
|
|
303
|
+
// otherwise, raise an error and close the change stream
|
|
304
|
+
const unresumableError = (err) => {
|
|
305
|
+
if (!callback) {
|
|
306
|
+
this.emit(ChangeStream.ERROR, err);
|
|
307
|
+
}
|
|
308
|
+
this.close(() => this._processResumeQueue(err));
|
|
309
|
+
};
|
|
310
|
+
if (cursor && (0, error_1.isResumableError)(error, (0, utils_1.maxWireVersion)(cursor.server))) {
|
|
311
|
+
this.cursor = undefined;
|
|
312
|
+
// stop listening to all events from old cursor
|
|
313
|
+
this._endStream();
|
|
314
|
+
// close internal cursor, ignore errors
|
|
315
|
+
cursor.close();
|
|
316
|
+
const topology = (0, utils_1.getTopology)(this.parent);
|
|
317
|
+
this._waitForTopologyConnected(topology, { readPreference: cursor.readPreference }, err => {
|
|
318
|
+
// if the topology can't reconnect, close the stream
|
|
319
|
+
if (err)
|
|
320
|
+
return unresumableError(err);
|
|
321
|
+
// create a new cursor, preserving the old cursor's options
|
|
322
|
+
const newCursor = this._createChangeStreamCursor(cursor.resumeOptions);
|
|
323
|
+
// attempt to continue in emitter mode
|
|
324
|
+
if (!callback)
|
|
325
|
+
return resumeWithCursor(newCursor);
|
|
326
|
+
// attempt to continue in iterator mode
|
|
327
|
+
newCursor.hasNext(err => {
|
|
328
|
+
// if there's an error immediately after resuming, close the stream
|
|
329
|
+
if (err)
|
|
330
|
+
return unresumableError(err);
|
|
331
|
+
resumeWithCursor(newCursor);
|
|
332
|
+
});
|
|
333
|
+
});
|
|
334
|
+
return;
|
|
335
|
+
}
|
|
336
|
+
// if initial error wasn't resumable, raise an error and close the change stream
|
|
337
|
+
return this._closeWithError(error, callback);
|
|
338
|
+
}
|
|
339
|
+
/** @internal */
|
|
340
|
+
_getCursor(callback) {
|
|
341
|
+
if (this[kClosed]) {
|
|
342
|
+
// TODO(NODE-3485): Replace with MongoChangeStreamClosedError
|
|
343
|
+
callback(new error_1.MongoAPIError(CHANGESTREAM_CLOSED_ERROR));
|
|
344
|
+
return;
|
|
345
|
+
}
|
|
346
|
+
// if a cursor exists and it is open, return it
|
|
347
|
+
if (this.cursor) {
|
|
348
|
+
callback(undefined, this.cursor);
|
|
349
|
+
return;
|
|
350
|
+
}
|
|
351
|
+
// no cursor, queue callback until topology reconnects
|
|
352
|
+
this[kResumeQueue].push(callback);
|
|
353
|
+
}
|
|
354
|
+
/**
|
|
355
|
+
* Drain the resume queue when a new has become available
|
|
356
|
+
* @internal
|
|
357
|
+
*
|
|
358
|
+
* @param error - error getting a new cursor
|
|
359
|
+
*/
|
|
360
|
+
_processResumeQueue(error) {
|
|
361
|
+
var _a;
|
|
362
|
+
while (this[kResumeQueue].length) {
|
|
363
|
+
const request = this[kResumeQueue].pop();
|
|
364
|
+
if (!request)
|
|
365
|
+
break; // Should never occur but TS can't use the length check in the while condition
|
|
366
|
+
if (!error) {
|
|
367
|
+
if (this[kClosed]) {
|
|
368
|
+
// TODO(NODE-3485): Replace with MongoChangeStreamClosedError
|
|
369
|
+
request(new error_1.MongoAPIError(CHANGESTREAM_CLOSED_ERROR));
|
|
370
|
+
return;
|
|
371
|
+
}
|
|
372
|
+
if (!this.cursor) {
|
|
373
|
+
request(new error_1.MongoChangeStreamError(NO_CURSOR_ERROR));
|
|
374
|
+
return;
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
request(error, (_a = this.cursor) !== null && _a !== void 0 ? _a : undefined);
|
|
378
|
+
}
|
|
379
|
+
}
|
|
170
380
|
}
|
|
171
381
|
exports.ChangeStream = ChangeStream;
|
|
172
382
|
/** @event */
|
|
173
|
-
ChangeStream.RESPONSE =
|
|
383
|
+
ChangeStream.RESPONSE = constants_1.RESPONSE;
|
|
174
384
|
/** @event */
|
|
175
|
-
ChangeStream.MORE =
|
|
385
|
+
ChangeStream.MORE = constants_1.MORE;
|
|
176
386
|
/** @event */
|
|
177
|
-
ChangeStream.INIT =
|
|
387
|
+
ChangeStream.INIT = constants_1.INIT;
|
|
178
388
|
/** @event */
|
|
179
|
-
ChangeStream.CLOSE =
|
|
389
|
+
ChangeStream.CLOSE = constants_1.CLOSE;
|
|
180
390
|
/**
|
|
181
391
|
* Fired for each new matching change in the specified namespace. Attaching a `change`
|
|
182
392
|
* event listener to a Change Stream will switch the stream into flowing mode. Data will
|
|
183
393
|
* then be passed as soon as it is available.
|
|
184
394
|
* @event
|
|
185
395
|
*/
|
|
186
|
-
ChangeStream.CHANGE =
|
|
396
|
+
ChangeStream.CHANGE = constants_1.CHANGE;
|
|
187
397
|
/** @event */
|
|
188
|
-
ChangeStream.END =
|
|
398
|
+
ChangeStream.END = constants_1.END;
|
|
189
399
|
/** @event */
|
|
190
|
-
ChangeStream.ERROR =
|
|
400
|
+
ChangeStream.ERROR = constants_1.ERROR;
|
|
191
401
|
/**
|
|
192
402
|
* Emitted each time the change stream stores a new resume token.
|
|
193
403
|
* @event
|
|
194
404
|
*/
|
|
195
|
-
ChangeStream.RESUME_TOKEN_CHANGED =
|
|
405
|
+
ChangeStream.RESUME_TOKEN_CHANGED = constants_1.RESUME_TOKEN_CHANGED;
|
|
196
406
|
/** @internal */
|
|
197
407
|
class ChangeStreamCursor extends abstract_cursor_1.AbstractCursor {
|
|
198
|
-
constructor(
|
|
199
|
-
super(
|
|
408
|
+
constructor(client, namespace, pipeline = [], options = {}) {
|
|
409
|
+
super(client, namespace, options);
|
|
200
410
|
this.pipeline = pipeline;
|
|
201
411
|
this.options = options;
|
|
202
412
|
this._resumeToken = null;
|
|
@@ -216,20 +426,24 @@ class ChangeStreamCursor extends abstract_cursor_1.AbstractCursor {
|
|
|
216
426
|
return this._resumeToken;
|
|
217
427
|
}
|
|
218
428
|
get resumeOptions() {
|
|
219
|
-
const
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
429
|
+
const options = {
|
|
430
|
+
...this.options
|
|
431
|
+
};
|
|
432
|
+
for (const key of ['resumeAfter', 'startAfter', 'startAtOperationTime']) {
|
|
433
|
+
delete options[key];
|
|
434
|
+
}
|
|
435
|
+
if (this.resumeToken != null) {
|
|
436
|
+
if (this.options.startAfter && !this.hasReceived) {
|
|
437
|
+
options.startAfter = this.resumeToken;
|
|
227
438
|
}
|
|
228
|
-
else
|
|
229
|
-
|
|
439
|
+
else {
|
|
440
|
+
options.resumeAfter = this.resumeToken;
|
|
230
441
|
}
|
|
231
442
|
}
|
|
232
|
-
|
|
443
|
+
else if (this.startAtOperationTime != null && (0, utils_1.maxWireVersion)(this.server) >= 7) {
|
|
444
|
+
options.startAtOperationTime = this.startAtOperationTime;
|
|
445
|
+
}
|
|
446
|
+
return options;
|
|
233
447
|
}
|
|
234
448
|
cacheResumeToken(resumeToken) {
|
|
235
449
|
if (this.bufferedCount() === 0 && this.postBatchResumeToken) {
|
|
@@ -240,17 +454,18 @@ class ChangeStreamCursor extends abstract_cursor_1.AbstractCursor {
|
|
|
240
454
|
}
|
|
241
455
|
this.hasReceived = true;
|
|
242
456
|
}
|
|
243
|
-
_processBatch(
|
|
244
|
-
const cursor =
|
|
457
|
+
_processBatch(response) {
|
|
458
|
+
const cursor = response.cursor;
|
|
245
459
|
if (cursor.postBatchResumeToken) {
|
|
246
|
-
this.postBatchResumeToken = cursor.postBatchResumeToken;
|
|
247
|
-
|
|
460
|
+
this.postBatchResumeToken = response.cursor.postBatchResumeToken;
|
|
461
|
+
const batch = 'firstBatch' in response.cursor ? response.cursor.firstBatch : response.cursor.nextBatch;
|
|
462
|
+
if (batch.length === 0) {
|
|
248
463
|
this.resumeToken = cursor.postBatchResumeToken;
|
|
249
464
|
}
|
|
250
465
|
}
|
|
251
466
|
}
|
|
252
467
|
clone() {
|
|
253
|
-
return new ChangeStreamCursor(this.
|
|
468
|
+
return new ChangeStreamCursor(this.client, this.namespace, this.pipeline, {
|
|
254
469
|
...this.cursorOptions
|
|
255
470
|
});
|
|
256
471
|
}
|
|
@@ -260,7 +475,7 @@ class ChangeStreamCursor extends abstract_cursor_1.AbstractCursor {
|
|
|
260
475
|
...this.options,
|
|
261
476
|
session
|
|
262
477
|
});
|
|
263
|
-
(0, execute_operation_1.executeOperation)(session, aggregateOperation, (err, response) => {
|
|
478
|
+
(0, execute_operation_1.executeOperation)(session.client, aggregateOperation, (err, response) => {
|
|
264
479
|
if (err || response == null) {
|
|
265
480
|
return callback(err);
|
|
266
481
|
}
|
|
@@ -271,7 +486,7 @@ class ChangeStreamCursor extends abstract_cursor_1.AbstractCursor {
|
|
|
271
486
|
(0, utils_1.maxWireVersion)(server) >= 7) {
|
|
272
487
|
this.startAtOperationTime = response.operationTime;
|
|
273
488
|
}
|
|
274
|
-
this._processBatch(
|
|
489
|
+
this._processBatch(response);
|
|
275
490
|
this.emit(ChangeStream.INIT, response);
|
|
276
491
|
this.emit(ChangeStream.RESPONSE);
|
|
277
492
|
// TODO: NODE-2882
|
|
@@ -283,7 +498,7 @@ class ChangeStreamCursor extends abstract_cursor_1.AbstractCursor {
|
|
|
283
498
|
if (err) {
|
|
284
499
|
return callback(err);
|
|
285
500
|
}
|
|
286
|
-
this._processBatch(
|
|
501
|
+
this._processBatch(response);
|
|
287
502
|
this.emit(ChangeStream.MORE, response);
|
|
288
503
|
this.emit(ChangeStream.RESPONSE);
|
|
289
504
|
callback(err, response);
|
|
@@ -291,213 +506,4 @@ class ChangeStreamCursor extends abstract_cursor_1.AbstractCursor {
|
|
|
291
506
|
}
|
|
292
507
|
}
|
|
293
508
|
exports.ChangeStreamCursor = ChangeStreamCursor;
|
|
294
|
-
const CHANGE_STREAM_EVENTS = [
|
|
295
|
-
ChangeStream.RESUME_TOKEN_CHANGED,
|
|
296
|
-
ChangeStream.END,
|
|
297
|
-
ChangeStream.CLOSE
|
|
298
|
-
];
|
|
299
|
-
function setIsEmitter(changeStream) {
|
|
300
|
-
if (changeStream[kMode] === 'iterator') {
|
|
301
|
-
// TODO(NODE-3485): Replace with MongoChangeStreamModeError
|
|
302
|
-
throw new error_1.MongoAPIError('ChangeStream cannot be used as an EventEmitter after being used as an iterator');
|
|
303
|
-
}
|
|
304
|
-
changeStream[kMode] = 'emitter';
|
|
305
|
-
}
|
|
306
|
-
function setIsIterator(changeStream) {
|
|
307
|
-
if (changeStream[kMode] === 'emitter') {
|
|
308
|
-
// TODO(NODE-3485): Replace with MongoChangeStreamModeError
|
|
309
|
-
throw new error_1.MongoAPIError('ChangeStream cannot be used as an iterator after being used as an EventEmitter');
|
|
310
|
-
}
|
|
311
|
-
changeStream[kMode] = 'iterator';
|
|
312
|
-
}
|
|
313
|
-
/**
|
|
314
|
-
* Create a new change stream cursor based on self's configuration
|
|
315
|
-
* @internal
|
|
316
|
-
*/
|
|
317
|
-
function createChangeStreamCursor(changeStream, options) {
|
|
318
|
-
const changeStreamStageOptions = applyKnownOptions(options, CHANGE_STREAM_OPTIONS);
|
|
319
|
-
if (changeStream.type === CHANGE_DOMAIN_TYPES.CLUSTER) {
|
|
320
|
-
changeStreamStageOptions.allChangesForCluster = true;
|
|
321
|
-
}
|
|
322
|
-
const pipeline = [{ $changeStream: changeStreamStageOptions }].concat(changeStream.pipeline);
|
|
323
|
-
const cursorOptions = applyKnownOptions(options, CURSOR_OPTIONS);
|
|
324
|
-
const changeStreamCursor = new ChangeStreamCursor((0, utils_1.getTopology)(changeStream.parent), changeStream.namespace, pipeline, cursorOptions);
|
|
325
|
-
for (const event of CHANGE_STREAM_EVENTS) {
|
|
326
|
-
changeStreamCursor.on(event, e => changeStream.emit(event, e));
|
|
327
|
-
}
|
|
328
|
-
if (changeStream.listenerCount(ChangeStream.CHANGE) > 0) {
|
|
329
|
-
streamEvents(changeStream, changeStreamCursor);
|
|
330
|
-
}
|
|
331
|
-
return changeStreamCursor;
|
|
332
|
-
}
|
|
333
|
-
function applyKnownOptions(source, options) {
|
|
334
|
-
const result = {};
|
|
335
|
-
for (const option of options) {
|
|
336
|
-
if (option in source) {
|
|
337
|
-
result[option] = source[option];
|
|
338
|
-
}
|
|
339
|
-
}
|
|
340
|
-
return result;
|
|
341
|
-
}
|
|
342
|
-
// This method performs a basic server selection loop, satisfying the requirements of
|
|
343
|
-
// ChangeStream resumability until the new SDAM layer can be used.
|
|
344
|
-
const SELECTION_TIMEOUT = 30000;
|
|
345
|
-
function waitForTopologyConnected(topology, options, callback) {
|
|
346
|
-
setTimeout(() => {
|
|
347
|
-
if (options && options.start == null) {
|
|
348
|
-
options.start = (0, utils_1.now)();
|
|
349
|
-
}
|
|
350
|
-
const start = options.start || (0, utils_1.now)();
|
|
351
|
-
const timeout = options.timeout || SELECTION_TIMEOUT;
|
|
352
|
-
if (topology.isConnected()) {
|
|
353
|
-
return callback();
|
|
354
|
-
}
|
|
355
|
-
if ((0, utils_1.calculateDurationInMs)(start) > timeout) {
|
|
356
|
-
// TODO(NODE-3497): Replace with MongoNetworkTimeoutError
|
|
357
|
-
return callback(new error_1.MongoRuntimeError('Timed out waiting for connection'));
|
|
358
|
-
}
|
|
359
|
-
waitForTopologyConnected(topology, options, callback);
|
|
360
|
-
}, 500); // this is an arbitrary wait time to allow SDAM to transition
|
|
361
|
-
}
|
|
362
|
-
function closeWithError(changeStream, error, callback) {
|
|
363
|
-
if (!callback) {
|
|
364
|
-
changeStream.emit(ChangeStream.ERROR, error);
|
|
365
|
-
}
|
|
366
|
-
changeStream.close(() => callback && callback(error));
|
|
367
|
-
}
|
|
368
|
-
function streamEvents(changeStream, cursor) {
|
|
369
|
-
setIsEmitter(changeStream);
|
|
370
|
-
const stream = changeStream[kCursorStream] || cursor.stream();
|
|
371
|
-
changeStream[kCursorStream] = stream;
|
|
372
|
-
stream.on('data', change => processNewChange(changeStream, change));
|
|
373
|
-
stream.on('error', error => processError(changeStream, error));
|
|
374
|
-
}
|
|
375
|
-
function endStream(changeStream) {
|
|
376
|
-
const cursorStream = changeStream[kCursorStream];
|
|
377
|
-
if (cursorStream) {
|
|
378
|
-
['data', 'close', 'end', 'error'].forEach(event => cursorStream.removeAllListeners(event));
|
|
379
|
-
cursorStream.destroy();
|
|
380
|
-
}
|
|
381
|
-
changeStream[kCursorStream] = undefined;
|
|
382
|
-
}
|
|
383
|
-
function processNewChange(changeStream, change, callback) {
|
|
384
|
-
var _a;
|
|
385
|
-
if (changeStream[kClosed]) {
|
|
386
|
-
// TODO(NODE-3485): Replace with MongoChangeStreamClosedError
|
|
387
|
-
if (callback)
|
|
388
|
-
callback(new error_1.MongoAPIError(CHANGESTREAM_CLOSED_ERROR));
|
|
389
|
-
return;
|
|
390
|
-
}
|
|
391
|
-
// a null change means the cursor has been notified, implicitly closing the change stream
|
|
392
|
-
if (change == null) {
|
|
393
|
-
// TODO(NODE-3485): Replace with MongoChangeStreamClosedError
|
|
394
|
-
return closeWithError(changeStream, new error_1.MongoRuntimeError(CHANGESTREAM_CLOSED_ERROR), callback);
|
|
395
|
-
}
|
|
396
|
-
if (change && !change._id) {
|
|
397
|
-
return closeWithError(changeStream, new error_1.MongoChangeStreamError(NO_RESUME_TOKEN_ERROR), callback);
|
|
398
|
-
}
|
|
399
|
-
// cache the resume token
|
|
400
|
-
(_a = changeStream.cursor) === null || _a === void 0 ? void 0 : _a.cacheResumeToken(change._id);
|
|
401
|
-
// wipe the startAtOperationTime if there was one so that there won't be a conflict
|
|
402
|
-
// between resumeToken and startAtOperationTime if we need to reconnect the cursor
|
|
403
|
-
changeStream.options.startAtOperationTime = undefined;
|
|
404
|
-
// Return the change
|
|
405
|
-
if (!callback)
|
|
406
|
-
return changeStream.emit(ChangeStream.CHANGE, change);
|
|
407
|
-
return callback(undefined, change);
|
|
408
|
-
}
|
|
409
|
-
function processError(changeStream, error, callback) {
|
|
410
|
-
const cursor = changeStream.cursor;
|
|
411
|
-
// If the change stream has been closed explicitly, do not process error.
|
|
412
|
-
if (changeStream[kClosed]) {
|
|
413
|
-
// TODO(NODE-3485): Replace with MongoChangeStreamClosedError
|
|
414
|
-
if (callback)
|
|
415
|
-
callback(new error_1.MongoAPIError(CHANGESTREAM_CLOSED_ERROR));
|
|
416
|
-
return;
|
|
417
|
-
}
|
|
418
|
-
// if the resume succeeds, continue with the new cursor
|
|
419
|
-
function resumeWithCursor(newCursor) {
|
|
420
|
-
changeStream.cursor = newCursor;
|
|
421
|
-
processResumeQueue(changeStream);
|
|
422
|
-
}
|
|
423
|
-
// otherwise, raise an error and close the change stream
|
|
424
|
-
function unresumableError(err) {
|
|
425
|
-
if (!callback) {
|
|
426
|
-
changeStream.emit(ChangeStream.ERROR, err);
|
|
427
|
-
}
|
|
428
|
-
changeStream.close(() => processResumeQueue(changeStream, err));
|
|
429
|
-
}
|
|
430
|
-
if (cursor && (0, error_1.isResumableError)(error, (0, utils_1.maxWireVersion)(cursor.server))) {
|
|
431
|
-
changeStream.cursor = undefined;
|
|
432
|
-
// stop listening to all events from old cursor
|
|
433
|
-
endStream(changeStream);
|
|
434
|
-
// close internal cursor, ignore errors
|
|
435
|
-
cursor.close();
|
|
436
|
-
const topology = (0, utils_1.getTopology)(changeStream.parent);
|
|
437
|
-
waitForTopologyConnected(topology, { readPreference: cursor.readPreference }, err => {
|
|
438
|
-
// if the topology can't reconnect, close the stream
|
|
439
|
-
if (err)
|
|
440
|
-
return unresumableError(err);
|
|
441
|
-
// create a new cursor, preserving the old cursor's options
|
|
442
|
-
const newCursor = createChangeStreamCursor(changeStream, cursor.resumeOptions);
|
|
443
|
-
// attempt to continue in emitter mode
|
|
444
|
-
if (!callback)
|
|
445
|
-
return resumeWithCursor(newCursor);
|
|
446
|
-
// attempt to continue in iterator mode
|
|
447
|
-
newCursor.hasNext(err => {
|
|
448
|
-
// if there's an error immediately after resuming, close the stream
|
|
449
|
-
if (err)
|
|
450
|
-
return unresumableError(err);
|
|
451
|
-
resumeWithCursor(newCursor);
|
|
452
|
-
});
|
|
453
|
-
});
|
|
454
|
-
return;
|
|
455
|
-
}
|
|
456
|
-
// if initial error wasn't resumable, raise an error and close the change stream
|
|
457
|
-
return closeWithError(changeStream, error, callback);
|
|
458
|
-
}
|
|
459
|
-
/**
|
|
460
|
-
* Safely provides a cursor across resume attempts
|
|
461
|
-
*
|
|
462
|
-
* @param changeStream - the parent ChangeStream
|
|
463
|
-
*/
|
|
464
|
-
function getCursor(changeStream, callback) {
|
|
465
|
-
if (changeStream[kClosed]) {
|
|
466
|
-
// TODO(NODE-3485): Replace with MongoChangeStreamClosedError
|
|
467
|
-
callback(new error_1.MongoAPIError(CHANGESTREAM_CLOSED_ERROR));
|
|
468
|
-
return;
|
|
469
|
-
}
|
|
470
|
-
// if a cursor exists and it is open, return it
|
|
471
|
-
if (changeStream.cursor) {
|
|
472
|
-
callback(undefined, changeStream.cursor);
|
|
473
|
-
return;
|
|
474
|
-
}
|
|
475
|
-
// no cursor, queue callback until topology reconnects
|
|
476
|
-
changeStream[kResumeQueue].push(callback);
|
|
477
|
-
}
|
|
478
|
-
/**
|
|
479
|
-
* Drain the resume queue when a new has become available
|
|
480
|
-
*
|
|
481
|
-
* @param changeStream - the parent ChangeStream
|
|
482
|
-
* @param err - error getting a new cursor
|
|
483
|
-
*/
|
|
484
|
-
function processResumeQueue(changeStream, err) {
|
|
485
|
-
while (changeStream[kResumeQueue].length) {
|
|
486
|
-
const request = changeStream[kResumeQueue].pop();
|
|
487
|
-
if (!request)
|
|
488
|
-
break; // Should never occur but TS can't use the length check in the while condition
|
|
489
|
-
if (!err) {
|
|
490
|
-
if (changeStream[kClosed]) {
|
|
491
|
-
// TODO(NODE-3485): Replace with MongoChangeStreamClosedError
|
|
492
|
-
request(new error_1.MongoAPIError(CHANGESTREAM_CLOSED_ERROR));
|
|
493
|
-
return;
|
|
494
|
-
}
|
|
495
|
-
if (!changeStream.cursor) {
|
|
496
|
-
request(new error_1.MongoChangeStreamError(NO_CURSOR_ERROR));
|
|
497
|
-
return;
|
|
498
|
-
}
|
|
499
|
-
}
|
|
500
|
-
request(err, changeStream.cursor);
|
|
501
|
-
}
|
|
502
|
-
}
|
|
503
509
|
//# sourceMappingURL=change_stream.js.map
|