mongodb 4.7.0 → 4.9.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/bson.js +4 -2
- package/lib/bson.js.map +1 -1
- package/lib/bulk/common.js +1 -0
- package/lib/bulk/common.js.map +1 -1
- package/lib/change_stream.js +136 -271
- package/lib/change_stream.js.map +1 -1
- package/lib/cmap/command_monitoring_events.js +2 -32
- package/lib/cmap/command_monitoring_events.js.map +1 -1
- package/lib/cmap/commands.js +1 -153
- package/lib/cmap/commands.js.map +1 -1
- package/lib/cmap/connect.js +3 -6
- package/lib/cmap/connect.js.map +1 -1
- package/lib/cmap/connection.js +21 -84
- package/lib/cmap/connection.js.map +1 -1
- package/lib/cmap/connection_pool.js +196 -170
- package/lib/cmap/connection_pool.js.map +1 -1
- package/lib/cmap/message_stream.js.map +1 -1
- package/lib/cmap/wire_protocol/compression.js +2 -6
- package/lib/cmap/wire_protocol/compression.js.map +1 -1
- package/lib/cmap/wire_protocol/constants.js +1 -3
- package/lib/cmap/wire_protocol/constants.js.map +1 -1
- package/lib/collection.js +24 -9
- package/lib/collection.js.map +1 -1
- package/lib/connection_string.js.map +1 -1
- package/lib/cursor/abstract_cursor.js +62 -75
- package/lib/cursor/abstract_cursor.js.map +1 -1
- package/lib/cursor/change_stream_cursor.js +115 -0
- package/lib/cursor/change_stream_cursor.js.map +1 -0
- package/lib/cursor/list_collections_cursor.js +37 -0
- package/lib/cursor/list_collections_cursor.js.map +1 -0
- package/lib/cursor/list_indexes_cursor.js +36 -0
- package/lib/cursor/list_indexes_cursor.js.map +1 -0
- package/lib/db.js +2 -2
- package/lib/db.js.map +1 -1
- package/lib/deps.js.map +1 -1
- package/lib/encrypter.js +3 -13
- package/lib/encrypter.js.map +1 -1
- package/lib/index.js +28 -21
- package/lib/index.js.map +1 -1
- package/lib/mongo_client.js +62 -21
- package/lib/mongo_client.js.map +1 -1
- package/lib/mongo_types.js.map +1 -1
- package/lib/operations/common_functions.js.map +1 -1
- package/lib/operations/create_collection.js.map +1 -1
- package/lib/operations/distinct.js +5 -5
- package/lib/operations/distinct.js.map +1 -1
- package/lib/operations/estimated_document_count.js +5 -0
- package/lib/operations/estimated_document_count.js.map +1 -1
- package/lib/operations/execute_operation.js +17 -8
- package/lib/operations/execute_operation.js.map +1 -1
- package/lib/operations/find.js +3 -0
- package/lib/operations/find.js.map +1 -1
- package/lib/operations/get_more.js +32 -7
- package/lib/operations/get_more.js.map +1 -1
- package/lib/operations/indexes.js +39 -65
- package/lib/operations/indexes.js.map +1 -1
- package/lib/operations/kill_cursors.js +32 -0
- package/lib/operations/kill_cursors.js.map +1 -0
- package/lib/operations/list_collections.js +1 -33
- package/lib/operations/list_collections.js.map +1 -1
- package/lib/operations/operation.js +4 -1
- package/lib/operations/operation.js.map +1 -1
- package/lib/read_preference.js.map +1 -1
- package/lib/sdam/common.js +2 -1
- package/lib/sdam/common.js.map +1 -1
- package/lib/sdam/monitor.js +2 -1
- package/lib/sdam/monitor.js.map +1 -1
- package/lib/sdam/server.js +1 -52
- package/lib/sdam/server.js.map +1 -1
- package/lib/sdam/server_description.js +51 -58
- package/lib/sdam/server_description.js.map +1 -1
- package/lib/sdam/srv_polling.js +2 -2
- package/lib/sdam/srv_polling.js.map +1 -1
- package/lib/sdam/topology.js +28 -67
- package/lib/sdam/topology.js.map +1 -1
- package/lib/sdam/topology_description.js +24 -42
- package/lib/sdam/topology_description.js.map +1 -1
- package/lib/sessions.js +29 -31
- package/lib/sessions.js.map +1 -1
- package/lib/utils.js +65 -70
- package/lib/utils.js.map +1 -1
- package/mongodb.d.ts +136 -73
- package/package.json +23 -22
- package/src/bson.ts +4 -0
- package/src/bulk/common.ts +1 -0
- package/src/change_stream.ts +147 -373
- package/src/cmap/command_monitoring_events.ts +3 -37
- package/src/cmap/commands.ts +2 -190
- package/src/cmap/connect.ts +20 -25
- package/src/cmap/connection.ts +27 -139
- package/src/cmap/connection_pool.ts +208 -169
- package/src/cmap/message_stream.ts +2 -3
- package/src/cmap/wire_protocol/compression.ts +8 -6
- package/src/cmap/wire_protocol/constants.ts +0 -2
- package/src/collection.ts +27 -13
- package/src/connection_string.ts +1 -1
- package/src/cursor/abstract_cursor.ts +98 -87
- package/src/cursor/change_stream_cursor.ts +194 -0
- package/src/cursor/list_collections_cursor.ts +52 -0
- package/src/cursor/list_indexes_cursor.ts +41 -0
- package/src/db.ts +2 -5
- package/src/deps.ts +13 -22
- package/src/encrypter.ts +4 -14
- package/src/index.ts +13 -9
- package/src/mongo_client.ts +102 -33
- package/src/mongo_types.ts +81 -57
- package/src/operations/common_functions.ts +1 -1
- package/src/operations/create_collection.ts +1 -2
- package/src/operations/distinct.ts +7 -9
- package/src/operations/estimated_document_count.ts +6 -0
- package/src/operations/execute_operation.ts +17 -8
- package/src/operations/find.ts +9 -0
- package/src/operations/get_more.ts +56 -13
- package/src/operations/indexes.ts +52 -89
- package/src/operations/kill_cursors.ts +53 -0
- package/src/operations/list_collections.ts +0 -43
- package/src/operations/operation.ts +5 -1
- package/src/read_preference.ts +5 -9
- package/src/sdam/common.ts +2 -0
- package/src/sdam/monitor.ts +2 -1
- package/src/sdam/server.ts +4 -89
- package/src/sdam/server_description.ts +70 -80
- package/src/sdam/srv_polling.ts +1 -1
- package/src/sdam/topology.ts +37 -103
- package/src/sdam/topology_description.ts +44 -68
- package/src/sessions.ts +32 -39
- package/src/utils.ts +78 -75
package/src/change_stream.ts
CHANGED
|
@@ -1,16 +1,11 @@
|
|
|
1
|
-
import Denque = require('denque');
|
|
2
1
|
import type { Readable } from 'stream';
|
|
3
|
-
import {
|
|
2
|
+
import { promisify } from 'util';
|
|
4
3
|
|
|
5
|
-
import type { Binary, Document,
|
|
4
|
+
import type { Binary, Document, Timestamp } from './bson';
|
|
6
5
|
import { Collection } from './collection';
|
|
7
6
|
import { CHANGE, CLOSE, END, ERROR, INIT, MORE, RESPONSE, RESUME_TOKEN_CHANGED } from './constants';
|
|
8
|
-
import {
|
|
9
|
-
|
|
10
|
-
AbstractCursorEvents,
|
|
11
|
-
AbstractCursorOptions,
|
|
12
|
-
CursorStreamOptions
|
|
13
|
-
} from './cursor/abstract_cursor';
|
|
7
|
+
import type { AbstractCursorEvents, CursorStreamOptions } from './cursor/abstract_cursor';
|
|
8
|
+
import { ChangeStreamCursor, ChangeStreamCursorOptions } from './cursor/change_stream_cursor';
|
|
14
9
|
import { Db } from './db';
|
|
15
10
|
import {
|
|
16
11
|
AnyError,
|
|
@@ -20,26 +15,13 @@ import {
|
|
|
20
15
|
MongoRuntimeError
|
|
21
16
|
} from './error';
|
|
22
17
|
import { MongoClient } from './mongo_client';
|
|
23
|
-
import { InferIdType,
|
|
24
|
-
import {
|
|
18
|
+
import { InferIdType, TypedEventEmitter } from './mongo_types';
|
|
19
|
+
import type { AggregateOptions } from './operations/aggregate';
|
|
25
20
|
import type { CollationOptions, OperationParent } from './operations/command';
|
|
26
|
-
import { executeOperation, ExecutionResult } from './operations/execute_operation';
|
|
27
21
|
import type { ReadPreference } from './read_preference';
|
|
28
|
-
import type {
|
|
29
|
-
import
|
|
30
|
-
import {
|
|
31
|
-
calculateDurationInMs,
|
|
32
|
-
Callback,
|
|
33
|
-
filterOptions,
|
|
34
|
-
getTopology,
|
|
35
|
-
maxWireVersion,
|
|
36
|
-
maybePromise,
|
|
37
|
-
MongoDBNamespace,
|
|
38
|
-
now
|
|
39
|
-
} from './utils';
|
|
22
|
+
import type { ServerSessionId } from './sessions';
|
|
23
|
+
import { Callback, filterOptions, getTopology, maybePromise, MongoDBNamespace } from './utils';
|
|
40
24
|
|
|
41
|
-
/** @internal */
|
|
42
|
-
const kResumeQueue = Symbol('resumeQueue');
|
|
43
25
|
/** @internal */
|
|
44
26
|
const kCursorStream = Symbol('cursorStream');
|
|
45
27
|
/** @internal */
|
|
@@ -62,19 +44,10 @@ const CHANGE_DOMAIN_TYPES = {
|
|
|
62
44
|
CLUSTER: Symbol('Cluster')
|
|
63
45
|
};
|
|
64
46
|
|
|
65
|
-
interface TopologyWaitOptions {
|
|
66
|
-
start?: number;
|
|
67
|
-
timeout?: number;
|
|
68
|
-
readPreference?: ReadPreference;
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
const SELECTION_TIMEOUT = 30000;
|
|
72
|
-
|
|
73
47
|
const CHANGE_STREAM_EVENTS = [RESUME_TOKEN_CHANGED, END, CLOSE];
|
|
74
48
|
|
|
75
49
|
const NO_RESUME_TOKEN_ERROR =
|
|
76
50
|
'A change stream document has been received that lacks a resume token (_id).';
|
|
77
|
-
const NO_CURSOR_ERROR = 'ChangeStream has no cursor';
|
|
78
51
|
const CHANGESTREAM_CLOSED_ERROR = 'ChangeStream is closed';
|
|
79
52
|
|
|
80
53
|
/**
|
|
@@ -111,18 +84,6 @@ export interface PipeOptions {
|
|
|
111
84
|
end?: boolean;
|
|
112
85
|
}
|
|
113
86
|
|
|
114
|
-
/** @internal */
|
|
115
|
-
export type ChangeStreamAggregateRawResult<TChange> = {
|
|
116
|
-
$clusterTime: { clusterTime: Timestamp };
|
|
117
|
-
cursor: {
|
|
118
|
-
postBatchResumeToken: ResumeToken;
|
|
119
|
-
ns: string;
|
|
120
|
-
id: number | Long;
|
|
121
|
-
} & ({ firstBatch: TChange[] } | { nextBatch: TChange[] });
|
|
122
|
-
ok: 1;
|
|
123
|
-
operationTime: Timestamp;
|
|
124
|
-
};
|
|
125
|
-
|
|
126
87
|
/**
|
|
127
88
|
* Options that can be passed to a ChangeStream. Note that startAfter, resumeAfter, and startAtOperationTime are all mutually exclusive, and the server will error if more than one is specified.
|
|
128
89
|
* @public
|
|
@@ -565,11 +526,9 @@ export class ChangeStream<
|
|
|
565
526
|
namespace: MongoDBNamespace;
|
|
566
527
|
type: symbol;
|
|
567
528
|
/** @internal */
|
|
568
|
-
cursor: ChangeStreamCursor<TSchema, TChange
|
|
529
|
+
cursor: ChangeStreamCursor<TSchema, TChange>;
|
|
569
530
|
streamOptions?: CursorStreamOptions;
|
|
570
531
|
/** @internal */
|
|
571
|
-
[kResumeQueue]: Denque<Callback<ChangeStreamCursor<TSchema, TChange>>>;
|
|
572
|
-
/** @internal */
|
|
573
532
|
[kCursorStream]?: Readable & AsyncIterable<TChange>;
|
|
574
533
|
/** @internal */
|
|
575
534
|
[kClosed]: boolean;
|
|
@@ -635,8 +594,6 @@ export class ChangeStream<
|
|
|
635
594
|
this.options.readPreference = parent.readPreference;
|
|
636
595
|
}
|
|
637
596
|
|
|
638
|
-
this[kResumeQueue] = new Denque();
|
|
639
|
-
|
|
640
597
|
// Create contained Change Stream cursor
|
|
641
598
|
this.cursor = this._createChangeStreamCursor(options);
|
|
642
599
|
|
|
@@ -672,11 +629,28 @@ export class ChangeStream<
|
|
|
672
629
|
hasNext(callback: Callback<boolean>): void;
|
|
673
630
|
hasNext(callback?: Callback): Promise<boolean> | void {
|
|
674
631
|
this._setIsIterator();
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
632
|
+
// TOOD(NODE-4319): Add eslint rule preventing accidental variable shadowing
|
|
633
|
+
// Shadowing is intentional here. We want to override the `callback` variable
|
|
634
|
+
// from the outer scope so that the inner scope doesn't accidentally call the wrong callback.
|
|
635
|
+
return maybePromise(callback, callback => {
|
|
636
|
+
(async () => {
|
|
637
|
+
try {
|
|
638
|
+
const hasNext = await this.cursor.hasNext();
|
|
639
|
+
return hasNext;
|
|
640
|
+
} catch (error) {
|
|
641
|
+
try {
|
|
642
|
+
await this._processErrorIteratorMode(error);
|
|
643
|
+
const hasNext = await this.cursor.hasNext();
|
|
644
|
+
return hasNext;
|
|
645
|
+
} catch (error) {
|
|
646
|
+
await this.close().catch(err => err);
|
|
647
|
+
throw error;
|
|
648
|
+
}
|
|
649
|
+
}
|
|
650
|
+
})().then(
|
|
651
|
+
hasNext => callback(undefined, hasNext),
|
|
652
|
+
error => callback(error)
|
|
653
|
+
);
|
|
680
654
|
});
|
|
681
655
|
}
|
|
682
656
|
|
|
@@ -685,39 +659,80 @@ export class ChangeStream<
|
|
|
685
659
|
next(callback: Callback<TChange>): void;
|
|
686
660
|
next(callback?: Callback<TChange>): Promise<TChange> | void {
|
|
687
661
|
this._setIsIterator();
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
662
|
+
// TOOD(NODE-4319): Add eslint rule preventing accidental variable shadowing
|
|
663
|
+
// Shadowing is intentional here. We want to override the `callback` variable
|
|
664
|
+
// from the outer scope so that the inner scope doesn't accidentally call the wrong callback.
|
|
665
|
+
return maybePromise(callback, callback => {
|
|
666
|
+
(async () => {
|
|
667
|
+
try {
|
|
668
|
+
const change = await this.cursor.next();
|
|
669
|
+
const processedChange = this._processChange(change ?? null);
|
|
670
|
+
return processedChange;
|
|
671
|
+
} catch (error) {
|
|
672
|
+
try {
|
|
673
|
+
await this._processErrorIteratorMode(error);
|
|
674
|
+
const change = await this.cursor.next();
|
|
675
|
+
const processedChange = this._processChange(change ?? null);
|
|
676
|
+
return processedChange;
|
|
677
|
+
} catch (error) {
|
|
678
|
+
await this.close().catch(err => err);
|
|
679
|
+
throw error;
|
|
696
680
|
}
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
681
|
+
}
|
|
682
|
+
})().then(
|
|
683
|
+
change => callback(undefined, change),
|
|
684
|
+
error => callback(error)
|
|
685
|
+
);
|
|
686
|
+
});
|
|
687
|
+
}
|
|
688
|
+
|
|
689
|
+
/**
|
|
690
|
+
* Try to get the next available document from the Change Stream's cursor or `null` if an empty batch is returned
|
|
691
|
+
*/
|
|
692
|
+
tryNext(): Promise<Document | null>;
|
|
693
|
+
tryNext(callback: Callback<Document | null>): void;
|
|
694
|
+
tryNext(callback?: Callback<Document | null>): Promise<Document | null> | void {
|
|
695
|
+
this._setIsIterator();
|
|
696
|
+
// TOOD(NODE-4319): Add eslint rule preventing accidental variable shadowing
|
|
697
|
+
// Shadowing is intentional here. We want to override the `callback` variable
|
|
698
|
+
// from the outer scope so that the inner scope doesn't accidentally call the wrong callback.
|
|
699
|
+
return maybePromise(callback, callback => {
|
|
700
|
+
(async () => {
|
|
701
|
+
try {
|
|
702
|
+
const change = await this.cursor.tryNext();
|
|
703
|
+
return change ?? null;
|
|
704
|
+
} catch (error) {
|
|
705
|
+
try {
|
|
706
|
+
await this._processErrorIteratorMode(error);
|
|
707
|
+
const change = await this.cursor.tryNext();
|
|
708
|
+
return change ?? null;
|
|
709
|
+
} catch (error) {
|
|
710
|
+
await this.close().catch(err => err);
|
|
711
|
+
throw error;
|
|
712
|
+
}
|
|
713
|
+
}
|
|
714
|
+
})().then(
|
|
715
|
+
change => callback(undefined, change),
|
|
716
|
+
error => callback(error)
|
|
717
|
+
);
|
|
700
718
|
});
|
|
701
719
|
}
|
|
702
720
|
|
|
703
721
|
/** Is the cursor closed */
|
|
704
722
|
get closed(): boolean {
|
|
705
|
-
return this[kClosed] ||
|
|
723
|
+
return this[kClosed] || this.cursor.closed;
|
|
706
724
|
}
|
|
707
725
|
|
|
708
726
|
/** Close the Change Stream */
|
|
727
|
+
close(): Promise<void>;
|
|
728
|
+
close(callback: Callback): void;
|
|
709
729
|
close(callback?: Callback): Promise<void> | void {
|
|
710
730
|
this[kClosed] = true;
|
|
711
731
|
|
|
712
732
|
return maybePromise(callback, cb => {
|
|
713
|
-
if (!this.cursor) {
|
|
714
|
-
return cb();
|
|
715
|
-
}
|
|
716
|
-
|
|
717
733
|
const cursor = this.cursor;
|
|
718
734
|
return cursor.close(err => {
|
|
719
735
|
this._endStream();
|
|
720
|
-
this.cursor = undefined;
|
|
721
736
|
return cb(err);
|
|
722
737
|
});
|
|
723
738
|
});
|
|
@@ -725,29 +740,21 @@ export class ChangeStream<
|
|
|
725
740
|
|
|
726
741
|
/**
|
|
727
742
|
* Return a modified Readable stream including a possible transform method.
|
|
728
|
-
*
|
|
743
|
+
*
|
|
744
|
+
* NOTE: When using a Stream to process change stream events, the stream will
|
|
745
|
+
* NOT automatically resume in the case a resumable error is encountered.
|
|
746
|
+
*
|
|
747
|
+
* @throws MongoChangeStreamError if the underlying cursor or the change stream is closed
|
|
729
748
|
*/
|
|
730
749
|
stream(options?: CursorStreamOptions): Readable & AsyncIterable<TChange> {
|
|
750
|
+
if (this.closed) {
|
|
751
|
+
throw new MongoChangeStreamError(CHANGESTREAM_CLOSED_ERROR);
|
|
752
|
+
}
|
|
753
|
+
|
|
731
754
|
this.streamOptions = options;
|
|
732
|
-
if (!this.cursor) throw new MongoChangeStreamError(NO_CURSOR_ERROR);
|
|
733
755
|
return this.cursor.stream(options);
|
|
734
756
|
}
|
|
735
757
|
|
|
736
|
-
/**
|
|
737
|
-
* Try to get the next available document from the Change Stream's cursor or `null` if an empty batch is returned
|
|
738
|
-
*/
|
|
739
|
-
tryNext(): Promise<Document | null>;
|
|
740
|
-
tryNext(callback: Callback<Document | null>): void;
|
|
741
|
-
tryNext(callback?: Callback<Document | null>): Promise<Document | null> | void {
|
|
742
|
-
this._setIsIterator();
|
|
743
|
-
return maybePromise(callback, cb => {
|
|
744
|
-
this._getCursor((err, cursor) => {
|
|
745
|
-
if (err || !cursor) return cb(err); // failed to resume, raise an error
|
|
746
|
-
return cursor.tryNext(cb);
|
|
747
|
-
});
|
|
748
|
-
});
|
|
749
|
-
}
|
|
750
|
-
|
|
751
758
|
/** @internal */
|
|
752
759
|
private _setIsEmitter(): void {
|
|
753
760
|
if (this[kMode] === 'iterator') {
|
|
@@ -817,43 +824,13 @@ export class ChangeStream<
|
|
|
817
824
|
return changeStreamCursor;
|
|
818
825
|
}
|
|
819
826
|
|
|
820
|
-
/**
|
|
821
|
-
* This method performs a basic server selection loop, satisfying the requirements of
|
|
822
|
-
* ChangeStream resumability until the new SDAM layer can be used.
|
|
823
|
-
* @internal
|
|
824
|
-
*/
|
|
825
|
-
private _waitForTopologyConnected(
|
|
826
|
-
topology: Topology,
|
|
827
|
-
options: TopologyWaitOptions,
|
|
828
|
-
callback: Callback
|
|
829
|
-
) {
|
|
830
|
-
setTimeout(() => {
|
|
831
|
-
if (options && options.start == null) {
|
|
832
|
-
options.start = now();
|
|
833
|
-
}
|
|
834
|
-
|
|
835
|
-
const start = options.start || now();
|
|
836
|
-
const timeout = options.timeout || SELECTION_TIMEOUT;
|
|
837
|
-
if (topology.isConnected()) {
|
|
838
|
-
return callback();
|
|
839
|
-
}
|
|
840
|
-
|
|
841
|
-
if (calculateDurationInMs(start) > timeout) {
|
|
842
|
-
// TODO(NODE-3497): Replace with MongoNetworkTimeoutError
|
|
843
|
-
return callback(new MongoRuntimeError('Timed out waiting for connection'));
|
|
844
|
-
}
|
|
845
|
-
|
|
846
|
-
this._waitForTopologyConnected(topology, options, callback);
|
|
847
|
-
}, 500); // this is an arbitrary wait time to allow SDAM to transition
|
|
848
|
-
}
|
|
849
|
-
|
|
850
827
|
/** @internal */
|
|
851
|
-
private
|
|
852
|
-
|
|
853
|
-
this.emit(ChangeStream.ERROR, error);
|
|
854
|
-
}
|
|
828
|
+
private _closeEmitterModeWithError(error: AnyError): void {
|
|
829
|
+
this.emit(ChangeStream.ERROR, error);
|
|
855
830
|
|
|
856
|
-
this.close(() =>
|
|
831
|
+
this.close(() => {
|
|
832
|
+
// nothing to do
|
|
833
|
+
});
|
|
857
834
|
}
|
|
858
835
|
|
|
859
836
|
/** @internal */
|
|
@@ -861,8 +838,15 @@ export class ChangeStream<
|
|
|
861
838
|
this._setIsEmitter();
|
|
862
839
|
const stream = this[kCursorStream] ?? cursor.stream();
|
|
863
840
|
this[kCursorStream] = stream;
|
|
864
|
-
stream.on('data', change =>
|
|
865
|
-
|
|
841
|
+
stream.on('data', change => {
|
|
842
|
+
try {
|
|
843
|
+
const processedChange = this._processChange(change);
|
|
844
|
+
this.emit(ChangeStream.CHANGE, processedChange);
|
|
845
|
+
} catch (error) {
|
|
846
|
+
this.emit(ChangeStream.ERROR, error);
|
|
847
|
+
}
|
|
848
|
+
});
|
|
849
|
+
stream.on('error', error => this._processErrorStreamMode(error));
|
|
866
850
|
}
|
|
867
851
|
|
|
868
852
|
/** @internal */
|
|
@@ -877,291 +861,81 @@ export class ChangeStream<
|
|
|
877
861
|
}
|
|
878
862
|
|
|
879
863
|
/** @internal */
|
|
880
|
-
private
|
|
864
|
+
private _processChange(change: TChange | null): TChange {
|
|
881
865
|
if (this[kClosed]) {
|
|
882
866
|
// TODO(NODE-3485): Replace with MongoChangeStreamClosedError
|
|
883
|
-
|
|
884
|
-
return;
|
|
867
|
+
throw new MongoAPIError(CHANGESTREAM_CLOSED_ERROR);
|
|
885
868
|
}
|
|
886
869
|
|
|
887
870
|
// a null change means the cursor has been notified, implicitly closing the change stream
|
|
888
871
|
if (change == null) {
|
|
889
872
|
// TODO(NODE-3485): Replace with MongoChangeStreamClosedError
|
|
890
|
-
|
|
873
|
+
throw new MongoRuntimeError(CHANGESTREAM_CLOSED_ERROR);
|
|
891
874
|
}
|
|
892
875
|
|
|
893
876
|
if (change && !change._id) {
|
|
894
|
-
|
|
877
|
+
throw new MongoChangeStreamError(NO_RESUME_TOKEN_ERROR);
|
|
895
878
|
}
|
|
896
879
|
|
|
897
880
|
// cache the resume token
|
|
898
|
-
this.cursor
|
|
881
|
+
this.cursor.cacheResumeToken(change._id);
|
|
899
882
|
|
|
900
883
|
// wipe the startAtOperationTime if there was one so that there won't be a conflict
|
|
901
884
|
// between resumeToken and startAtOperationTime if we need to reconnect the cursor
|
|
902
885
|
this.options.startAtOperationTime = undefined;
|
|
903
886
|
|
|
904
|
-
|
|
905
|
-
if (!callback) return this.emit(ChangeStream.CHANGE, change);
|
|
906
|
-
return callback(undefined, change);
|
|
887
|
+
return change;
|
|
907
888
|
}
|
|
908
889
|
|
|
909
890
|
/** @internal */
|
|
910
|
-
private
|
|
911
|
-
const cursor = this.cursor;
|
|
912
|
-
|
|
891
|
+
private _processErrorStreamMode(changeStreamError: AnyError) {
|
|
913
892
|
// If the change stream has been closed explicitly, do not process error.
|
|
914
|
-
if (this[kClosed])
|
|
915
|
-
// TODO(NODE-3485): Replace with MongoChangeStreamClosedError
|
|
916
|
-
if (callback) callback(new MongoAPIError(CHANGESTREAM_CLOSED_ERROR));
|
|
917
|
-
return;
|
|
918
|
-
}
|
|
893
|
+
if (this[kClosed]) return;
|
|
919
894
|
|
|
920
|
-
|
|
921
|
-
const resumeWithCursor = (newCursor: ChangeStreamCursor<TSchema, TChange>) => {
|
|
922
|
-
this.cursor = newCursor;
|
|
923
|
-
this._processResumeQueue();
|
|
924
|
-
};
|
|
925
|
-
|
|
926
|
-
// otherwise, raise an error and close the change stream
|
|
927
|
-
const unresumableError = (err: AnyError) => {
|
|
928
|
-
if (!callback) {
|
|
929
|
-
this.emit(ChangeStream.ERROR, err);
|
|
930
|
-
}
|
|
931
|
-
|
|
932
|
-
this.close(() => this._processResumeQueue(err));
|
|
933
|
-
};
|
|
934
|
-
|
|
935
|
-
if (cursor && isResumableError(error, maxWireVersion(cursor.server))) {
|
|
936
|
-
this.cursor = undefined;
|
|
937
|
-
|
|
938
|
-
// stop listening to all events from old cursor
|
|
895
|
+
if (isResumableError(changeStreamError, this.cursor.maxWireVersion)) {
|
|
939
896
|
this._endStream();
|
|
940
|
-
|
|
941
|
-
// close internal cursor, ignore errors
|
|
942
|
-
cursor.close();
|
|
897
|
+
this.cursor.close().catch(() => null);
|
|
943
898
|
|
|
944
899
|
const topology = getTopology(this.parent);
|
|
945
|
-
this.
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
// create a new cursor, preserving the old cursor's options
|
|
950
|
-
const newCursor = this._createChangeStreamCursor(cursor.resumeOptions);
|
|
951
|
-
|
|
952
|
-
// attempt to continue in emitter mode
|
|
953
|
-
if (!callback) return resumeWithCursor(newCursor);
|
|
954
|
-
|
|
955
|
-
// attempt to continue in iterator mode
|
|
956
|
-
newCursor.hasNext(err => {
|
|
957
|
-
// if there's an error immediately after resuming, close the stream
|
|
958
|
-
if (err) return unresumableError(err);
|
|
959
|
-
resumeWithCursor(newCursor);
|
|
960
|
-
});
|
|
900
|
+
topology.selectServer(this.cursor.readPreference, {}, serverSelectionError => {
|
|
901
|
+
if (serverSelectionError) return this._closeEmitterModeWithError(changeStreamError);
|
|
902
|
+
this.cursor = this._createChangeStreamCursor(this.cursor.resumeOptions);
|
|
961
903
|
});
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
// if initial error wasn't resumable, raise an error and close the change stream
|
|
966
|
-
return this._closeWithError(error, callback);
|
|
967
|
-
}
|
|
968
|
-
|
|
969
|
-
/** @internal */
|
|
970
|
-
private _getCursor(callback: Callback<ChangeStreamCursor<TSchema, TChange>>) {
|
|
971
|
-
if (this[kClosed]) {
|
|
972
|
-
// TODO(NODE-3485): Replace with MongoChangeStreamClosedError
|
|
973
|
-
callback(new MongoAPIError(CHANGESTREAM_CLOSED_ERROR));
|
|
974
|
-
return;
|
|
975
|
-
}
|
|
976
|
-
|
|
977
|
-
// if a cursor exists and it is open, return it
|
|
978
|
-
if (this.cursor) {
|
|
979
|
-
callback(undefined, this.cursor);
|
|
980
|
-
return;
|
|
904
|
+
} else {
|
|
905
|
+
this._closeEmitterModeWithError(changeStreamError);
|
|
981
906
|
}
|
|
982
|
-
|
|
983
|
-
// no cursor, queue callback until topology reconnects
|
|
984
|
-
this[kResumeQueue].push(callback);
|
|
985
907
|
}
|
|
986
908
|
|
|
987
909
|
/**
|
|
988
|
-
* Drain the resume queue when a new has become available
|
|
989
910
|
* @internal
|
|
990
911
|
*
|
|
991
|
-
*
|
|
912
|
+
* TODO(NODE-4320): promisify selectServer and refactor this code to be async
|
|
913
|
+
*
|
|
914
|
+
* we promisify _processErrorIteratorModeCallback until we have a promisifed version of selectServer.
|
|
992
915
|
*/
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
const request = this[kResumeQueue].pop();
|
|
996
|
-
if (!request) break; // Should never occur but TS can't use the length check in the while condition
|
|
997
|
-
|
|
998
|
-
if (!error) {
|
|
999
|
-
if (this[kClosed]) {
|
|
1000
|
-
// TODO(NODE-3485): Replace with MongoChangeStreamClosedError
|
|
1001
|
-
request(new MongoAPIError(CHANGESTREAM_CLOSED_ERROR));
|
|
1002
|
-
return;
|
|
1003
|
-
}
|
|
1004
|
-
if (!this.cursor) {
|
|
1005
|
-
request(new MongoChangeStreamError(NO_CURSOR_ERROR));
|
|
1006
|
-
return;
|
|
1007
|
-
}
|
|
1008
|
-
}
|
|
1009
|
-
request(error, this.cursor ?? undefined);
|
|
1010
|
-
}
|
|
1011
|
-
}
|
|
1012
|
-
}
|
|
1013
|
-
|
|
1014
|
-
/** @internal */
|
|
1015
|
-
export interface ChangeStreamCursorOptions extends AbstractCursorOptions {
|
|
1016
|
-
startAtOperationTime?: OperationTime;
|
|
1017
|
-
resumeAfter?: ResumeToken;
|
|
1018
|
-
startAfter?: ResumeToken;
|
|
1019
|
-
maxAwaitTimeMS?: number;
|
|
1020
|
-
collation?: CollationOptions;
|
|
1021
|
-
fullDocument?: string;
|
|
1022
|
-
}
|
|
1023
|
-
|
|
1024
|
-
/** @internal */
|
|
1025
|
-
export class ChangeStreamCursor<
|
|
1026
|
-
TSchema extends Document = Document,
|
|
1027
|
-
TChange extends Document = ChangeStreamDocument<TSchema>
|
|
1028
|
-
> extends AbstractCursor<TChange, ChangeStreamEvents> {
|
|
1029
|
-
_resumeToken: ResumeToken;
|
|
1030
|
-
startAtOperationTime?: OperationTime;
|
|
1031
|
-
hasReceived?: boolean;
|
|
1032
|
-
resumeAfter: ResumeToken;
|
|
1033
|
-
startAfter: ResumeToken;
|
|
1034
|
-
options: ChangeStreamCursorOptions;
|
|
1035
|
-
|
|
1036
|
-
postBatchResumeToken?: ResumeToken;
|
|
1037
|
-
pipeline: Document[];
|
|
1038
|
-
|
|
1039
|
-
constructor(
|
|
1040
|
-
client: MongoClient,
|
|
1041
|
-
namespace: MongoDBNamespace,
|
|
1042
|
-
pipeline: Document[] = [],
|
|
1043
|
-
options: ChangeStreamCursorOptions = {}
|
|
1044
|
-
) {
|
|
1045
|
-
super(client, namespace, options);
|
|
1046
|
-
|
|
1047
|
-
this.pipeline = pipeline;
|
|
1048
|
-
this.options = options;
|
|
1049
|
-
this._resumeToken = null;
|
|
1050
|
-
this.startAtOperationTime = options.startAtOperationTime;
|
|
1051
|
-
|
|
1052
|
-
if (options.startAfter) {
|
|
1053
|
-
this.resumeToken = options.startAfter;
|
|
1054
|
-
} else if (options.resumeAfter) {
|
|
1055
|
-
this.resumeToken = options.resumeAfter;
|
|
1056
|
-
}
|
|
1057
|
-
}
|
|
916
|
+
// eslint-disable-next-line @typescript-eslint/unbound-method
|
|
917
|
+
private _processErrorIteratorMode = promisify(this._processErrorIteratorModeCallback);
|
|
1058
918
|
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
this
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
get resumeToken(): ResumeToken {
|
|
1065
|
-
return this._resumeToken;
|
|
1066
|
-
}
|
|
1067
|
-
|
|
1068
|
-
get resumeOptions(): ChangeStreamCursorOptions {
|
|
1069
|
-
const options: ChangeStreamCursorOptions = {
|
|
1070
|
-
...this.options
|
|
1071
|
-
};
|
|
1072
|
-
|
|
1073
|
-
for (const key of ['resumeAfter', 'startAfter', 'startAtOperationTime'] as const) {
|
|
1074
|
-
delete options[key];
|
|
919
|
+
/** @internal */
|
|
920
|
+
private _processErrorIteratorModeCallback(changeStreamError: AnyError, callback: Callback) {
|
|
921
|
+
if (this[kClosed]) {
|
|
922
|
+
// TODO(NODE-3485): Replace with MongoChangeStreamClosedError
|
|
923
|
+
return callback(new MongoAPIError(CHANGESTREAM_CLOSED_ERROR));
|
|
1075
924
|
}
|
|
1076
925
|
|
|
1077
|
-
if (this.
|
|
1078
|
-
|
|
1079
|
-
options.startAfter = this.resumeToken;
|
|
1080
|
-
} else {
|
|
1081
|
-
options.resumeAfter = this.resumeToken;
|
|
1082
|
-
}
|
|
1083
|
-
} else if (this.startAtOperationTime != null && maxWireVersion(this.server) >= 7) {
|
|
1084
|
-
options.startAtOperationTime = this.startAtOperationTime;
|
|
1085
|
-
}
|
|
926
|
+
if (isResumableError(changeStreamError, this.cursor.maxWireVersion)) {
|
|
927
|
+
this.cursor.close().catch(() => null);
|
|
1086
928
|
|
|
1087
|
-
|
|
1088
|
-
|
|
929
|
+
const topology = getTopology(this.parent);
|
|
930
|
+
topology.selectServer(this.cursor.readPreference, {}, serverSelectionError => {
|
|
931
|
+
// if the topology can't reconnect, close the stream
|
|
932
|
+
if (serverSelectionError) return this.close(() => callback(changeStreamError));
|
|
1089
933
|
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
934
|
+
this.cursor = this._createChangeStreamCursor(this.cursor.resumeOptions);
|
|
935
|
+
callback();
|
|
936
|
+
});
|
|
1093
937
|
} else {
|
|
1094
|
-
this.
|
|
1095
|
-
}
|
|
1096
|
-
this.hasReceived = true;
|
|
1097
|
-
}
|
|
1098
|
-
|
|
1099
|
-
_processBatch(response: ChangeStreamAggregateRawResult<TChange>): void {
|
|
1100
|
-
const cursor = response.cursor;
|
|
1101
|
-
if (cursor.postBatchResumeToken) {
|
|
1102
|
-
this.postBatchResumeToken = response.cursor.postBatchResumeToken;
|
|
1103
|
-
|
|
1104
|
-
const batch =
|
|
1105
|
-
'firstBatch' in response.cursor ? response.cursor.firstBatch : response.cursor.nextBatch;
|
|
1106
|
-
if (batch.length === 0) {
|
|
1107
|
-
this.resumeToken = cursor.postBatchResumeToken;
|
|
1108
|
-
}
|
|
938
|
+
this.close(() => callback(changeStreamError));
|
|
1109
939
|
}
|
|
1110
940
|
}
|
|
1111
|
-
|
|
1112
|
-
clone(): AbstractCursor<TChange> {
|
|
1113
|
-
return new ChangeStreamCursor(this.client, this.namespace, this.pipeline, {
|
|
1114
|
-
...this.cursorOptions
|
|
1115
|
-
});
|
|
1116
|
-
}
|
|
1117
|
-
|
|
1118
|
-
_initialize(session: ClientSession, callback: Callback<ExecutionResult>): void {
|
|
1119
|
-
const aggregateOperation = new AggregateOperation(this.namespace, this.pipeline, {
|
|
1120
|
-
...this.cursorOptions,
|
|
1121
|
-
...this.options,
|
|
1122
|
-
session
|
|
1123
|
-
});
|
|
1124
|
-
|
|
1125
|
-
executeOperation<TODO_NODE_3286, ChangeStreamAggregateRawResult<TChange>>(
|
|
1126
|
-
session.client,
|
|
1127
|
-
aggregateOperation,
|
|
1128
|
-
(err, response) => {
|
|
1129
|
-
if (err || response == null) {
|
|
1130
|
-
return callback(err);
|
|
1131
|
-
}
|
|
1132
|
-
|
|
1133
|
-
const server = aggregateOperation.server;
|
|
1134
|
-
if (
|
|
1135
|
-
this.startAtOperationTime == null &&
|
|
1136
|
-
this.resumeAfter == null &&
|
|
1137
|
-
this.startAfter == null &&
|
|
1138
|
-
maxWireVersion(server) >= 7
|
|
1139
|
-
) {
|
|
1140
|
-
this.startAtOperationTime = response.operationTime;
|
|
1141
|
-
}
|
|
1142
|
-
|
|
1143
|
-
this._processBatch(response);
|
|
1144
|
-
|
|
1145
|
-
this.emit(ChangeStream.INIT, response);
|
|
1146
|
-
this.emit(ChangeStream.RESPONSE);
|
|
1147
|
-
|
|
1148
|
-
// TODO: NODE-2882
|
|
1149
|
-
callback(undefined, { server, session, response });
|
|
1150
|
-
}
|
|
1151
|
-
);
|
|
1152
|
-
}
|
|
1153
|
-
|
|
1154
|
-
override _getMore(batchSize: number, callback: Callback): void {
|
|
1155
|
-
super._getMore(batchSize, (err, response) => {
|
|
1156
|
-
if (err) {
|
|
1157
|
-
return callback(err);
|
|
1158
|
-
}
|
|
1159
|
-
|
|
1160
|
-
this._processBatch(response as TODO_NODE_3286 as ChangeStreamAggregateRawResult<TChange>);
|
|
1161
|
-
|
|
1162
|
-
this.emit(ChangeStream.MORE, response);
|
|
1163
|
-
this.emit(ChangeStream.RESPONSE);
|
|
1164
|
-
callback(err, response);
|
|
1165
|
-
});
|
|
1166
|
-
}
|
|
1167
941
|
}
|