mongodb 4.4.1 → 4.6.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/README.md +3 -2
- package/lib/admin.js +5 -6
- package/lib/admin.js.map +1 -1
- package/lib/bulk/common.js +34 -7
- package/lib/bulk/common.js.map +1 -1
- package/lib/bulk/unordered.js.map +1 -1
- package/lib/change_stream.js +251 -245
- package/lib/change_stream.js.map +1 -1
- package/lib/cmap/auth/gssapi.js.map +1 -1
- package/lib/cmap/auth/mongocr.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/plain.js.map +1 -1
- package/lib/cmap/auth/scram.js +1 -0
- package/lib/cmap/auth/scram.js.map +1 -1
- package/lib/cmap/auth/x509.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 +111 -145
- package/lib/cmap/connection.js.map +1 -1
- package/lib/cmap/errors.js.map +1 -1
- package/lib/cmap/message_stream.js.map +1 -1
- package/lib/cmap/stream_description.js +3 -0
- package/lib/cmap/stream_description.js.map +1 -1
- package/lib/collection.js +60 -29
- package/lib/collection.js.map +1 -1
- package/lib/connection_string.js +32 -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 +64 -51
- package/lib/cursor/abstract_cursor.js.map +1 -1
- package/lib/cursor/aggregation_cursor.js +2 -2
- package/lib/cursor/aggregation_cursor.js.map +1 -1
- package/lib/cursor/find_cursor.js +9 -4
- package/lib/cursor/find_cursor.js.map +1 -1
- package/lib/db.js +20 -13
- package/lib/db.js.map +1 -1
- package/lib/encrypter.js +21 -10
- package/lib/encrypter.js.map +1 -1
- package/lib/error.js +121 -59
- package/lib/error.js.map +1 -1
- package/lib/gridfs/download.js +2 -0
- package/lib/gridfs/download.js.map +1 -1
- package/lib/gridfs/index.js +42 -51
- package/lib/gridfs/index.js.map +1 -1
- package/lib/gridfs/upload.js +1 -1
- package/lib/gridfs/upload.js.map +1 -1
- package/lib/index.js +7 -3
- package/lib/index.js.map +1 -1
- package/lib/mongo_client.js +21 -27
- package/lib/mongo_client.js.map +1 -1
- package/lib/operations/add_user.js +8 -1
- package/lib/operations/add_user.js.map +1 -1
- package/lib/operations/aggregate.js +5 -0
- package/lib/operations/aggregate.js.map +1 -1
- package/lib/operations/bulk_write.js.map +1 -1
- package/lib/operations/collections.js.map +1 -1
- package/lib/operations/command.js +0 -3
- package/lib/operations/command.js.map +1 -1
- package/lib/operations/common_functions.js +8 -1
- package/lib/operations/common_functions.js.map +1 -1
- package/lib/operations/count.js.map +1 -1
- package/lib/operations/count_documents.js.map +1 -1
- package/lib/operations/create_collection.js +51 -17
- package/lib/operations/create_collection.js.map +1 -1
- package/lib/operations/delete.js +5 -3
- package/lib/operations/delete.js.map +1 -1
- package/lib/operations/distinct.js.map +1 -1
- package/lib/operations/drop.js +67 -7
- package/lib/operations/drop.js.map +1 -1
- package/lib/operations/estimated_document_count.js.map +1 -1
- package/lib/operations/eval.js.map +1 -1
- package/lib/operations/execute_operation.js +71 -79
- package/lib/operations/execute_operation.js.map +1 -1
- package/lib/operations/find.js +3 -52
- package/lib/operations/find.js.map +1 -1
- package/lib/operations/find_and_modify.js +5 -0
- package/lib/operations/find_and_modify.js.map +1 -1
- package/lib/operations/get_more.js +5 -0
- package/lib/operations/get_more.js.map +1 -1
- package/lib/operations/indexes.js +8 -9
- package/lib/operations/indexes.js.map +1 -1
- package/lib/operations/insert.js +8 -2
- package/lib/operations/insert.js.map +1 -1
- package/lib/operations/is_capped.js.map +1 -1
- package/lib/operations/list_collections.js +10 -42
- package/lib/operations/list_collections.js.map +1 -1
- package/lib/operations/list_databases.js +5 -0
- package/lib/operations/list_databases.js.map +1 -1
- package/lib/operations/map_reduce.js +1 -2
- package/lib/operations/map_reduce.js.map +1 -1
- package/lib/operations/operation.js +1 -3
- package/lib/operations/operation.js.map +1 -1
- package/lib/operations/options_operation.js.map +1 -1
- package/lib/operations/profiling_level.js.map +1 -1
- package/lib/operations/remove_user.js.map +1 -1
- package/lib/operations/rename.js +1 -1
- package/lib/operations/rename.js.map +1 -1
- package/lib/operations/run_command.js.map +1 -1
- package/lib/operations/set_profiling_level.js.map +1 -1
- package/lib/operations/stats.js.map +1 -1
- package/lib/operations/update.js +5 -0
- package/lib/operations/update.js.map +1 -1
- package/lib/operations/validate_collection.js.map +1 -1
- package/lib/read_concern.js +1 -0
- package/lib/read_concern.js.map +1 -1
- package/lib/sdam/common.js +1 -7
- package/lib/sdam/common.js.map +1 -1
- package/lib/sdam/events.js +1 -1
- package/lib/sdam/events.js.map +1 -1
- package/lib/sdam/monitor.js +1 -2
- package/lib/sdam/monitor.js.map +1 -1
- package/lib/sdam/server.js +108 -78
- package/lib/sdam/server.js.map +1 -1
- package/lib/sdam/topology.js +38 -55
- package/lib/sdam/topology.js.map +1 -1
- package/lib/sdam/topology_description.js +3 -4
- package/lib/sdam/topology_description.js.map +1 -1
- package/lib/sessions.js +93 -68
- package/lib/sessions.js.map +1 -1
- package/lib/utils.js +21 -97
- package/lib/utils.js.map +1 -1
- package/mongodb.d.ts +417 -92
- package/package.json +25 -29
- package/src/admin.ts +6 -10
- package/src/bulk/common.ts +45 -14
- package/src/bulk/unordered.ts +1 -1
- package/src/change_stream.ts +559 -425
- package/src/cmap/auth/gssapi.ts +1 -1
- package/src/cmap/auth/mongocr.ts +1 -1
- package/src/cmap/auth/mongodb_aws.ts +6 -1
- package/src/cmap/auth/plain.ts +1 -1
- package/src/cmap/auth/scram.ts +3 -2
- package/src/cmap/auth/x509.ts +6 -2
- package/src/cmap/commands.ts +26 -22
- package/src/cmap/connect.ts +15 -14
- package/src/cmap/connection.ts +163 -185
- package/src/cmap/errors.ts +2 -2
- package/src/cmap/message_stream.ts +2 -2
- package/src/cmap/stream_description.ts +4 -1
- package/src/collection.ts +66 -35
- package/src/connection_string.ts +46 -18
- package/src/constants.ts +6 -0
- package/src/cursor/abstract_cursor.ts +87 -65
- package/src/cursor/aggregation_cursor.ts +4 -4
- package/src/cursor/find_cursor.ts +16 -8
- package/src/db.ts +27 -24
- package/src/deps.ts +40 -0
- package/src/encrypter.ts +22 -11
- package/src/error.ts +170 -89
- package/src/gridfs/download.ts +3 -1
- package/src/gridfs/index.ts +51 -68
- package/src/gridfs/upload.ts +13 -13
- package/src/index.ts +21 -0
- package/src/mongo_client.ts +36 -50
- package/src/mongo_types.ts +1 -1
- package/src/operations/add_user.ts +14 -3
- package/src/operations/aggregate.ts +15 -5
- package/src/operations/bulk_write.ts +6 -2
- package/src/operations/collections.ts +6 -2
- package/src/operations/command.ts +23 -12
- package/src/operations/common_functions.ts +8 -1
- package/src/operations/count.ts +6 -2
- package/src/operations/count_documents.ts +5 -1
- package/src/operations/create_collection.ts +91 -23
- package/src/operations/delete.ts +19 -13
- package/src/operations/distinct.ts +6 -2
- package/src/operations/drop.ts +100 -10
- package/src/operations/estimated_document_count.ts +11 -3
- package/src/operations/eval.ts +6 -2
- package/src/operations/execute_operation.ts +103 -101
- package/src/operations/find.ts +9 -85
- package/src/operations/find_and_modify.ts +21 -2
- package/src/operations/get_more.ts +20 -6
- package/src/operations/indexes.ts +54 -36
- package/src/operations/insert.ts +28 -7
- package/src/operations/is_capped.ts +6 -2
- package/src/operations/list_collections.ts +24 -59
- package/src/operations/list_databases.ts +13 -3
- package/src/operations/map_reduce.ts +7 -6
- package/src/operations/operation.ts +10 -9
- package/src/operations/options_operation.ts +6 -2
- package/src/operations/profiling_level.ts +6 -2
- package/src/operations/remove_user.ts +6 -2
- package/src/operations/rename.ts +7 -3
- package/src/operations/run_command.ts +6 -2
- package/src/operations/set_profiling_level.ts +6 -2
- package/src/operations/stats.ts +12 -4
- package/src/operations/update.ts +19 -9
- package/src/operations/validate_collection.ts +6 -2
- package/src/read_concern.ts +1 -0
- package/src/sdam/common.ts +0 -6
- package/src/sdam/events.ts +2 -2
- package/src/sdam/monitor.ts +4 -5
- package/src/sdam/server.ts +125 -117
- package/src/sdam/topology.ts +39 -78
- package/src/sdam/topology_description.ts +3 -4
- package/src/sessions.ts +108 -78
- package/src/utils.ts +39 -119
- package/tsconfig.json +40 -0
- package/mongodb.ts34.d.ts +0 -5720
package/src/sessions.ts
CHANGED
|
@@ -6,16 +6,14 @@ import { PINNED, UNPINNED } from './constants';
|
|
|
6
6
|
import type { AbstractCursor } from './cursor/abstract_cursor';
|
|
7
7
|
import {
|
|
8
8
|
AnyError,
|
|
9
|
-
isRetryableEndTransactionError,
|
|
10
|
-
isRetryableError,
|
|
11
9
|
MongoAPIError,
|
|
12
10
|
MongoCompatibilityError,
|
|
13
11
|
MONGODB_ERROR_CODES,
|
|
14
12
|
MongoDriverError,
|
|
15
13
|
MongoError,
|
|
14
|
+
MongoErrorLabel,
|
|
16
15
|
MongoExpiredSessionError,
|
|
17
16
|
MongoInvalidArgumentError,
|
|
18
|
-
MongoNetworkError,
|
|
19
17
|
MongoRuntimeError,
|
|
20
18
|
MongoServerError,
|
|
21
19
|
MongoTransactionError,
|
|
@@ -41,24 +39,9 @@ import {
|
|
|
41
39
|
now,
|
|
42
40
|
uuidV4
|
|
43
41
|
} from './utils';
|
|
44
|
-
import type { WriteConcern } from './write_concern';
|
|
45
42
|
|
|
46
43
|
const minWireVersionForShardedTransactions = 8;
|
|
47
44
|
|
|
48
|
-
function assertAlive(session: ClientSession, callback?: Callback): boolean {
|
|
49
|
-
if (session.serverSession == null) {
|
|
50
|
-
const error = new MongoExpiredSessionError();
|
|
51
|
-
if (typeof callback === 'function') {
|
|
52
|
-
callback(error);
|
|
53
|
-
return false;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
throw error;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
return true;
|
|
60
|
-
}
|
|
61
|
-
|
|
62
45
|
/** @public */
|
|
63
46
|
export interface ClientSessionOptions {
|
|
64
47
|
/** Whether causal consistency should be enabled on this session */
|
|
@@ -92,6 +75,8 @@ const kSnapshotTime = Symbol('snapshotTime');
|
|
|
92
75
|
const kSnapshotEnabled = Symbol('snapshotEnabled');
|
|
93
76
|
/** @internal */
|
|
94
77
|
const kPinnedConnection = Symbol('pinnedConnection');
|
|
78
|
+
/** @internal Accumulates total number of increments to add to txnNumber when applying session to command */
|
|
79
|
+
const kTxnNumberIncrement = Symbol('txnNumberIncrement');
|
|
95
80
|
|
|
96
81
|
/** @public */
|
|
97
82
|
export interface EndSessionOptions {
|
|
@@ -126,13 +111,15 @@ export class ClientSession extends TypedEventEmitter<ClientSessionEvents> {
|
|
|
126
111
|
defaultTransactionOptions: TransactionOptions;
|
|
127
112
|
transaction: Transaction;
|
|
128
113
|
/** @internal */
|
|
129
|
-
[kServerSession]
|
|
114
|
+
[kServerSession]: ServerSession | null;
|
|
130
115
|
/** @internal */
|
|
131
116
|
[kSnapshotTime]?: Timestamp;
|
|
132
117
|
/** @internal */
|
|
133
118
|
[kSnapshotEnabled] = false;
|
|
134
119
|
/** @internal */
|
|
135
120
|
[kPinnedConnection]?: Connection;
|
|
121
|
+
/** @internal */
|
|
122
|
+
[kTxnNumberIncrement]: number;
|
|
136
123
|
|
|
137
124
|
/**
|
|
138
125
|
* Create a client session.
|
|
@@ -175,7 +162,10 @@ export class ClientSession extends TypedEventEmitter<ClientSessionEvents> {
|
|
|
175
162
|
this.sessionPool = sessionPool;
|
|
176
163
|
this.hasEnded = false;
|
|
177
164
|
this.clientOptions = clientOptions;
|
|
178
|
-
|
|
165
|
+
|
|
166
|
+
this.explicit = !!options.explicit;
|
|
167
|
+
this[kServerSession] = this.explicit ? this.sessionPool.acquire() : null;
|
|
168
|
+
this[kTxnNumberIncrement] = 0;
|
|
179
169
|
|
|
180
170
|
this.supports = {
|
|
181
171
|
causalConsistency: options.snapshot !== true && options.causalConsistency !== false
|
|
@@ -184,7 +174,6 @@ export class ClientSession extends TypedEventEmitter<ClientSessionEvents> {
|
|
|
184
174
|
this.clusterTime = options.initialClusterTime;
|
|
185
175
|
|
|
186
176
|
this.operationTime = undefined;
|
|
187
|
-
this.explicit = !!options.explicit;
|
|
188
177
|
this.owner = options.owner;
|
|
189
178
|
this.defaultTransactionOptions = Object.assign({}, options.defaultTransactionOptions);
|
|
190
179
|
this.transaction = new Transaction();
|
|
@@ -192,16 +181,22 @@ export class ClientSession extends TypedEventEmitter<ClientSessionEvents> {
|
|
|
192
181
|
|
|
193
182
|
/** The server id associated with this session */
|
|
194
183
|
get id(): ServerSessionId | undefined {
|
|
195
|
-
return this
|
|
184
|
+
return this[kServerSession]?.id;
|
|
196
185
|
}
|
|
197
186
|
|
|
198
187
|
get serverSession(): ServerSession {
|
|
199
|
-
|
|
200
|
-
|
|
188
|
+
let serverSession = this[kServerSession];
|
|
189
|
+
if (serverSession == null) {
|
|
190
|
+
if (this.explicit) {
|
|
191
|
+
throw new MongoRuntimeError('Unexpected null serverSession for an explicit session');
|
|
192
|
+
}
|
|
193
|
+
if (this.hasEnded) {
|
|
194
|
+
throw new MongoRuntimeError('Unexpected null serverSession for an ended implicit session');
|
|
195
|
+
}
|
|
196
|
+
serverSession = this.sessionPool.acquire();
|
|
197
|
+
this[kServerSession] = serverSession;
|
|
201
198
|
}
|
|
202
|
-
|
|
203
|
-
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
204
|
-
return this[kServerSession]!;
|
|
199
|
+
return serverSession;
|
|
205
200
|
}
|
|
206
201
|
|
|
207
202
|
/** Whether or not this session is configured for snapshot reads */
|
|
@@ -270,9 +265,15 @@ export class ClientSession extends TypedEventEmitter<ClientSessionEvents> {
|
|
|
270
265
|
const completeEndSession = () => {
|
|
271
266
|
maybeClearPinnedConnection(this, finalOptions);
|
|
272
267
|
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
268
|
+
const serverSession = this[kServerSession];
|
|
269
|
+
if (serverSession != null) {
|
|
270
|
+
// release the server session back to the pool
|
|
271
|
+
this.sessionPool.release(serverSession);
|
|
272
|
+
// Make sure a new serverSession never makes it on to the ClientSession
|
|
273
|
+
Object.defineProperty(this, kServerSession, {
|
|
274
|
+
value: ServerSession.clone(serverSession)
|
|
275
|
+
});
|
|
276
|
+
}
|
|
276
277
|
|
|
277
278
|
// mark the session as ended, and emit a signal
|
|
278
279
|
this.hasEnded = true;
|
|
@@ -282,7 +283,9 @@ export class ClientSession extends TypedEventEmitter<ClientSessionEvents> {
|
|
|
282
283
|
done();
|
|
283
284
|
};
|
|
284
285
|
|
|
285
|
-
if (this.
|
|
286
|
+
if (this.inTransaction()) {
|
|
287
|
+
// If we've reached endSession and the transaction is still active
|
|
288
|
+
// by default we abort it
|
|
286
289
|
this.abortTransaction(err => {
|
|
287
290
|
if (err) return done(err);
|
|
288
291
|
completeEndSession();
|
|
@@ -356,12 +359,16 @@ export class ClientSession extends TypedEventEmitter<ClientSessionEvents> {
|
|
|
356
359
|
return this.id.id.buffer.equals(session.id.id.buffer);
|
|
357
360
|
}
|
|
358
361
|
|
|
359
|
-
/**
|
|
362
|
+
/**
|
|
363
|
+
* Increment the transaction number on the internal ServerSession
|
|
364
|
+
*
|
|
365
|
+
* @privateRemarks
|
|
366
|
+
* This helper increments a value stored on the client session that will be
|
|
367
|
+
* added to the serverSession's txnNumber upon applying it to a command.
|
|
368
|
+
* This is because the serverSession is lazily acquired after a connection is obtained
|
|
369
|
+
*/
|
|
360
370
|
incrementTransactionNumber(): void {
|
|
361
|
-
|
|
362
|
-
this.serverSession.txnNumber =
|
|
363
|
-
typeof this.serverSession.txnNumber === 'number' ? this.serverSession.txnNumber + 1 : 0;
|
|
364
|
-
}
|
|
371
|
+
this[kTxnNumberIncrement] += 1;
|
|
365
372
|
}
|
|
366
373
|
|
|
367
374
|
/** @returns whether this session is currently in a transaction or not */
|
|
@@ -379,7 +386,6 @@ export class ClientSession extends TypedEventEmitter<ClientSessionEvents> {
|
|
|
379
386
|
throw new MongoCompatibilityError('Transactions are not allowed with snapshot sessions');
|
|
380
387
|
}
|
|
381
388
|
|
|
382
|
-
assertAlive(this);
|
|
383
389
|
if (this.inTransaction()) {
|
|
384
390
|
throw new MongoTransactionError('Transaction already in progress');
|
|
385
391
|
}
|
|
@@ -507,7 +513,7 @@ export function maybeClearPinnedConnection(
|
|
|
507
513
|
session.inTransaction() &&
|
|
508
514
|
error &&
|
|
509
515
|
error instanceof MongoError &&
|
|
510
|
-
error.hasErrorLabel(
|
|
516
|
+
error.hasErrorLabel(MongoErrorLabel.TransientTransactionError)
|
|
511
517
|
) {
|
|
512
518
|
return;
|
|
513
519
|
}
|
|
@@ -559,11 +565,11 @@ function attemptTransactionCommit<T>(
|
|
|
559
565
|
hasNotTimedOut(startTime, MAX_WITH_TRANSACTION_TIMEOUT) &&
|
|
560
566
|
!isMaxTimeMSExpiredError(err)
|
|
561
567
|
) {
|
|
562
|
-
if (err.hasErrorLabel(
|
|
568
|
+
if (err.hasErrorLabel(MongoErrorLabel.UnknownTransactionCommitResult)) {
|
|
563
569
|
return attemptTransactionCommit(session, startTime, fn, options);
|
|
564
570
|
}
|
|
565
571
|
|
|
566
|
-
if (err.hasErrorLabel(
|
|
572
|
+
if (err.hasErrorLabel(MongoErrorLabel.TransientTransactionError)) {
|
|
567
573
|
return attemptTransaction(session, startTime, fn, options);
|
|
568
574
|
}
|
|
569
575
|
}
|
|
@@ -617,20 +623,20 @@ function attemptTransaction<TSchema>(
|
|
|
617
623
|
function maybeRetryOrThrow(err: MongoError): Promise<any> {
|
|
618
624
|
if (
|
|
619
625
|
err instanceof MongoError &&
|
|
620
|
-
err.hasErrorLabel(
|
|
626
|
+
err.hasErrorLabel(MongoErrorLabel.TransientTransactionError) &&
|
|
621
627
|
hasNotTimedOut(startTime, MAX_WITH_TRANSACTION_TIMEOUT)
|
|
622
628
|
) {
|
|
623
629
|
return attemptTransaction(session, startTime, fn, options);
|
|
624
630
|
}
|
|
625
631
|
|
|
626
632
|
if (isMaxTimeMSExpiredError(err)) {
|
|
627
|
-
err.addErrorLabel(
|
|
633
|
+
err.addErrorLabel(MongoErrorLabel.UnknownTransactionCommitResult);
|
|
628
634
|
}
|
|
629
635
|
|
|
630
636
|
throw err;
|
|
631
637
|
}
|
|
632
638
|
|
|
633
|
-
if (session.
|
|
639
|
+
if (session.inTransaction()) {
|
|
634
640
|
return session.abortTransaction().then(() => maybeRetryOrThrow(err));
|
|
635
641
|
}
|
|
636
642
|
|
|
@@ -639,12 +645,11 @@ function attemptTransaction<TSchema>(
|
|
|
639
645
|
);
|
|
640
646
|
}
|
|
641
647
|
|
|
642
|
-
function endTransaction(
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
+
function endTransaction(
|
|
649
|
+
session: ClientSession,
|
|
650
|
+
commandName: 'abortTransaction' | 'commitTransaction',
|
|
651
|
+
callback: Callback<Document>
|
|
652
|
+
) {
|
|
648
653
|
// handle any initial problematic cases
|
|
649
654
|
const txnState = session.transaction.state;
|
|
650
655
|
|
|
@@ -717,7 +722,7 @@ function endTransaction(session: ClientSession, commandName: string, callback: C
|
|
|
717
722
|
Object.assign(command, { maxTimeMS: session.transaction.options.maxTimeMS });
|
|
718
723
|
}
|
|
719
724
|
|
|
720
|
-
function commandHandler(
|
|
725
|
+
function commandHandler(error?: Error, result?: Document) {
|
|
721
726
|
if (commandName !== 'commitTransaction') {
|
|
722
727
|
session.transaction.transition(TxnState.TRANSACTION_ABORTED);
|
|
723
728
|
if (session.loadBalanced) {
|
|
@@ -729,47 +734,45 @@ function endTransaction(session: ClientSession, commandName: string, callback: C
|
|
|
729
734
|
}
|
|
730
735
|
|
|
731
736
|
session.transaction.transition(TxnState.TRANSACTION_COMMITTED);
|
|
732
|
-
if (
|
|
737
|
+
if (error instanceof MongoError) {
|
|
733
738
|
if (
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
isMaxTimeMSExpiredError(e)
|
|
739
|
+
error.hasErrorLabel(MongoErrorLabel.RetryableWriteError) ||
|
|
740
|
+
error instanceof MongoWriteConcernError ||
|
|
741
|
+
isMaxTimeMSExpiredError(error)
|
|
738
742
|
) {
|
|
739
|
-
if (isUnknownTransactionCommitResult(
|
|
740
|
-
|
|
743
|
+
if (isUnknownTransactionCommitResult(error)) {
|
|
744
|
+
error.addErrorLabel(MongoErrorLabel.UnknownTransactionCommitResult);
|
|
741
745
|
|
|
742
746
|
// per txns spec, must unpin session in this case
|
|
743
|
-
session.unpin({ error
|
|
747
|
+
session.unpin({ error });
|
|
744
748
|
}
|
|
745
|
-
} else if (
|
|
746
|
-
session.unpin({ error
|
|
749
|
+
} else if (error.hasErrorLabel(MongoErrorLabel.TransientTransactionError)) {
|
|
750
|
+
session.unpin({ error });
|
|
747
751
|
}
|
|
748
752
|
}
|
|
749
753
|
|
|
750
|
-
callback(
|
|
754
|
+
callback(error, result);
|
|
751
755
|
}
|
|
752
756
|
|
|
753
|
-
// Assumption here that commandName is "commitTransaction" or "abortTransaction"
|
|
754
757
|
if (session.transaction.recoveryToken) {
|
|
755
758
|
command.recoveryToken = session.transaction.recoveryToken;
|
|
756
759
|
}
|
|
757
760
|
|
|
758
761
|
// send the command
|
|
759
762
|
executeOperation(
|
|
760
|
-
session
|
|
763
|
+
session,
|
|
761
764
|
new RunAdminCommandOperation(undefined, command, {
|
|
762
765
|
session,
|
|
763
766
|
readPreference: ReadPreference.primary,
|
|
764
767
|
bypassPinningCheck: true
|
|
765
768
|
}),
|
|
766
|
-
(
|
|
769
|
+
(error, result) => {
|
|
767
770
|
if (command.abortTransaction) {
|
|
768
771
|
// always unpin on abort regardless of command outcome
|
|
769
772
|
session.unpin();
|
|
770
773
|
}
|
|
771
774
|
|
|
772
|
-
if (
|
|
775
|
+
if (error instanceof MongoError && error.hasErrorLabel(MongoErrorLabel.RetryableWriteError)) {
|
|
773
776
|
// SPEC-1185: apply majority write concern when retrying commitTransaction
|
|
774
777
|
if (command.commitTransaction) {
|
|
775
778
|
// per txns spec, must unpin session in this case
|
|
@@ -781,17 +784,17 @@ function endTransaction(session: ClientSession, commandName: string, callback: C
|
|
|
781
784
|
}
|
|
782
785
|
|
|
783
786
|
return executeOperation(
|
|
784
|
-
session
|
|
787
|
+
session,
|
|
785
788
|
new RunAdminCommandOperation(undefined, command, {
|
|
786
789
|
session,
|
|
787
790
|
readPreference: ReadPreference.primary,
|
|
788
791
|
bypassPinningCheck: true
|
|
789
792
|
}),
|
|
790
|
-
|
|
793
|
+
commandHandler
|
|
791
794
|
);
|
|
792
795
|
}
|
|
793
796
|
|
|
794
|
-
commandHandler(
|
|
797
|
+
commandHandler(error, result);
|
|
795
798
|
}
|
|
796
799
|
);
|
|
797
800
|
}
|
|
@@ -832,6 +835,30 @@ export class ServerSession {
|
|
|
832
835
|
|
|
833
836
|
return idleTimeMinutes > sessionTimeoutMinutes - 1;
|
|
834
837
|
}
|
|
838
|
+
|
|
839
|
+
/**
|
|
840
|
+
* @internal
|
|
841
|
+
* Cloning meant to keep a readable reference to the server session data
|
|
842
|
+
* after ClientSession has ended
|
|
843
|
+
*/
|
|
844
|
+
static clone(serverSession: ServerSession): Readonly<ServerSession> {
|
|
845
|
+
const arrayBuffer = new ArrayBuffer(16);
|
|
846
|
+
const idBytes = Buffer.from(arrayBuffer);
|
|
847
|
+
idBytes.set(serverSession.id.id.buffer);
|
|
848
|
+
|
|
849
|
+
const id = new Binary(idBytes, serverSession.id.id.sub_type);
|
|
850
|
+
|
|
851
|
+
// Manual prototype construction to avoid modifying the constructor of this class
|
|
852
|
+
return Object.setPrototypeOf(
|
|
853
|
+
{
|
|
854
|
+
id: { id },
|
|
855
|
+
lastUse: serverSession.lastUse,
|
|
856
|
+
txnNumber: serverSession.txnNumber,
|
|
857
|
+
isDirty: serverSession.isDirty
|
|
858
|
+
},
|
|
859
|
+
ServerSession.prototype
|
|
860
|
+
);
|
|
861
|
+
}
|
|
835
862
|
}
|
|
836
863
|
|
|
837
864
|
/**
|
|
@@ -936,26 +963,27 @@ export class ServerSessionPool {
|
|
|
936
963
|
* @param session - the session tracking transaction state
|
|
937
964
|
* @param command - the command to decorate
|
|
938
965
|
* @param options - Optional settings passed to calling operation
|
|
966
|
+
*
|
|
967
|
+
* @internal
|
|
939
968
|
*/
|
|
940
969
|
export function applySession(
|
|
941
970
|
session: ClientSession,
|
|
942
971
|
command: Document,
|
|
943
|
-
options
|
|
972
|
+
options: CommandOptions
|
|
944
973
|
): MongoDriverError | undefined {
|
|
945
|
-
// TODO: merge this with `assertAlive`, did not want to throw a try/catch here
|
|
946
974
|
if (session.hasEnded) {
|
|
947
975
|
return new MongoExpiredSessionError();
|
|
948
976
|
}
|
|
949
977
|
|
|
978
|
+
// May acquire serverSession here
|
|
950
979
|
const serverSession = session.serverSession;
|
|
951
980
|
if (serverSession == null) {
|
|
952
981
|
return new MongoRuntimeError('Unable to acquire server session');
|
|
953
982
|
}
|
|
954
983
|
|
|
955
|
-
|
|
956
|
-
// FIXME: NODE-2781, this check for write concern shouldn't be happening here, but instead during command construction
|
|
957
|
-
if (options && options.writeConcern && (options.writeConcern as WriteConcern).w === 0) {
|
|
984
|
+
if (options.writeConcern?.w === 0) {
|
|
958
985
|
if (session && session.explicit) {
|
|
986
|
+
// Error if user provided an explicit session to an unacknowledged write (SPEC-1019)
|
|
959
987
|
return new MongoAPIError('Cannot have explicit session with unacknowledged writes');
|
|
960
988
|
}
|
|
961
989
|
return;
|
|
@@ -965,15 +993,16 @@ export function applySession(
|
|
|
965
993
|
serverSession.lastUse = now();
|
|
966
994
|
command.lsid = serverSession.id;
|
|
967
995
|
|
|
968
|
-
|
|
969
|
-
const
|
|
970
|
-
const isRetryableWrite = options?.willRetryWrite || false;
|
|
996
|
+
const inTxnOrTxnCommand = session.inTransaction() || isTransactionCommand(command);
|
|
997
|
+
const isRetryableWrite = !!options.willRetryWrite;
|
|
971
998
|
|
|
972
|
-
if (
|
|
999
|
+
if (isRetryableWrite || inTxnOrTxnCommand) {
|
|
1000
|
+
serverSession.txnNumber += session[kTxnNumberIncrement];
|
|
1001
|
+
session[kTxnNumberIncrement] = 0;
|
|
973
1002
|
command.txnNumber = Long.fromNumber(serverSession.txnNumber);
|
|
974
1003
|
}
|
|
975
1004
|
|
|
976
|
-
if (!
|
|
1005
|
+
if (!inTxnOrTxnCommand) {
|
|
977
1006
|
if (session.transaction.state !== TxnState.NO_TRANSACTION) {
|
|
978
1007
|
session.transaction.transition(TxnState.NO_TRANSACTION);
|
|
979
1008
|
}
|
|
@@ -1015,6 +1044,7 @@ export function applySession(
|
|
|
1015
1044
|
Object.assign(command.readConcern, { afterClusterTime: session.operationTime });
|
|
1016
1045
|
}
|
|
1017
1046
|
}
|
|
1047
|
+
return;
|
|
1018
1048
|
}
|
|
1019
1049
|
|
|
1020
1050
|
export function updateSessionFromResponse(session: ClientSession, document: Document): void {
|
package/src/utils.ts
CHANGED
|
@@ -8,11 +8,12 @@ import type { Connection } from './cmap/connection';
|
|
|
8
8
|
import { MAX_SUPPORTED_WIRE_VERSION } from './cmap/wire_protocol/constants';
|
|
9
9
|
import type { Collection } from './collection';
|
|
10
10
|
import { LEGACY_HELLO_COMMAND } from './constants';
|
|
11
|
+
import type { AbstractCursor } from './cursor/abstract_cursor';
|
|
12
|
+
import type { FindCursor } from './cursor/find_cursor';
|
|
11
13
|
import type { Db } from './db';
|
|
12
14
|
import {
|
|
13
15
|
AnyError,
|
|
14
16
|
MongoCompatibilityError,
|
|
15
|
-
MongoExpiredSessionError,
|
|
16
17
|
MongoInvalidArgumentError,
|
|
17
18
|
MongoNotConnectedError,
|
|
18
19
|
MongoParseError,
|
|
@@ -37,8 +38,6 @@ import { W, WriteConcern, WriteConcernOptions } from './write_concern';
|
|
|
37
38
|
* @public
|
|
38
39
|
*/
|
|
39
40
|
export type Callback<T = any> = (error?: AnyError, result?: T) => void;
|
|
40
|
-
/** @public */
|
|
41
|
-
export type CallbackWithType<E = AnyError, T0 = any> = (error?: E, result?: T0) => void;
|
|
42
41
|
|
|
43
42
|
export const MAX_JS_INT = Number.MAX_SAFE_INTEGER + 1;
|
|
44
43
|
|
|
@@ -177,7 +176,7 @@ export function mergeOptions<T, S>(target: T, source: S): T & S {
|
|
|
177
176
|
}
|
|
178
177
|
|
|
179
178
|
/** @internal */
|
|
180
|
-
export function filterOptions(options: AnyOptions, names: string
|
|
179
|
+
export function filterOptions(options: AnyOptions, names: ReadonlyArray<string>): AnyOptions {
|
|
181
180
|
const filterOptions: AnyOptions = {};
|
|
182
181
|
|
|
183
182
|
for (const name in options) {
|
|
@@ -190,110 +189,6 @@ export function filterOptions(options: AnyOptions, names: string[]): AnyOptions
|
|
|
190
189
|
return filterOptions;
|
|
191
190
|
}
|
|
192
191
|
|
|
193
|
-
/**
|
|
194
|
-
* Executes the given operation with provided arguments.
|
|
195
|
-
*
|
|
196
|
-
* @remarks
|
|
197
|
-
* This method reduces large amounts of duplication in the entire codebase by providing
|
|
198
|
-
* a single point for determining whether callbacks or promises should be used. Additionally
|
|
199
|
-
* it allows for a single point of entry to provide features such as implicit sessions, which
|
|
200
|
-
* are required by the Driver Sessions specification in the event that a ClientSession is
|
|
201
|
-
* not provided
|
|
202
|
-
*
|
|
203
|
-
* @internal
|
|
204
|
-
*
|
|
205
|
-
* @param topology - The topology to execute this operation on
|
|
206
|
-
* @param operation - The operation to execute
|
|
207
|
-
* @param args - Arguments to apply the provided operation
|
|
208
|
-
* @param options - Options that modify the behavior of the method
|
|
209
|
-
*/
|
|
210
|
-
export function executeLegacyOperation(
|
|
211
|
-
topology: Topology,
|
|
212
|
-
operation: (...args: any[]) => void | Promise<Document>,
|
|
213
|
-
args: any[],
|
|
214
|
-
options?: AnyOptions
|
|
215
|
-
): void | Promise<any> {
|
|
216
|
-
const Promise = PromiseProvider.get();
|
|
217
|
-
|
|
218
|
-
if (!Array.isArray(args)) {
|
|
219
|
-
// TODO(NODE-3483)
|
|
220
|
-
throw new MongoRuntimeError('This method requires an array of arguments to apply');
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
options = options ?? {};
|
|
224
|
-
|
|
225
|
-
let callback = args[args.length - 1];
|
|
226
|
-
|
|
227
|
-
// The driver sessions spec mandates that we implicitly create sessions for operations
|
|
228
|
-
// that are not explicitly provided with a session.
|
|
229
|
-
let session: ClientSession;
|
|
230
|
-
let opOptions: any;
|
|
231
|
-
let owner: any;
|
|
232
|
-
if (!options.skipSessions && topology.hasSessionSupport()) {
|
|
233
|
-
opOptions = args[args.length - 2];
|
|
234
|
-
if (opOptions == null || opOptions.session == null) {
|
|
235
|
-
owner = Symbol();
|
|
236
|
-
session = topology.startSession({ owner });
|
|
237
|
-
const optionsIndex = args.length - 2;
|
|
238
|
-
args[optionsIndex] = Object.assign({}, args[optionsIndex], { session: session });
|
|
239
|
-
} else if (opOptions.session && opOptions.session.hasEnded) {
|
|
240
|
-
throw new MongoExpiredSessionError();
|
|
241
|
-
}
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
function makeExecuteCallback(
|
|
245
|
-
resolve: (value?: Document) => void,
|
|
246
|
-
reject: (reason?: AnyError) => void
|
|
247
|
-
) {
|
|
248
|
-
return function (err?: AnyError, result?: any) {
|
|
249
|
-
if (session && session.owner === owner && !options?.returnsCursor) {
|
|
250
|
-
session.endSession(() => {
|
|
251
|
-
delete opOptions.session;
|
|
252
|
-
if (err) return reject(err);
|
|
253
|
-
resolve(result);
|
|
254
|
-
});
|
|
255
|
-
} else {
|
|
256
|
-
if (err) return reject(err);
|
|
257
|
-
resolve(result);
|
|
258
|
-
}
|
|
259
|
-
};
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
// Execute using callback
|
|
263
|
-
if (typeof callback === 'function') {
|
|
264
|
-
callback = args.pop();
|
|
265
|
-
const handler = makeExecuteCallback(
|
|
266
|
-
result => callback(undefined, result),
|
|
267
|
-
err => callback(err, null)
|
|
268
|
-
);
|
|
269
|
-
args.push(handler);
|
|
270
|
-
|
|
271
|
-
try {
|
|
272
|
-
return operation(...args);
|
|
273
|
-
} catch (e) {
|
|
274
|
-
handler(e);
|
|
275
|
-
throw e;
|
|
276
|
-
}
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
// Return a Promise
|
|
280
|
-
if (args[args.length - 1] != null) {
|
|
281
|
-
// TODO(NODE-3483)
|
|
282
|
-
throw new MongoRuntimeError('Final argument to `executeLegacyOperation` must be a callback');
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
return new Promise<any>((resolve, reject) => {
|
|
286
|
-
const handler = makeExecuteCallback(resolve, reject);
|
|
287
|
-
args[args.length - 1] = handler;
|
|
288
|
-
|
|
289
|
-
try {
|
|
290
|
-
return operation(...args);
|
|
291
|
-
} catch (e) {
|
|
292
|
-
handler(e);
|
|
293
|
-
}
|
|
294
|
-
});
|
|
295
|
-
}
|
|
296
|
-
|
|
297
192
|
interface HasRetryableWrites {
|
|
298
193
|
retryWrites?: boolean;
|
|
299
194
|
}
|
|
@@ -434,17 +329,30 @@ export function decorateWithExplain(command: Document, explain: Explain): Docume
|
|
|
434
329
|
return { explain: command, verbosity: explain.verbosity };
|
|
435
330
|
}
|
|
436
331
|
|
|
332
|
+
/**
|
|
333
|
+
* @internal
|
|
334
|
+
*/
|
|
335
|
+
export type TopologyProvider =
|
|
336
|
+
| MongoClient
|
|
337
|
+
| ClientSession
|
|
338
|
+
| FindCursor
|
|
339
|
+
| AbstractCursor
|
|
340
|
+
| Collection<any>
|
|
341
|
+
| Db;
|
|
342
|
+
|
|
437
343
|
/**
|
|
438
344
|
* A helper function to get the topology from a given provider. Throws
|
|
439
345
|
* if the topology cannot be found.
|
|
346
|
+
* @throws MongoNotConnectedError
|
|
440
347
|
* @internal
|
|
441
348
|
*/
|
|
442
|
-
export function getTopology
|
|
349
|
+
export function getTopology(provider: TopologyProvider): Topology {
|
|
350
|
+
// MongoClient or ClientSession or AbstractCursor
|
|
443
351
|
if (`topology` in provider && provider.topology) {
|
|
444
352
|
return provider.topology;
|
|
445
|
-
} else if ('client' in provider.s && provider.s.client.topology) {
|
|
353
|
+
} else if ('s' in provider && 'client' in provider.s && provider.s.client.topology) {
|
|
446
354
|
return provider.s.client.topology;
|
|
447
|
-
} else if ('db' in provider.s && provider.s.db.s.client.topology) {
|
|
355
|
+
} else if ('s' in provider && 'db' in provider.s && provider.s.db.s.client.topology) {
|
|
448
356
|
return provider.s.db.s.client.topology;
|
|
449
357
|
}
|
|
450
358
|
|
|
@@ -1181,7 +1089,7 @@ export function deepCopy<T>(value: T): T {
|
|
|
1181
1089
|
case 'set':
|
|
1182
1090
|
return new Set(value as any) as unknown as T;
|
|
1183
1091
|
case 'buffer':
|
|
1184
|
-
return Buffer.from(value as Buffer) as unknown as T;
|
|
1092
|
+
return Buffer.from(value as unknown as Buffer) as unknown as T;
|
|
1185
1093
|
}
|
|
1186
1094
|
}
|
|
1187
1095
|
|
|
@@ -1419,13 +1327,25 @@ export function enumToString(en: Record<string, unknown>): string {
|
|
|
1419
1327
|
*
|
|
1420
1328
|
* @internal
|
|
1421
1329
|
*/
|
|
1422
|
-
export function supportsRetryableWrites(server
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1330
|
+
export function supportsRetryableWrites(server?: Server): boolean {
|
|
1331
|
+
if (!server) {
|
|
1332
|
+
return false;
|
|
1333
|
+
}
|
|
1334
|
+
|
|
1335
|
+
if (server.loadBalanced) {
|
|
1336
|
+
// Loadbalanced topologies will always support retry writes
|
|
1337
|
+
return true;
|
|
1338
|
+
}
|
|
1339
|
+
|
|
1340
|
+
if (server.description.logicalSessionTimeoutMinutes != null) {
|
|
1341
|
+
// that supports sessions
|
|
1342
|
+
if (server.description.type !== ServerType.Standalone) {
|
|
1343
|
+
// and that is not a standalone
|
|
1344
|
+
return true;
|
|
1345
|
+
}
|
|
1346
|
+
}
|
|
1347
|
+
|
|
1348
|
+
return false;
|
|
1429
1349
|
}
|
|
1430
1350
|
|
|
1431
1351
|
export function parsePackageVersion({ version }: { version: string }): {
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"allowJs": false,
|
|
4
|
+
"checkJs": false,
|
|
5
|
+
"strict": true,
|
|
6
|
+
"alwaysStrict": true,
|
|
7
|
+
"target": "ES2019",
|
|
8
|
+
"module": "commonJS",
|
|
9
|
+
"moduleResolution": "node",
|
|
10
|
+
"skipLibCheck": true,
|
|
11
|
+
"lib": ["es2020"],
|
|
12
|
+
// We don't make use of tslib helpers, all syntax used is supported by target engine
|
|
13
|
+
"importHelpers": false,
|
|
14
|
+
"noEmitHelpers": true,
|
|
15
|
+
// Never emit error filled code
|
|
16
|
+
"noEmitOnError": true,
|
|
17
|
+
"outDir": "lib",
|
|
18
|
+
"importsNotUsedAsValues": "error",
|
|
19
|
+
// We want the sourcemaps in a separate file
|
|
20
|
+
"inlineSourceMap": false,
|
|
21
|
+
"sourceMap": true,
|
|
22
|
+
// API-Extractor uses declaration maps to report problems in source, no need to distribute
|
|
23
|
+
"declaration": true,
|
|
24
|
+
"declarationMap": true,
|
|
25
|
+
// we include sources in the release
|
|
26
|
+
"inlineSources": false,
|
|
27
|
+
// Prevents web types from being suggested by vscode.
|
|
28
|
+
"types": ["node"],
|
|
29
|
+
"forceConsistentCasingInFileNames": true,
|
|
30
|
+
"noImplicitOverride": true,
|
|
31
|
+
"noImplicitReturns": true,
|
|
32
|
+
// TODO(NODE-3659): Enable useUnknownInCatchVariables and add type assertions or remove unnecessary catch blocks
|
|
33
|
+
"useUnknownInCatchVariables": false
|
|
34
|
+
},
|
|
35
|
+
"ts-node": {
|
|
36
|
+
"transpileOnly": true,
|
|
37
|
+
"compiler": "typescript-cached-transpile"
|
|
38
|
+
},
|
|
39
|
+
"include": ["src/**/*"]
|
|
40
|
+
}
|