mongodb 4.0.0 → 4.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +62 -30
- package/lib/bson.js +1 -0
- package/lib/bson.js.map +1 -1
- package/lib/bulk/common.js +53 -30
- package/lib/bulk/common.js.map +1 -1
- package/lib/bulk/ordered.js +3 -2
- package/lib/bulk/ordered.js.map +1 -1
- package/lib/bulk/unordered.js +3 -2
- package/lib/bulk/unordered.js.map +1 -1
- package/lib/change_stream.js +23 -13
- package/lib/change_stream.js.map +1 -1
- package/lib/cmap/auth/auth_provider.js +2 -1
- package/lib/cmap/auth/auth_provider.js.map +1 -1
- package/lib/cmap/auth/gssapi.js +5 -4
- package/lib/cmap/auth/gssapi.js.map +1 -1
- package/lib/cmap/auth/mongo_credentials.js +9 -5
- package/lib/cmap/auth/mongo_credentials.js.map +1 -1
- package/lib/cmap/auth/mongocr.js +2 -2
- package/lib/cmap/auth/mongocr.js.map +1 -1
- package/lib/cmap/auth/mongodb_aws.js +32 -32
- package/lib/cmap/auth/mongodb_aws.js.map +1 -1
- package/lib/cmap/auth/plain.js +1 -1
- package/lib/cmap/auth/plain.js.map +1 -1
- package/lib/cmap/auth/scram.js +15 -12
- package/lib/cmap/auth/scram.js.map +1 -1
- package/lib/cmap/auth/x509.js +2 -2
- package/lib/cmap/auth/x509.js.map +1 -1
- package/lib/cmap/command_monitoring_events.js +26 -10
- package/lib/cmap/command_monitoring_events.js.map +1 -1
- package/lib/cmap/commands.js +9 -5
- package/lib/cmap/commands.js.map +1 -1
- package/lib/cmap/connect.js +23 -9
- package/lib/cmap/connect.js.map +1 -1
- package/lib/cmap/connection.js +43 -46
- package/lib/cmap/connection.js.map +1 -1
- package/lib/cmap/connection_pool.js +113 -15
- package/lib/cmap/connection_pool.js.map +1 -1
- package/lib/cmap/connection_pool_events.js +3 -1
- package/lib/cmap/connection_pool_events.js.map +1 -1
- package/lib/cmap/errors.js +3 -3
- package/lib/cmap/errors.js.map +1 -1
- package/lib/cmap/message_stream.js +1 -1
- package/lib/cmap/message_stream.js.map +1 -1
- package/lib/cmap/metrics.js +62 -0
- package/lib/cmap/metrics.js.map +1 -0
- package/lib/cmap/stream_description.js +3 -1
- package/lib/cmap/stream_description.js.map +1 -1
- package/lib/cmap/wire_protocol/compression.js +22 -9
- package/lib/cmap/wire_protocol/compression.js.map +1 -1
- package/lib/cmap/wire_protocol/shared.js +1 -1
- package/lib/cmap/wire_protocol/shared.js.map +1 -1
- package/lib/collection.js +23 -18
- package/lib/collection.js.map +1 -1
- package/lib/connection_string.js +76 -30
- package/lib/connection_string.js.map +1 -1
- package/lib/cursor/abstract_cursor.js +75 -68
- package/lib/cursor/abstract_cursor.js.map +1 -1
- package/lib/cursor/aggregation_cursor.js +47 -9
- package/lib/cursor/aggregation_cursor.js.map +1 -1
- package/lib/cursor/find_cursor.js +53 -13
- 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 +16 -5
- package/lib/deps.js.map +1 -1
- package/lib/encrypter.js +5 -8
- package/lib/encrypter.js.map +1 -1
- package/lib/error.js +230 -34
- package/lib/error.js.map +1 -1
- package/lib/explain.js +2 -2
- package/lib/explain.js.map +1 -1
- package/lib/gridfs/download.js +22 -47
- package/lib/gridfs/download.js.map +1 -1
- package/lib/gridfs/index.js +4 -3
- package/lib/gridfs/index.js.map +1 -1
- package/lib/gridfs/upload.js +13 -21
- package/lib/gridfs/upload.js.map +1 -1
- package/lib/index.js +27 -2
- package/lib/index.js.map +1 -1
- package/lib/logger.js +3 -2
- package/lib/logger.js.map +1 -1
- package/lib/mongo_client.js +5 -8
- package/lib/mongo_client.js.map +1 -1
- package/lib/mongo_types.js.map +1 -1
- package/lib/operations/add_user.js +2 -3
- package/lib/operations/add_user.js.map +1 -1
- package/lib/operations/aggregate.js +12 -9
- package/lib/operations/aggregate.js.map +1 -1
- package/lib/operations/command.js +5 -7
- package/lib/operations/command.js.map +1 -1
- package/lib/operations/common_functions.js +1 -1
- package/lib/operations/common_functions.js.map +1 -1
- package/lib/operations/connect.js +3 -2
- package/lib/operations/connect.js.map +1 -1
- package/lib/operations/count.js +1 -1
- package/lib/operations/count.js.map +1 -1
- package/lib/operations/count_documents.js +1 -1
- package/lib/operations/count_documents.js.map +1 -1
- package/lib/operations/delete.js +5 -5
- package/lib/operations/delete.js.map +1 -1
- package/lib/operations/distinct.js +2 -2
- package/lib/operations/distinct.js.map +1 -1
- package/lib/operations/estimated_document_count.js +5 -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 +31 -17
- package/lib/operations/execute_operation.js.map +1 -1
- package/lib/operations/find.js +13 -9
- package/lib/operations/find.js.map +1 -1
- package/lib/operations/find_and_modify.js +9 -9
- package/lib/operations/find_and_modify.js.map +1 -1
- package/lib/operations/indexes.js +8 -3
- package/lib/operations/indexes.js.map +1 -1
- package/lib/operations/insert.js +5 -3
- package/lib/operations/insert.js.map +1 -1
- package/lib/operations/is_capped.js +2 -1
- package/lib/operations/is_capped.js.map +1 -1
- package/lib/operations/list_collections.js +6 -3
- package/lib/operations/list_collections.js.map +1 -1
- package/lib/operations/map_reduce.js +1 -1
- package/lib/operations/map_reduce.js.map +1 -1
- package/lib/operations/operation.js +3 -1
- package/lib/operations/operation.js.map +1 -1
- package/lib/operations/options_operation.js +2 -1
- package/lib/operations/options_operation.js.map +1 -1
- package/lib/operations/profiling_level.js +4 -2
- package/lib/operations/profiling_level.js.map +1 -1
- package/lib/operations/set_profiling_level.js +4 -2
- package/lib/operations/set_profiling_level.js.map +1 -1
- package/lib/operations/update.js +12 -12
- package/lib/operations/update.js.map +1 -1
- package/lib/operations/validate_collection.js +6 -5
- package/lib/operations/validate_collection.js.map +1 -1
- package/lib/promise_provider.js +1 -1
- package/lib/promise_provider.js.map +1 -1
- package/lib/read_preference.js +8 -8
- package/lib/read_preference.js.map +1 -1
- package/lib/sdam/common.js +12 -10
- package/lib/sdam/common.js.map +1 -1
- package/lib/sdam/server.js +90 -25
- package/lib/sdam/server.js.map +1 -1
- package/lib/sdam/server_description.js +9 -4
- package/lib/sdam/server_description.js.map +1 -1
- package/lib/sdam/server_selection.js +10 -4
- package/lib/sdam/server_selection.js.map +1 -1
- package/lib/sdam/srv_polling.js +1 -1
- package/lib/sdam/srv_polling.js.map +1 -1
- package/lib/sdam/topology.js +42 -21
- package/lib/sdam/topology.js.map +1 -1
- package/lib/sdam/topology_description.js +7 -3
- package/lib/sdam/topology_description.js.map +1 -1
- package/lib/sessions.js +132 -31
- package/lib/sessions.js.map +1 -1
- package/lib/sort.js +3 -3
- package/lib/sort.js.map +1 -1
- package/lib/transactions.js +15 -7
- package/lib/transactions.js.map +1 -1
- package/lib/utils.js +60 -20
- package/lib/utils.js.map +1 -1
- package/mongodb.d.ts +523 -138
- package/mongodb.ts34.d.ts +480 -141
- package/package.json +44 -48
- package/src/bson.ts +1 -0
- package/src/bulk/common.ts +83 -43
- package/src/bulk/ordered.ts +4 -3
- package/src/bulk/unordered.ts +4 -3
- package/src/change_stream.ts +46 -29
- package/src/cmap/auth/auth_provider.ts +3 -2
- package/src/cmap/auth/gssapi.ts +15 -5
- package/src/cmap/auth/mongo_credentials.ts +22 -8
- package/src/cmap/auth/mongocr.ts +3 -3
- package/src/cmap/auth/mongodb_aws.ts +52 -39
- package/src/cmap/auth/plain.ts +2 -2
- package/src/cmap/auth/scram.ts +23 -13
- package/src/cmap/auth/x509.ts +3 -3
- package/src/cmap/command_monitoring_events.ts +36 -14
- package/src/cmap/commands.ts +12 -6
- package/src/cmap/connect.ts +42 -12
- package/src/cmap/connection.ts +54 -62
- package/src/cmap/connection_pool.ts +141 -20
- package/src/cmap/connection_pool_events.ts +8 -1
- package/src/cmap/errors.ts +3 -4
- package/src/cmap/message_stream.ts +2 -4
- package/src/cmap/metrics.ts +58 -0
- package/src/cmap/stream_description.ts +6 -1
- package/src/cmap/wire_protocol/compression.ts +26 -13
- package/src/cmap/wire_protocol/shared.ts +4 -2
- package/src/collection.ts +75 -70
- package/src/connection_string.ts +97 -34
- package/src/cursor/abstract_cursor.ts +141 -104
- package/src/cursor/aggregation_cursor.ts +34 -20
- package/src/cursor/find_cursor.ts +41 -21
- package/src/db.ts +19 -18
- package/src/deps.ts +110 -22
- package/src/encrypter.ts +6 -12
- package/src/error.ts +264 -48
- package/src/explain.ts +3 -3
- package/src/gridfs/download.ts +48 -53
- package/src/gridfs/index.ts +5 -4
- package/src/gridfs/upload.ts +32 -33
- package/src/index.ts +42 -4
- package/src/logger.ts +6 -3
- package/src/mongo_client.ts +20 -23
- package/src/mongo_types.ts +19 -20
- package/src/operations/add_user.ts +4 -5
- package/src/operations/aggregate.ts +18 -17
- package/src/operations/command.ts +7 -10
- package/src/operations/common_functions.ts +2 -3
- package/src/operations/connect.ts +4 -3
- package/src/operations/count.ts +2 -2
- package/src/operations/count_documents.ts +2 -2
- package/src/operations/delete.ts +8 -6
- package/src/operations/distinct.ts +5 -3
- package/src/operations/estimated_document_count.ts +5 -1
- package/src/operations/eval.ts +1 -1
- package/src/operations/execute_operation.ts +41 -20
- package/src/operations/find.ts +25 -16
- package/src/operations/find_and_modify.ts +12 -10
- package/src/operations/indexes.ts +39 -8
- package/src/operations/insert.ts +7 -4
- package/src/operations/is_capped.ts +3 -2
- package/src/operations/list_collections.ts +9 -6
- package/src/operations/map_reduce.ts +4 -2
- package/src/operations/operation.ts +7 -2
- package/src/operations/options_operation.ts +3 -2
- package/src/operations/profiling_level.ts +5 -3
- package/src/operations/set_profiling_level.ts +9 -3
- package/src/operations/update.ts +17 -13
- package/src/operations/validate_collection.ts +7 -6
- package/src/promise_provider.ts +2 -2
- package/src/read_preference.ts +11 -9
- package/src/sdam/common.ts +11 -9
- package/src/sdam/server.ts +168 -69
- package/src/sdam/server_description.ts +16 -4
- package/src/sdam/server_selection.ts +15 -7
- package/src/sdam/srv_polling.ts +2 -2
- package/src/sdam/topology.ts +67 -36
- package/src/sdam/topology_description.ts +11 -4
- package/src/sessions.ts +194 -37
- package/src/sort.ts +6 -4
- package/src/transactions.ts +18 -9
- package/src/utils.ts +73 -20
- package/HISTORY.md +0 -2993
- package/lib/operations/find_one.js +0 -34
- package/lib/operations/find_one.js.map +0 -1
- package/src/operations/find_one.ts +0 -43
package/src/sessions.ts
CHANGED
|
@@ -2,16 +2,23 @@ import { PromiseProvider } from './promise_provider';
|
|
|
2
2
|
import { Binary, Long, Timestamp, Document } from './bson';
|
|
3
3
|
import { ReadPreference } from './read_preference';
|
|
4
4
|
import { isTransactionCommand, TxnState, Transaction, TransactionOptions } from './transactions';
|
|
5
|
-
import {
|
|
5
|
+
import { _advanceClusterTime, ClusterTime, TopologyType } from './sdam/common';
|
|
6
6
|
import { isSharded } from './cmap/wire_protocol/shared';
|
|
7
7
|
import {
|
|
8
8
|
MongoError,
|
|
9
|
+
MongoInvalidArgumentError,
|
|
9
10
|
isRetryableError,
|
|
11
|
+
MongoCompatibilityError,
|
|
10
12
|
MongoNetworkError,
|
|
11
13
|
MongoWriteConcernError,
|
|
12
14
|
MONGODB_ERROR_CODES,
|
|
15
|
+
MongoServerError,
|
|
13
16
|
MongoDriverError,
|
|
14
|
-
|
|
17
|
+
MongoAPIError,
|
|
18
|
+
AnyError,
|
|
19
|
+
MongoExpiredSessionError,
|
|
20
|
+
MongoTransactionError,
|
|
21
|
+
MongoRuntimeError
|
|
15
22
|
} from './error';
|
|
16
23
|
import {
|
|
17
24
|
now,
|
|
@@ -28,6 +35,8 @@ import { executeOperation } from './operations/execute_operation';
|
|
|
28
35
|
import { RunAdminCommandOperation } from './operations/run_command';
|
|
29
36
|
import type { AbstractCursor } from './cursor/abstract_cursor';
|
|
30
37
|
import type { CommandOptions } from './cmap/connection';
|
|
38
|
+
import { Connection } from './cmap/connection';
|
|
39
|
+
import { ConnectionPoolMetrics } from './cmap/metrics';
|
|
31
40
|
import type { WriteConcern } from './write_concern';
|
|
32
41
|
import { TypedEventEmitter } from './mongo_types';
|
|
33
42
|
import { ReadConcernLevel } from './read_concern';
|
|
@@ -36,7 +45,7 @@ const minWireVersionForShardedTransactions = 8;
|
|
|
36
45
|
|
|
37
46
|
function assertAlive(session: ClientSession, callback?: Callback): boolean {
|
|
38
47
|
if (session.serverSession == null) {
|
|
39
|
-
const error = new
|
|
48
|
+
const error = new MongoExpiredSessionError();
|
|
40
49
|
if (typeof callback === 'function') {
|
|
41
50
|
callback(error);
|
|
42
51
|
return false;
|
|
@@ -79,6 +88,19 @@ const kServerSession = Symbol('serverSession');
|
|
|
79
88
|
const kSnapshotTime = Symbol('snapshotTime');
|
|
80
89
|
/** @internal */
|
|
81
90
|
const kSnapshotEnabled = Symbol('snapshotEnabled');
|
|
91
|
+
/** @internal */
|
|
92
|
+
const kPinnedConnection = Symbol('pinnedConnection');
|
|
93
|
+
|
|
94
|
+
/** @public */
|
|
95
|
+
export interface EndSessionOptions {
|
|
96
|
+
/**
|
|
97
|
+
* An optional error which caused the call to end this session
|
|
98
|
+
* @internal
|
|
99
|
+
*/
|
|
100
|
+
error?: AnyError;
|
|
101
|
+
force?: boolean;
|
|
102
|
+
forceClear?: boolean;
|
|
103
|
+
}
|
|
82
104
|
|
|
83
105
|
/**
|
|
84
106
|
* A class representing a client session on the server
|
|
@@ -107,6 +129,8 @@ export class ClientSession extends TypedEventEmitter<ClientSessionEvents> {
|
|
|
107
129
|
[kSnapshotTime]?: Timestamp;
|
|
108
130
|
/** @internal */
|
|
109
131
|
[kSnapshotEnabled] = false;
|
|
132
|
+
/** @internal */
|
|
133
|
+
[kPinnedConnection]?: Connection;
|
|
110
134
|
|
|
111
135
|
/**
|
|
112
136
|
* Create a client session.
|
|
@@ -125,11 +149,13 @@ export class ClientSession extends TypedEventEmitter<ClientSessionEvents> {
|
|
|
125
149
|
super();
|
|
126
150
|
|
|
127
151
|
if (topology == null) {
|
|
128
|
-
|
|
152
|
+
// TODO(NODE-3483)
|
|
153
|
+
throw new MongoRuntimeError('ClientSession requires a topology');
|
|
129
154
|
}
|
|
130
155
|
|
|
131
156
|
if (sessionPool == null || !(sessionPool instanceof ServerSessionPool)) {
|
|
132
|
-
|
|
157
|
+
// TODO(NODE-3483)
|
|
158
|
+
throw new MongoRuntimeError('ClientSession requires a ServerSessionPool');
|
|
133
159
|
}
|
|
134
160
|
|
|
135
161
|
options = options ?? {};
|
|
@@ -137,7 +163,7 @@ export class ClientSession extends TypedEventEmitter<ClientSessionEvents> {
|
|
|
137
163
|
if (options.snapshot === true) {
|
|
138
164
|
this[kSnapshotEnabled] = true;
|
|
139
165
|
if (options.causalConsistency === true) {
|
|
140
|
-
throw new
|
|
166
|
+
throw new MongoInvalidArgumentError(
|
|
141
167
|
'Properties "causalConsistency" and "snapshot" are mutually exclusive'
|
|
142
168
|
);
|
|
143
169
|
}
|
|
@@ -181,6 +207,41 @@ export class ClientSession extends TypedEventEmitter<ClientSessionEvents> {
|
|
|
181
207
|
return this[kSnapshotEnabled];
|
|
182
208
|
}
|
|
183
209
|
|
|
210
|
+
get loadBalanced(): boolean {
|
|
211
|
+
return this.topology.description.type === TopologyType.LoadBalanced;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/** @internal */
|
|
215
|
+
get pinnedConnection(): Connection | undefined {
|
|
216
|
+
return this[kPinnedConnection];
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/** @internal */
|
|
220
|
+
pin(conn: Connection): void {
|
|
221
|
+
if (this[kPinnedConnection]) {
|
|
222
|
+
throw TypeError('Cannot pin multiple connections to the same session');
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
this[kPinnedConnection] = conn;
|
|
226
|
+
conn.emit(
|
|
227
|
+
Connection.PINNED,
|
|
228
|
+
this.inTransaction() ? ConnectionPoolMetrics.TXN : ConnectionPoolMetrics.CURSOR
|
|
229
|
+
);
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
/** @internal */
|
|
233
|
+
unpin(options?: { force?: boolean; forceClear?: boolean; error?: AnyError }): void {
|
|
234
|
+
if (this.loadBalanced) {
|
|
235
|
+
return maybeClearPinnedConnection(this, options);
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
this.transaction.unpinServer();
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
get isPinned(): boolean {
|
|
242
|
+
return this.loadBalanced ? !!this[kPinnedConnection] : this.transaction.isPinned;
|
|
243
|
+
}
|
|
244
|
+
|
|
184
245
|
/**
|
|
185
246
|
* Ends this session on the server
|
|
186
247
|
*
|
|
@@ -189,21 +250,24 @@ export class ClientSession extends TypedEventEmitter<ClientSessionEvents> {
|
|
|
189
250
|
*/
|
|
190
251
|
endSession(): Promise<void>;
|
|
191
252
|
endSession(callback: Callback<void>): void;
|
|
192
|
-
endSession(options:
|
|
193
|
-
endSession(options:
|
|
253
|
+
endSession(options: EndSessionOptions): Promise<void>;
|
|
254
|
+
endSession(options: EndSessionOptions, callback: Callback<void>): void;
|
|
194
255
|
endSession(
|
|
195
|
-
options?:
|
|
256
|
+
options?: EndSessionOptions | Callback<void>,
|
|
196
257
|
callback?: Callback<void>
|
|
197
258
|
): void | Promise<void> {
|
|
198
259
|
if (typeof options === 'function') (callback = options), (options = {});
|
|
199
|
-
|
|
260
|
+
const finalOptions = { force: true, ...options };
|
|
200
261
|
|
|
201
262
|
return maybePromise(callback, done => {
|
|
202
263
|
if (this.hasEnded) {
|
|
264
|
+
maybeClearPinnedConnection(this, finalOptions);
|
|
203
265
|
return done();
|
|
204
266
|
}
|
|
205
267
|
|
|
206
268
|
const completeEndSession = () => {
|
|
269
|
+
maybeClearPinnedConnection(this, finalOptions);
|
|
270
|
+
|
|
207
271
|
// release the server session back to the pool
|
|
208
272
|
this.sessionPool.release(this.serverSession);
|
|
209
273
|
this[kServerSession] = undefined;
|
|
@@ -245,6 +309,34 @@ export class ClientSession extends TypedEventEmitter<ClientSessionEvents> {
|
|
|
245
309
|
}
|
|
246
310
|
}
|
|
247
311
|
|
|
312
|
+
/**
|
|
313
|
+
* Advances the clusterTime for a ClientSession to the provided clusterTime of another ClientSession
|
|
314
|
+
*
|
|
315
|
+
* @param clusterTime - the $clusterTime returned by the server from another session in the form of a document containing the `BSON.Timestamp` clusterTime and signature
|
|
316
|
+
*/
|
|
317
|
+
advanceClusterTime(clusterTime: ClusterTime): void {
|
|
318
|
+
if (!clusterTime || typeof clusterTime !== 'object') {
|
|
319
|
+
throw new MongoInvalidArgumentError('input cluster time must be an object');
|
|
320
|
+
}
|
|
321
|
+
if (!clusterTime.clusterTime || clusterTime.clusterTime._bsontype !== 'Timestamp') {
|
|
322
|
+
throw new MongoInvalidArgumentError(
|
|
323
|
+
'input cluster time "clusterTime" property must be a valid BSON Timestamp'
|
|
324
|
+
);
|
|
325
|
+
}
|
|
326
|
+
if (
|
|
327
|
+
!clusterTime.signature ||
|
|
328
|
+
clusterTime.signature.hash?._bsontype !== 'Binary' ||
|
|
329
|
+
(typeof clusterTime.signature.keyId !== 'number' &&
|
|
330
|
+
clusterTime.signature.keyId?._bsontype !== 'Long') // apparently we decode the key to number?
|
|
331
|
+
) {
|
|
332
|
+
throw new MongoInvalidArgumentError(
|
|
333
|
+
'input cluster time must have a valid "signature" property with BSON Binary hash and BSON Long keyId'
|
|
334
|
+
);
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
_advanceClusterTime(this, clusterTime);
|
|
338
|
+
}
|
|
339
|
+
|
|
248
340
|
/**
|
|
249
341
|
* Used to determine if this session equals another
|
|
250
342
|
*
|
|
@@ -282,12 +374,16 @@ export class ClientSession extends TypedEventEmitter<ClientSessionEvents> {
|
|
|
282
374
|
*/
|
|
283
375
|
startTransaction(options?: TransactionOptions): void {
|
|
284
376
|
if (this[kSnapshotEnabled]) {
|
|
285
|
-
throw new
|
|
377
|
+
throw new MongoCompatibilityError('Transactions are not allowed with snapshot sessions');
|
|
286
378
|
}
|
|
287
379
|
|
|
288
380
|
assertAlive(this);
|
|
289
381
|
if (this.inTransaction()) {
|
|
290
|
-
throw new
|
|
382
|
+
throw new MongoTransactionError('Transaction already in progress');
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
if (this.isPinned && this.transaction.isCommitted) {
|
|
386
|
+
this.unpin();
|
|
291
387
|
}
|
|
292
388
|
|
|
293
389
|
const topologyMaxWireVersion = maxWireVersion(this.topology);
|
|
@@ -296,7 +392,7 @@ export class ClientSession extends TypedEventEmitter<ClientSessionEvents> {
|
|
|
296
392
|
topologyMaxWireVersion != null &&
|
|
297
393
|
topologyMaxWireVersion < minWireVersionForShardedTransactions
|
|
298
394
|
) {
|
|
299
|
-
throw new
|
|
395
|
+
throw new MongoCompatibilityError(
|
|
300
396
|
'Transactions are not supported on sharded clusters in MongoDB < 4.2.'
|
|
301
397
|
);
|
|
302
398
|
}
|
|
@@ -349,7 +445,7 @@ export class ClientSession extends TypedEventEmitter<ClientSessionEvents> {
|
|
|
349
445
|
* This is here to ensure that ClientSession is never serialized to BSON.
|
|
350
446
|
*/
|
|
351
447
|
toBSON(): never {
|
|
352
|
-
throw new
|
|
448
|
+
throw new MongoRuntimeError('ClientSession cannot be serialized to BSON.');
|
|
353
449
|
}
|
|
354
450
|
|
|
355
451
|
/**
|
|
@@ -397,6 +493,47 @@ function isUnknownTransactionCommitResult(err: MongoError) {
|
|
|
397
493
|
);
|
|
398
494
|
}
|
|
399
495
|
|
|
496
|
+
export function maybeClearPinnedConnection(
|
|
497
|
+
session: ClientSession,
|
|
498
|
+
options?: EndSessionOptions
|
|
499
|
+
): void {
|
|
500
|
+
// unpin a connection if it has been pinned
|
|
501
|
+
const conn = session[kPinnedConnection];
|
|
502
|
+
const error = options?.error;
|
|
503
|
+
|
|
504
|
+
if (
|
|
505
|
+
session.inTransaction() &&
|
|
506
|
+
error &&
|
|
507
|
+
error instanceof MongoError &&
|
|
508
|
+
error.hasErrorLabel('TransientTransactionError')
|
|
509
|
+
) {
|
|
510
|
+
return;
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
// NOTE: the spec talks about what to do on a network error only, but the tests seem to
|
|
514
|
+
// to validate that we don't unpin on _all_ errors?
|
|
515
|
+
if (conn) {
|
|
516
|
+
const servers = Array.from(session.topology.s.servers.values());
|
|
517
|
+
const loadBalancer = servers[0];
|
|
518
|
+
|
|
519
|
+
if (options?.error == null || options?.force) {
|
|
520
|
+
loadBalancer.s.pool.checkIn(conn);
|
|
521
|
+
conn.emit(
|
|
522
|
+
Connection.UNPINNED,
|
|
523
|
+
session.transaction.state !== TxnState.NO_TRANSACTION
|
|
524
|
+
? ConnectionPoolMetrics.TXN
|
|
525
|
+
: ConnectionPoolMetrics.CURSOR
|
|
526
|
+
);
|
|
527
|
+
|
|
528
|
+
if (options?.forceClear) {
|
|
529
|
+
loadBalancer.s.pool.clear(conn.serviceId);
|
|
530
|
+
}
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
session[kPinnedConnection] = undefined;
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
|
|
400
537
|
function isMaxTimeMSExpiredError(err: MongoError) {
|
|
401
538
|
if (err == null || !(err instanceof MongoServerError)) {
|
|
402
539
|
return false;
|
|
@@ -461,7 +598,9 @@ function attemptTransaction<TSchema>(
|
|
|
461
598
|
|
|
462
599
|
if (!isPromiseLike(promise)) {
|
|
463
600
|
session.abortTransaction();
|
|
464
|
-
throw new
|
|
601
|
+
throw new MongoInvalidArgumentError(
|
|
602
|
+
'Function provided to `withTransaction` must return a Promise'
|
|
603
|
+
);
|
|
465
604
|
}
|
|
466
605
|
|
|
467
606
|
return promise.then(
|
|
@@ -508,7 +647,7 @@ function endTransaction(session: ClientSession, commandName: string, callback: C
|
|
|
508
647
|
const txnState = session.transaction.state;
|
|
509
648
|
|
|
510
649
|
if (txnState === TxnState.NO_TRANSACTION) {
|
|
511
|
-
callback(new
|
|
650
|
+
callback(new MongoTransactionError('No transaction started'));
|
|
512
651
|
return;
|
|
513
652
|
}
|
|
514
653
|
|
|
@@ -525,7 +664,7 @@ function endTransaction(session: ClientSession, commandName: string, callback: C
|
|
|
525
664
|
|
|
526
665
|
if (txnState === TxnState.TRANSACTION_ABORTED) {
|
|
527
666
|
callback(
|
|
528
|
-
new
|
|
667
|
+
new MongoTransactionError('Cannot call commitTransaction after calling abortTransaction')
|
|
529
668
|
);
|
|
530
669
|
return;
|
|
531
670
|
}
|
|
@@ -538,7 +677,7 @@ function endTransaction(session: ClientSession, commandName: string, callback: C
|
|
|
538
677
|
}
|
|
539
678
|
|
|
540
679
|
if (txnState === TxnState.TRANSACTION_ABORTED) {
|
|
541
|
-
callback(new
|
|
680
|
+
callback(new MongoTransactionError('Cannot call abortTransaction twice'));
|
|
542
681
|
return;
|
|
543
682
|
}
|
|
544
683
|
|
|
@@ -547,7 +686,7 @@ function endTransaction(session: ClientSession, commandName: string, callback: C
|
|
|
547
686
|
txnState === TxnState.TRANSACTION_COMMITTED_EMPTY
|
|
548
687
|
) {
|
|
549
688
|
callback(
|
|
550
|
-
new
|
|
689
|
+
new MongoTransactionError('Cannot call abortTransaction after calling commitTransaction')
|
|
551
690
|
);
|
|
552
691
|
return;
|
|
553
692
|
}
|
|
@@ -579,6 +718,10 @@ function endTransaction(session: ClientSession, commandName: string, callback: C
|
|
|
579
718
|
function commandHandler(e?: MongoError, r?: Document) {
|
|
580
719
|
if (commandName !== 'commitTransaction') {
|
|
581
720
|
session.transaction.transition(TxnState.TRANSACTION_ABORTED);
|
|
721
|
+
if (session.loadBalanced) {
|
|
722
|
+
maybeClearPinnedConnection(session, { force: false });
|
|
723
|
+
}
|
|
724
|
+
|
|
582
725
|
// The spec indicates that we should ignore all errors on `abortTransaction`
|
|
583
726
|
return callback();
|
|
584
727
|
}
|
|
@@ -595,12 +738,13 @@ function endTransaction(session: ClientSession, commandName: string, callback: C
|
|
|
595
738
|
e.addErrorLabel('UnknownTransactionCommitResult');
|
|
596
739
|
|
|
597
740
|
// per txns spec, must unpin session in this case
|
|
598
|
-
session.
|
|
741
|
+
session.unpin({ error: e });
|
|
599
742
|
}
|
|
600
743
|
} else if (e.hasErrorLabel('TransientTransactionError')) {
|
|
601
|
-
session.
|
|
744
|
+
session.unpin({ error: e });
|
|
602
745
|
}
|
|
603
746
|
}
|
|
747
|
+
|
|
604
748
|
callback(e, r);
|
|
605
749
|
}
|
|
606
750
|
|
|
@@ -614,14 +758,20 @@ function endTransaction(session: ClientSession, commandName: string, callback: C
|
|
|
614
758
|
session.topology,
|
|
615
759
|
new RunAdminCommandOperation(undefined, command, {
|
|
616
760
|
session,
|
|
617
|
-
readPreference: ReadPreference.primary
|
|
761
|
+
readPreference: ReadPreference.primary,
|
|
762
|
+
bypassPinningCheck: true
|
|
618
763
|
}),
|
|
619
764
|
(err, reply) => {
|
|
765
|
+
if (command.abortTransaction) {
|
|
766
|
+
// always unpin on abort regardless of command outcome
|
|
767
|
+
session.unpin();
|
|
768
|
+
}
|
|
769
|
+
|
|
620
770
|
if (err && isRetryableError(err as MongoError)) {
|
|
621
771
|
// SPEC-1185: apply majority write concern when retrying commitTransaction
|
|
622
772
|
if (command.commitTransaction) {
|
|
623
773
|
// per txns spec, must unpin session in this case
|
|
624
|
-
session.
|
|
774
|
+
session.unpin({ force: true });
|
|
625
775
|
|
|
626
776
|
command.writeConcern = Object.assign({ wtimeout: 10000 }, command.writeConcern, {
|
|
627
777
|
w: 'majority'
|
|
@@ -632,7 +782,8 @@ function endTransaction(session: ClientSession, commandName: string, callback: C
|
|
|
632
782
|
session.topology,
|
|
633
783
|
new RunAdminCommandOperation(undefined, command, {
|
|
634
784
|
session,
|
|
635
|
-
readPreference: ReadPreference.primary
|
|
785
|
+
readPreference: ReadPreference.primary,
|
|
786
|
+
bypassPinningCheck: true
|
|
636
787
|
}),
|
|
637
788
|
(_err, _reply) => commandHandler(_err as MongoError, _reply)
|
|
638
789
|
);
|
|
@@ -692,7 +843,7 @@ export class ServerSessionPool {
|
|
|
692
843
|
|
|
693
844
|
constructor(topology: Topology) {
|
|
694
845
|
if (topology == null) {
|
|
695
|
-
throw new
|
|
846
|
+
throw new MongoRuntimeError('ServerSessionPool requires a topology');
|
|
696
847
|
}
|
|
697
848
|
|
|
698
849
|
this.topology = topology;
|
|
@@ -731,7 +882,7 @@ export class ServerSessionPool {
|
|
|
731
882
|
|
|
732
883
|
while (this.sessions.length) {
|
|
733
884
|
const session = this.sessions.shift();
|
|
734
|
-
if (session && !session.hasTimedOut(sessionTimeoutMinutes)) {
|
|
885
|
+
if (session && (this.topology.loadBalanced || !session.hasTimedOut(sessionTimeoutMinutes))) {
|
|
735
886
|
return session;
|
|
736
887
|
}
|
|
737
888
|
}
|
|
@@ -748,6 +899,11 @@ export class ServerSessionPool {
|
|
|
748
899
|
*/
|
|
749
900
|
release(session: ServerSession): void {
|
|
750
901
|
const sessionTimeoutMinutes = this.topology.logicalSessionTimeoutMinutes;
|
|
902
|
+
|
|
903
|
+
if (this.topology.loadBalanced && !sessionTimeoutMinutes) {
|
|
904
|
+
this.sessions.unshift(session);
|
|
905
|
+
}
|
|
906
|
+
|
|
751
907
|
if (!sessionTimeoutMinutes) {
|
|
752
908
|
return;
|
|
753
909
|
}
|
|
@@ -805,19 +961,19 @@ export function applySession(
|
|
|
805
961
|
): MongoDriverError | undefined {
|
|
806
962
|
// TODO: merge this with `assertAlive`, did not want to throw a try/catch here
|
|
807
963
|
if (session.hasEnded) {
|
|
808
|
-
return new
|
|
964
|
+
return new MongoExpiredSessionError();
|
|
809
965
|
}
|
|
810
966
|
|
|
811
967
|
const serverSession = session.serverSession;
|
|
812
968
|
if (serverSession == null) {
|
|
813
|
-
return new
|
|
969
|
+
return new MongoRuntimeError('Unable to acquire server session');
|
|
814
970
|
}
|
|
815
971
|
|
|
816
972
|
// SPEC-1019: silently ignore explicit session with unacknowledged write for backwards compatibility
|
|
817
973
|
// FIXME: NODE-2781, this check for write concern shouldn't be happening here, but instead during command construction
|
|
818
974
|
if (options && options.writeConcern && (options.writeConcern as WriteConcern).w === 0) {
|
|
819
975
|
if (session && session.explicit) {
|
|
820
|
-
return new
|
|
976
|
+
return new MongoAPIError('Cannot have explicit session with unacknowledged writes');
|
|
821
977
|
}
|
|
822
978
|
return;
|
|
823
979
|
}
|
|
@@ -848,7 +1004,7 @@ export function applySession(
|
|
|
848
1004
|
Object.assign(command.readConcern, { afterClusterTime: session.operationTime });
|
|
849
1005
|
} else if (session[kSnapshotEnabled]) {
|
|
850
1006
|
command.readConcern = command.readConcern || { level: ReadConcernLevel.snapshot };
|
|
851
|
-
if (session[kSnapshotTime]
|
|
1007
|
+
if (session[kSnapshotTime] != null) {
|
|
852
1008
|
Object.assign(command.readConcern, { atClusterTime: session[kSnapshotTime] });
|
|
853
1009
|
}
|
|
854
1010
|
}
|
|
@@ -880,7 +1036,7 @@ export function applySession(
|
|
|
880
1036
|
|
|
881
1037
|
export function updateSessionFromResponse(session: ClientSession, document: Document): void {
|
|
882
1038
|
if (document.$clusterTime) {
|
|
883
|
-
|
|
1039
|
+
_advanceClusterTime(session, document.$clusterTime);
|
|
884
1040
|
}
|
|
885
1041
|
|
|
886
1042
|
if (document.operationTime && session && session.supports.causalConsistency) {
|
|
@@ -891,11 +1047,12 @@ export function updateSessionFromResponse(session: ClientSession, document: Docu
|
|
|
891
1047
|
session.transaction._recoveryToken = document.recoveryToken;
|
|
892
1048
|
}
|
|
893
1049
|
|
|
894
|
-
if (
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
1050
|
+
if (session?.[kSnapshotEnabled] && session[kSnapshotTime] == null) {
|
|
1051
|
+
// find and aggregate commands return atClusterTime on the cursor
|
|
1052
|
+
// distinct includes it in the response body
|
|
1053
|
+
const atClusterTime = document.cursor?.atClusterTime || document.atClusterTime;
|
|
1054
|
+
if (atClusterTime) {
|
|
1055
|
+
session[kSnapshotTime] = atClusterTime;
|
|
1056
|
+
}
|
|
900
1057
|
}
|
|
901
1058
|
}
|
package/src/sort.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { MongoInvalidArgumentError } from './error';
|
|
2
2
|
|
|
3
3
|
/** @public */
|
|
4
4
|
export type SortDirection =
|
|
@@ -45,13 +45,13 @@ function prepareDirection(direction: any = 1): SortDirectionForCmd {
|
|
|
45
45
|
case '-1':
|
|
46
46
|
return -1;
|
|
47
47
|
default:
|
|
48
|
-
throw new
|
|
48
|
+
throw new MongoInvalidArgumentError(`Invalid sort direction: ${JSON.stringify(direction)}`);
|
|
49
49
|
}
|
|
50
50
|
}
|
|
51
51
|
|
|
52
52
|
/** @internal */
|
|
53
53
|
function isMeta(t: SortDirection): t is { $meta: string } {
|
|
54
|
-
return typeof t === 'object' && t
|
|
54
|
+
return typeof t === 'object' && t != null && '$meta' in t && typeof t.$meta === 'string';
|
|
55
55
|
}
|
|
56
56
|
|
|
57
57
|
/** @internal */
|
|
@@ -118,7 +118,9 @@ export function formatSort(
|
|
|
118
118
|
if (sort == null) return undefined;
|
|
119
119
|
if (typeof sort === 'string') return new Map([[sort, prepareDirection(direction)]]);
|
|
120
120
|
if (typeof sort !== 'object') {
|
|
121
|
-
throw new
|
|
121
|
+
throw new MongoInvalidArgumentError(
|
|
122
|
+
`Invalid sort format: ${JSON.stringify(sort)} Sort must be a valid object`
|
|
123
|
+
);
|
|
122
124
|
}
|
|
123
125
|
if (!Array.isArray(sort)) {
|
|
124
126
|
return isMap(sort) ? mapToMap(sort) : Object.keys(sort).length ? objectToMap(sort) : undefined;
|
package/src/transactions.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { ReadPreference } from './read_preference';
|
|
2
|
-
import {
|
|
2
|
+
import { MongoRuntimeError, MongoTransactionError } from './error';
|
|
3
3
|
import { ReadConcern } from './read_concern';
|
|
4
4
|
import { WriteConcern } from './write_concern';
|
|
5
5
|
import type { Server } from './sdam/server';
|
|
@@ -45,6 +45,17 @@ const stateMachine: { [state in TxnState]: TxnState[] } = {
|
|
|
45
45
|
]
|
|
46
46
|
};
|
|
47
47
|
|
|
48
|
+
const ACTIVE_STATES: Set<TxnState> = new Set([
|
|
49
|
+
TxnState.STARTING_TRANSACTION,
|
|
50
|
+
TxnState.TRANSACTION_IN_PROGRESS
|
|
51
|
+
]);
|
|
52
|
+
|
|
53
|
+
const COMMITTED_STATES: Set<TxnState> = new Set([
|
|
54
|
+
TxnState.TRANSACTION_COMMITTED,
|
|
55
|
+
TxnState.TRANSACTION_COMMITTED_EMPTY,
|
|
56
|
+
TxnState.TRANSACTION_ABORTED
|
|
57
|
+
]);
|
|
58
|
+
|
|
48
59
|
/**
|
|
49
60
|
* Configuration options for a transaction.
|
|
50
61
|
* @public
|
|
@@ -77,14 +88,13 @@ export class Transaction {
|
|
|
77
88
|
/** Create a transaction @internal */
|
|
78
89
|
constructor(options?: TransactionOptions) {
|
|
79
90
|
options = options ?? {};
|
|
80
|
-
|
|
81
91
|
this.state = TxnState.NO_TRANSACTION;
|
|
82
92
|
this.options = {};
|
|
83
93
|
|
|
84
94
|
const writeConcern = WriteConcern.fromOptions(options);
|
|
85
95
|
if (writeConcern) {
|
|
86
96
|
if (writeConcern.w === 0) {
|
|
87
|
-
throw new
|
|
97
|
+
throw new MongoTransactionError('Transactions do not support unacknowledged write concern');
|
|
88
98
|
}
|
|
89
99
|
|
|
90
100
|
this.options.writeConcern = writeConcern;
|
|
@@ -129,13 +139,12 @@ export class Transaction {
|
|
|
129
139
|
* @returns Whether this session is presently in a transaction
|
|
130
140
|
*/
|
|
131
141
|
get isActive(): boolean {
|
|
132
|
-
|
|
133
|
-
TxnState.STARTING_TRANSACTION,
|
|
134
|
-
TxnState.TRANSACTION_IN_PROGRESS
|
|
135
|
-
];
|
|
136
|
-
return activeStates.includes(this.state);
|
|
142
|
+
return ACTIVE_STATES.has(this.state);
|
|
137
143
|
}
|
|
138
144
|
|
|
145
|
+
get isCommitted(): boolean {
|
|
146
|
+
return COMMITTED_STATES.has(this.state);
|
|
147
|
+
}
|
|
139
148
|
/**
|
|
140
149
|
* Transition the transaction in the state machine
|
|
141
150
|
* @internal
|
|
@@ -155,7 +164,7 @@ export class Transaction {
|
|
|
155
164
|
return;
|
|
156
165
|
}
|
|
157
166
|
|
|
158
|
-
throw new
|
|
167
|
+
throw new MongoRuntimeError(
|
|
159
168
|
`Attempted illegal state transition from [${this.state}] to [${nextState}]`
|
|
160
169
|
);
|
|
161
170
|
}
|