mongodb 6.12.0 → 6.13.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/beta.d.ts +176 -108
- package/lib/bulk/common.js +5 -7
- package/lib/bulk/common.js.map +1 -1
- package/lib/change_stream.js +16 -26
- package/lib/change_stream.js.map +1 -1
- package/lib/client-side-encryption/auto_encrypter.js +4 -2
- package/lib/client-side-encryption/auto_encrypter.js.map +1 -1
- package/lib/client-side-encryption/client_encryption.js +4 -4
- package/lib/client-side-encryption/client_encryption.js.map +1 -1
- package/lib/client-side-encryption/state_machine.js +56 -30
- package/lib/client-side-encryption/state_machine.js.map +1 -1
- package/lib/cmap/auth/mongodb_oidc.js +1 -1
- package/lib/cmap/auth/mongodb_oidc.js.map +1 -1
- package/lib/cmap/command_monitoring_events.js +9 -50
- package/lib/cmap/command_monitoring_events.js.map +1 -1
- package/lib/cmap/connection.js +28 -22
- package/lib/cmap/connection.js.map +1 -1
- package/lib/cmap/connection_pool.js +88 -117
- package/lib/cmap/connection_pool.js.map +1 -1
- package/lib/cmap/wire_protocol/on_data.js +6 -1
- package/lib/cmap/wire_protocol/on_data.js.map +1 -1
- package/lib/collection.js.map +1 -1
- package/lib/connection_string.js +68 -86
- package/lib/connection_string.js.map +1 -1
- package/lib/cursor/abstract_cursor.js +47 -18
- package/lib/cursor/abstract_cursor.js.map +1 -1
- package/lib/cursor/aggregation_cursor.js +2 -1
- package/lib/cursor/aggregation_cursor.js.map +1 -1
- package/lib/cursor/find_cursor.js +2 -1
- package/lib/cursor/find_cursor.js.map +1 -1
- package/lib/cursor/list_collections_cursor.js +2 -1
- package/lib/cursor/list_collections_cursor.js.map +1 -1
- package/lib/db.js +2 -1
- package/lib/db.js.map +1 -1
- package/lib/encrypter.js +5 -9
- package/lib/encrypter.js.map +1 -1
- package/lib/error.js +10 -18
- package/lib/error.js.map +1 -1
- package/lib/index.js +5 -2
- package/lib/index.js.map +1 -1
- package/lib/mongo_client.js +46 -26
- package/lib/mongo_client.js.map +1 -1
- package/lib/mongo_logger.js +102 -3
- package/lib/mongo_logger.js.map +1 -1
- package/lib/operations/execute_operation.js +9 -5
- package/lib/operations/execute_operation.js.map +1 -1
- package/lib/operations/list_collections.js.map +1 -1
- package/lib/operations/operation.js +4 -5
- package/lib/operations/operation.js.map +1 -1
- package/lib/sdam/monitor.js +25 -31
- package/lib/sdam/monitor.js.map +1 -1
- package/lib/sdam/server.js +27 -17
- package/lib/sdam/server.js.map +1 -1
- package/lib/sdam/topology.js +20 -19
- package/lib/sdam/topology.js.map +1 -1
- package/lib/sessions.js +24 -48
- package/lib/sessions.js.map +1 -1
- package/lib/utils.js +64 -44
- package/lib/utils.js.map +1 -1
- package/mongodb.d.ts +176 -108
- package/package.json +1 -1
- package/src/bulk/common.ts +6 -9
- package/src/change_stream.ts +21 -33
- package/src/client-side-encryption/auto_encrypter.ts +12 -8
- package/src/client-side-encryption/client_encryption.ts +6 -4
- package/src/client-side-encryption/state_machine.ts +80 -36
- package/src/cmap/auth/mongodb_oidc.ts +1 -1
- package/src/cmap/command_monitoring_events.ts +10 -55
- package/src/cmap/connection.ts +37 -29
- package/src/cmap/connection_pool.ts +121 -145
- package/src/cmap/wire_protocol/on_data.ts +9 -2
- package/src/collection.ts +15 -8
- package/src/connection_string.ts +74 -99
- package/src/cursor/abstract_cursor.ts +71 -23
- package/src/cursor/aggregation_cursor.ts +5 -3
- package/src/cursor/find_cursor.ts +5 -3
- package/src/cursor/list_collections_cursor.ts +5 -3
- package/src/db.ts +11 -7
- package/src/encrypter.ts +6 -11
- package/src/error.ts +11 -23
- package/src/index.ts +3 -3
- package/src/mongo_client.ts +78 -47
- package/src/mongo_logger.ts +158 -11
- package/src/mongo_types.ts +38 -0
- package/src/operations/execute_operation.ts +11 -6
- package/src/operations/list_collections.ts +4 -1
- package/src/operations/operation.ts +8 -9
- package/src/sdam/monitor.ts +30 -38
- package/src/sdam/server.ts +33 -20
- package/src/sdam/topology.ts +29 -26
- package/src/sessions.ts +37 -58
- package/src/utils.ts +79 -43
package/src/sdam/server.ts
CHANGED
|
@@ -22,7 +22,6 @@ import {
|
|
|
22
22
|
} from '../constants';
|
|
23
23
|
import {
|
|
24
24
|
type AnyError,
|
|
25
|
-
isNetworkErrorBeforeHandshake,
|
|
26
25
|
isNodeShuttingDownError,
|
|
27
26
|
isSDAMUnrecoverableError,
|
|
28
27
|
MONGODB_ERROR_CODES,
|
|
@@ -37,12 +36,13 @@ import {
|
|
|
37
36
|
needsRetryableWriteLabel
|
|
38
37
|
} from '../error';
|
|
39
38
|
import type { ServerApi } from '../mongo_client';
|
|
40
|
-
import { TypedEventEmitter } from '../mongo_types';
|
|
39
|
+
import { type Abortable, TypedEventEmitter } from '../mongo_types';
|
|
41
40
|
import type { GetMoreOptions } from '../operations/get_more';
|
|
42
41
|
import type { ClientSession } from '../sessions';
|
|
43
42
|
import { type TimeoutContext } from '../timeout';
|
|
44
43
|
import { isTransactionCommand } from '../transactions';
|
|
45
44
|
import {
|
|
45
|
+
abortable,
|
|
46
46
|
type EventEmitterWithState,
|
|
47
47
|
makeStateMachine,
|
|
48
48
|
maxWireVersion,
|
|
@@ -108,7 +108,7 @@ export type ServerEvents = {
|
|
|
108
108
|
/** @internal */
|
|
109
109
|
export type ServerCommandOptions = Omit<CommandOptions, 'timeoutContext' | 'socketTimeoutMS'> & {
|
|
110
110
|
timeoutContext: TimeoutContext;
|
|
111
|
-
};
|
|
111
|
+
} & Abortable;
|
|
112
112
|
|
|
113
113
|
/** @internal */
|
|
114
114
|
export class Server extends TypedEventEmitter<ServerEvents> {
|
|
@@ -286,7 +286,7 @@ export class Server extends TypedEventEmitter<ServerEvents> {
|
|
|
286
286
|
public async command(
|
|
287
287
|
ns: MongoDBNamespace,
|
|
288
288
|
cmd: Document,
|
|
289
|
-
options: ServerCommandOptions,
|
|
289
|
+
{ ...options }: ServerCommandOptions,
|
|
290
290
|
responseType?: MongoDBResponseConstructor
|
|
291
291
|
): Promise<Document> {
|
|
292
292
|
if (ns.db == null || typeof ns === 'string') {
|
|
@@ -297,25 +297,21 @@ export class Server extends TypedEventEmitter<ServerEvents> {
|
|
|
297
297
|
throw new MongoServerClosedError();
|
|
298
298
|
}
|
|
299
299
|
|
|
300
|
-
|
|
301
|
-
const finalOptions = Object.assign({}, options, {
|
|
302
|
-
wireProtocolCommand: false,
|
|
303
|
-
directConnection: this.topology.s.options.directConnection
|
|
304
|
-
});
|
|
300
|
+
options.directConnection = this.topology.s.options.directConnection;
|
|
305
301
|
|
|
306
302
|
// There are cases where we need to flag the read preference not to get sent in
|
|
307
303
|
// the command, such as pre-5.0 servers attempting to perform an aggregate write
|
|
308
304
|
// with a non-primary read preference. In this case the effective read preference
|
|
309
305
|
// (primary) is not the same as the provided and must be removed completely.
|
|
310
|
-
if (
|
|
311
|
-
delete
|
|
306
|
+
if (options.omitReadPreference) {
|
|
307
|
+
delete options.readPreference;
|
|
312
308
|
}
|
|
313
309
|
|
|
314
310
|
if (this.description.iscryptd) {
|
|
315
|
-
|
|
311
|
+
options.omitMaxTimeMS = true;
|
|
316
312
|
}
|
|
317
313
|
|
|
318
|
-
const session =
|
|
314
|
+
const session = options.session;
|
|
319
315
|
let conn = session?.pinnedConnection;
|
|
320
316
|
|
|
321
317
|
this.incrementOperationCount();
|
|
@@ -332,26 +328,35 @@ export class Server extends TypedEventEmitter<ServerEvents> {
|
|
|
332
328
|
}
|
|
333
329
|
}
|
|
334
330
|
|
|
331
|
+
let reauthPromise: Promise<void> | null = null;
|
|
332
|
+
|
|
335
333
|
try {
|
|
336
334
|
try {
|
|
337
|
-
const res = await conn.command(ns, cmd,
|
|
335
|
+
const res = await conn.command(ns, cmd, options, responseType);
|
|
338
336
|
throwIfWriteConcernError(res);
|
|
339
337
|
return res;
|
|
340
338
|
} catch (commandError) {
|
|
341
|
-
throw this.decorateCommandError(conn, cmd,
|
|
339
|
+
throw this.decorateCommandError(conn, cmd, options, commandError);
|
|
342
340
|
}
|
|
343
341
|
} catch (operationError) {
|
|
344
342
|
if (
|
|
345
343
|
operationError instanceof MongoError &&
|
|
346
344
|
operationError.code === MONGODB_ERROR_CODES.Reauthenticate
|
|
347
345
|
) {
|
|
348
|
-
|
|
346
|
+
reauthPromise = this.pool.reauthenticate(conn).catch(error => {
|
|
347
|
+
reauthPromise = null;
|
|
348
|
+
throw error;
|
|
349
|
+
});
|
|
350
|
+
|
|
351
|
+
await abortable(reauthPromise, options);
|
|
352
|
+
reauthPromise = null; // only reachable if reauth succeeds
|
|
353
|
+
|
|
349
354
|
try {
|
|
350
|
-
const res = await conn.command(ns, cmd,
|
|
355
|
+
const res = await conn.command(ns, cmd, options, responseType);
|
|
351
356
|
throwIfWriteConcernError(res);
|
|
352
357
|
return res;
|
|
353
358
|
} catch (commandError) {
|
|
354
|
-
throw this.decorateCommandError(conn, cmd,
|
|
359
|
+
throw this.decorateCommandError(conn, cmd, options, commandError);
|
|
355
360
|
}
|
|
356
361
|
} else {
|
|
357
362
|
throw operationError;
|
|
@@ -359,7 +364,14 @@ export class Server extends TypedEventEmitter<ServerEvents> {
|
|
|
359
364
|
} finally {
|
|
360
365
|
this.decrementOperationCount();
|
|
361
366
|
if (session?.pinnedConnection !== conn) {
|
|
362
|
-
|
|
367
|
+
if (reauthPromise != null) {
|
|
368
|
+
// The reauth promise only exists if it hasn't thrown.
|
|
369
|
+
void reauthPromise.finally(() => {
|
|
370
|
+
this.pool.checkIn(conn);
|
|
371
|
+
});
|
|
372
|
+
} else {
|
|
373
|
+
this.pool.checkIn(conn);
|
|
374
|
+
}
|
|
363
375
|
}
|
|
364
376
|
}
|
|
365
377
|
}
|
|
@@ -381,7 +393,8 @@ export class Server extends TypedEventEmitter<ServerEvents> {
|
|
|
381
393
|
|
|
382
394
|
const isNetworkNonTimeoutError =
|
|
383
395
|
error instanceof MongoNetworkError && !(error instanceof MongoNetworkTimeoutError);
|
|
384
|
-
const isNetworkTimeoutBeforeHandshakeError =
|
|
396
|
+
const isNetworkTimeoutBeforeHandshakeError =
|
|
397
|
+
error instanceof MongoNetworkError && error.beforeHandshake;
|
|
385
398
|
const isAuthHandshakeError = error.hasErrorLabel(MongoErrorLabel.HandshakeError);
|
|
386
399
|
if (isNetworkNonTimeoutError || isNetworkTimeoutBeforeHandshakeError || isAuthHandshakeError) {
|
|
387
400
|
// In load balanced mode we never mark the server as unknown and always
|
package/src/sdam/topology.ts
CHANGED
|
@@ -3,7 +3,7 @@ import type { MongoCredentials } from '../cmap/auth/mongo_credentials';
|
|
|
3
3
|
import type { ConnectionEvents } from '../cmap/connection';
|
|
4
4
|
import type { ConnectionPoolEvents } from '../cmap/connection_pool';
|
|
5
5
|
import type { ClientMetadata } from '../cmap/handshake/client_metadata';
|
|
6
|
-
import { DEFAULT_OPTIONS
|
|
6
|
+
import { DEFAULT_OPTIONS } from '../connection_string';
|
|
7
7
|
import {
|
|
8
8
|
CLOSE,
|
|
9
9
|
CONNECT,
|
|
@@ -31,15 +31,17 @@ import {
|
|
|
31
31
|
} from '../error';
|
|
32
32
|
import type { MongoClient, ServerApi } from '../mongo_client';
|
|
33
33
|
import { MongoLoggableComponent, type MongoLogger, SeverityLevel } from '../mongo_logger';
|
|
34
|
-
import { TypedEventEmitter } from '../mongo_types';
|
|
34
|
+
import { type Abortable, TypedEventEmitter } from '../mongo_types';
|
|
35
35
|
import { ReadPreference, type ReadPreferenceLike } from '../read_preference';
|
|
36
36
|
import type { ClientSession } from '../sessions';
|
|
37
37
|
import { Timeout, TimeoutContext, TimeoutError } from '../timeout';
|
|
38
38
|
import type { Transaction } from '../transactions';
|
|
39
39
|
import {
|
|
40
|
+
addAbortListener,
|
|
40
41
|
type Callback,
|
|
41
42
|
type EventEmitterWithState,
|
|
42
43
|
HostAddress,
|
|
44
|
+
kDispose,
|
|
43
45
|
List,
|
|
44
46
|
makeStateMachine,
|
|
45
47
|
now,
|
|
@@ -88,11 +90,6 @@ const stateTransition = makeStateMachine({
|
|
|
88
90
|
[STATE_CLOSING]: [STATE_CLOSING, STATE_CLOSED]
|
|
89
91
|
});
|
|
90
92
|
|
|
91
|
-
/** @internal */
|
|
92
|
-
const kCancelled = Symbol('cancelled');
|
|
93
|
-
/** @internal */
|
|
94
|
-
const kWaitQueue = Symbol('waitQueue');
|
|
95
|
-
|
|
96
93
|
/** @internal */
|
|
97
94
|
export type ServerSelectionCallback = Callback<Server>;
|
|
98
95
|
|
|
@@ -105,7 +102,7 @@ export interface ServerSelectionRequest {
|
|
|
105
102
|
startTime: number;
|
|
106
103
|
resolve: (server: Server) => void;
|
|
107
104
|
reject: (error: MongoError) => void;
|
|
108
|
-
|
|
105
|
+
cancelled: boolean;
|
|
109
106
|
operationName: string;
|
|
110
107
|
waitingLogged: boolean;
|
|
111
108
|
previousServer?: ServerDescription;
|
|
@@ -158,7 +155,7 @@ export interface TopologyOptions extends BSONSerializeOptions, ServerOptions {
|
|
|
158
155
|
serverMonitoringMode: ServerMonitoringMode;
|
|
159
156
|
/** MongoDB server API version */
|
|
160
157
|
serverApi?: ServerApi;
|
|
161
|
-
|
|
158
|
+
__skipPingOnConnect?: boolean;
|
|
162
159
|
}
|
|
163
160
|
|
|
164
161
|
/** @public */
|
|
@@ -208,7 +205,7 @@ export class Topology extends TypedEventEmitter<TopologyEvents> {
|
|
|
208
205
|
/** @internal */
|
|
209
206
|
s: TopologyPrivate;
|
|
210
207
|
/** @internal */
|
|
211
|
-
|
|
208
|
+
waitQueue: List<ServerSelectionRequest>;
|
|
212
209
|
/** @internal */
|
|
213
210
|
hello?: Document;
|
|
214
211
|
/** @internal */
|
|
@@ -256,8 +253,7 @@ export class Topology extends TypedEventEmitter<TopologyEvents> {
|
|
|
256
253
|
// Options should only be undefined in tests, MongoClient will always have defined options
|
|
257
254
|
options = options ?? {
|
|
258
255
|
hosts: [HostAddress.fromString('localhost:27017')],
|
|
259
|
-
...Object.fromEntries(DEFAULT_OPTIONS.entries())
|
|
260
|
-
...Object.fromEntries(FEATURE_FLAGS.entries())
|
|
256
|
+
...Object.fromEntries(DEFAULT_OPTIONS.entries())
|
|
261
257
|
};
|
|
262
258
|
|
|
263
259
|
if (typeof seeds === 'string') {
|
|
@@ -293,7 +289,7 @@ export class Topology extends TypedEventEmitter<TopologyEvents> {
|
|
|
293
289
|
serverDescriptions.set(hostAddress.toString(), new ServerDescription(hostAddress));
|
|
294
290
|
}
|
|
295
291
|
|
|
296
|
-
this
|
|
292
|
+
this.waitQueue = new List();
|
|
297
293
|
this.s = {
|
|
298
294
|
// the id of this topology
|
|
299
295
|
id: topologyId,
|
|
@@ -471,7 +467,7 @@ export class Topology extends TypedEventEmitter<TopologyEvents> {
|
|
|
471
467
|
readPreferenceServerSelector(readPreference),
|
|
472
468
|
selectServerOptions
|
|
473
469
|
);
|
|
474
|
-
const skipPingOnConnect = this.s.options
|
|
470
|
+
const skipPingOnConnect = this.s.options.__skipPingOnConnect === true;
|
|
475
471
|
if (!skipPingOnConnect && this.s.credentials) {
|
|
476
472
|
await server.command(ns('admin.$cmd'), { ping: 1 }, { timeoutContext });
|
|
477
473
|
stateTransition(this, STATE_CONNECTED);
|
|
@@ -506,7 +502,7 @@ export class Topology extends TypedEventEmitter<TopologyEvents> {
|
|
|
506
502
|
|
|
507
503
|
stateTransition(this, STATE_CLOSING);
|
|
508
504
|
|
|
509
|
-
drainWaitQueue(this
|
|
505
|
+
drainWaitQueue(this.waitQueue, new MongoTopologyClosedError());
|
|
510
506
|
|
|
511
507
|
if (this.s.srvPoller) {
|
|
512
508
|
this.s.srvPoller.stop();
|
|
@@ -531,7 +527,7 @@ export class Topology extends TypedEventEmitter<TopologyEvents> {
|
|
|
531
527
|
*/
|
|
532
528
|
async selectServer(
|
|
533
529
|
selector: string | ReadPreference | ServerSelector,
|
|
534
|
-
options: SelectServerOptions
|
|
530
|
+
options: SelectServerOptions & Abortable
|
|
535
531
|
): Promise<Server> {
|
|
536
532
|
let serverSelector;
|
|
537
533
|
if (typeof selector !== 'function') {
|
|
@@ -601,13 +597,19 @@ export class Topology extends TypedEventEmitter<TopologyEvents> {
|
|
|
601
597
|
transaction,
|
|
602
598
|
resolve,
|
|
603
599
|
reject,
|
|
600
|
+
cancelled: false,
|
|
604
601
|
startTime: now(),
|
|
605
602
|
operationName: options.operationName,
|
|
606
603
|
waitingLogged: false,
|
|
607
604
|
previousServer: options.previousServer
|
|
608
605
|
};
|
|
609
606
|
|
|
610
|
-
|
|
607
|
+
const abortListener = addAbortListener(options.signal, function () {
|
|
608
|
+
waitQueueMember.cancelled = true;
|
|
609
|
+
reject(this.reason);
|
|
610
|
+
});
|
|
611
|
+
|
|
612
|
+
this.waitQueue.push(waitQueueMember);
|
|
611
613
|
processWaitQueue(this);
|
|
612
614
|
|
|
613
615
|
try {
|
|
@@ -620,7 +622,7 @@ export class Topology extends TypedEventEmitter<TopologyEvents> {
|
|
|
620
622
|
} catch (error) {
|
|
621
623
|
if (TimeoutError.is(error)) {
|
|
622
624
|
// Timeout
|
|
623
|
-
waitQueueMember
|
|
625
|
+
waitQueueMember.cancelled = true;
|
|
624
626
|
const timeoutError = new MongoServerSelectionError(
|
|
625
627
|
`Server selection timed out after ${timeout?.duration} ms`,
|
|
626
628
|
this.description
|
|
@@ -652,6 +654,7 @@ export class Topology extends TypedEventEmitter<TopologyEvents> {
|
|
|
652
654
|
// Other server selection error
|
|
653
655
|
throw error;
|
|
654
656
|
} finally {
|
|
657
|
+
abortListener?.[kDispose]();
|
|
655
658
|
if (options.timeoutContext?.clearServerSelectionTimeout) timeout?.clear();
|
|
656
659
|
}
|
|
657
660
|
}
|
|
@@ -721,7 +724,7 @@ export class Topology extends TypedEventEmitter<TopologyEvents> {
|
|
|
721
724
|
updateServers(this, serverDescription);
|
|
722
725
|
|
|
723
726
|
// attempt to resolve any outstanding server selection attempts
|
|
724
|
-
if (this
|
|
727
|
+
if (this.waitQueue.length > 0) {
|
|
725
728
|
processWaitQueue(this);
|
|
726
729
|
}
|
|
727
730
|
|
|
@@ -910,7 +913,7 @@ function drainWaitQueue(queue: List<ServerSelectionRequest>, drainError: MongoDr
|
|
|
910
913
|
continue;
|
|
911
914
|
}
|
|
912
915
|
|
|
913
|
-
if (!waitQueueMember
|
|
916
|
+
if (!waitQueueMember.cancelled) {
|
|
914
917
|
if (
|
|
915
918
|
waitQueueMember.mongoLogger?.willLog(
|
|
916
919
|
MongoLoggableComponent.SERVER_SELECTION,
|
|
@@ -934,20 +937,20 @@ function drainWaitQueue(queue: List<ServerSelectionRequest>, drainError: MongoDr
|
|
|
934
937
|
|
|
935
938
|
function processWaitQueue(topology: Topology) {
|
|
936
939
|
if (topology.s.state === STATE_CLOSED) {
|
|
937
|
-
drainWaitQueue(topology
|
|
940
|
+
drainWaitQueue(topology.waitQueue, new MongoTopologyClosedError());
|
|
938
941
|
return;
|
|
939
942
|
}
|
|
940
943
|
|
|
941
944
|
const isSharded = topology.description.type === TopologyType.Sharded;
|
|
942
945
|
const serverDescriptions = Array.from(topology.description.servers.values());
|
|
943
|
-
const membersToProcess = topology
|
|
946
|
+
const membersToProcess = topology.waitQueue.length;
|
|
944
947
|
for (let i = 0; i < membersToProcess; ++i) {
|
|
945
|
-
const waitQueueMember = topology
|
|
948
|
+
const waitQueueMember = topology.waitQueue.shift();
|
|
946
949
|
if (!waitQueueMember) {
|
|
947
950
|
continue;
|
|
948
951
|
}
|
|
949
952
|
|
|
950
|
-
if (waitQueueMember
|
|
953
|
+
if (waitQueueMember.cancelled) {
|
|
951
954
|
continue;
|
|
952
955
|
}
|
|
953
956
|
|
|
@@ -1006,7 +1009,7 @@ function processWaitQueue(topology: Topology) {
|
|
|
1006
1009
|
}
|
|
1007
1010
|
waitQueueMember.waitingLogged = true;
|
|
1008
1011
|
}
|
|
1009
|
-
topology
|
|
1012
|
+
topology.waitQueue.push(waitQueueMember);
|
|
1010
1013
|
continue;
|
|
1011
1014
|
} else if (selectedDescriptions.length === 1) {
|
|
1012
1015
|
selectedServer = topology.s.servers.get(selectedDescriptions[0].address);
|
|
@@ -1069,7 +1072,7 @@ function processWaitQueue(topology: Topology) {
|
|
|
1069
1072
|
waitQueueMember.resolve(selectedServer);
|
|
1070
1073
|
}
|
|
1071
1074
|
|
|
1072
|
-
if (topology
|
|
1075
|
+
if (topology.waitQueue.length > 0) {
|
|
1073
1076
|
// ensure all server monitors attempt monitoring soon
|
|
1074
1077
|
for (const [, server] of topology.s.servers) {
|
|
1075
1078
|
process.nextTick(function scheduleServerCheck() {
|
package/src/sessions.ts
CHANGED
|
@@ -83,17 +83,6 @@ export type ClientSessionEvents = {
|
|
|
83
83
|
ended(session: ClientSession): void;
|
|
84
84
|
};
|
|
85
85
|
|
|
86
|
-
/** @internal */
|
|
87
|
-
const kServerSession = Symbol('serverSession');
|
|
88
|
-
/** @internal */
|
|
89
|
-
const kSnapshotTime = Symbol('snapshotTime');
|
|
90
|
-
/** @internal */
|
|
91
|
-
const kSnapshotEnabled = Symbol('snapshotEnabled');
|
|
92
|
-
/** @internal */
|
|
93
|
-
const kPinnedConnection = Symbol('pinnedConnection');
|
|
94
|
-
/** @internal Accumulates total number of increments to add to txnNumber when applying session to command */
|
|
95
|
-
const kTxnNumberIncrement = Symbol('txnNumberIncrement');
|
|
96
|
-
|
|
97
86
|
/** @public */
|
|
98
87
|
export interface EndSessionOptions {
|
|
99
88
|
/**
|
|
@@ -132,20 +121,22 @@ export class ClientSession
|
|
|
132
121
|
owner?: symbol | AbstractCursor;
|
|
133
122
|
defaultTransactionOptions: TransactionOptions;
|
|
134
123
|
transaction: Transaction;
|
|
135
|
-
/**
|
|
124
|
+
/**
|
|
125
|
+
* @internal
|
|
136
126
|
* Keeps track of whether or not the current transaction has attempted to be committed. Is
|
|
137
|
-
* initially undefined. Gets set to false when startTransaction is called. When commitTransaction is sent to server, if the commitTransaction succeeds, it is then set to undefined, otherwise, set to true
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
127
|
+
* initially undefined. Gets set to false when startTransaction is called. When commitTransaction is sent to server, if the commitTransaction succeeds, it is then set to undefined, otherwise, set to true
|
|
128
|
+
*/
|
|
129
|
+
private commitAttempted?: boolean;
|
|
130
|
+
public readonly snapshotEnabled: boolean;
|
|
131
|
+
|
|
141
132
|
/** @internal */
|
|
142
|
-
|
|
133
|
+
private _serverSession: ServerSession | null;
|
|
143
134
|
/** @internal */
|
|
144
|
-
|
|
135
|
+
public snapshotTime?: Timestamp;
|
|
145
136
|
/** @internal */
|
|
146
|
-
|
|
137
|
+
public pinnedConnection?: Connection;
|
|
147
138
|
/** @internal */
|
|
148
|
-
|
|
139
|
+
public txnNumberIncrement: number;
|
|
149
140
|
/**
|
|
150
141
|
* @experimental
|
|
151
142
|
* Specifies the time an operation in a given `ClientSession` will run until it throws a timeout error
|
|
@@ -183,13 +174,11 @@ export class ClientSession
|
|
|
183
174
|
|
|
184
175
|
options = options ?? {};
|
|
185
176
|
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
);
|
|
192
|
-
}
|
|
177
|
+
this.snapshotEnabled = options.snapshot === true;
|
|
178
|
+
if (options.causalConsistency === true && this.snapshotEnabled) {
|
|
179
|
+
throw new MongoInvalidArgumentError(
|
|
180
|
+
'Properties "causalConsistency" and "snapshot" are mutually exclusive'
|
|
181
|
+
);
|
|
193
182
|
}
|
|
194
183
|
|
|
195
184
|
this.client = client;
|
|
@@ -199,8 +188,8 @@ export class ClientSession
|
|
|
199
188
|
this.timeoutMS = options.defaultTimeoutMS ?? client.s.options?.timeoutMS;
|
|
200
189
|
|
|
201
190
|
this.explicit = !!options.explicit;
|
|
202
|
-
this
|
|
203
|
-
this
|
|
191
|
+
this._serverSession = this.explicit ? this.sessionPool.acquire() : null;
|
|
192
|
+
this.txnNumberIncrement = 0;
|
|
204
193
|
|
|
205
194
|
const defaultCausalConsistencyValue = this.explicit && options.snapshot !== true;
|
|
206
195
|
this.supports = {
|
|
@@ -218,11 +207,11 @@ export class ClientSession
|
|
|
218
207
|
|
|
219
208
|
/** The server id associated with this session */
|
|
220
209
|
get id(): ServerSessionId | undefined {
|
|
221
|
-
return this
|
|
210
|
+
return this.serverSession?.id;
|
|
222
211
|
}
|
|
223
212
|
|
|
224
213
|
get serverSession(): ServerSession {
|
|
225
|
-
let serverSession = this
|
|
214
|
+
let serverSession = this._serverSession;
|
|
226
215
|
if (serverSession == null) {
|
|
227
216
|
if (this.explicit) {
|
|
228
217
|
throw new MongoRuntimeError('Unexpected null serverSession for an explicit session');
|
|
@@ -231,32 +220,22 @@ export class ClientSession
|
|
|
231
220
|
throw new MongoRuntimeError('Unexpected null serverSession for an ended implicit session');
|
|
232
221
|
}
|
|
233
222
|
serverSession = this.sessionPool.acquire();
|
|
234
|
-
this
|
|
223
|
+
this._serverSession = serverSession;
|
|
235
224
|
}
|
|
236
225
|
return serverSession;
|
|
237
226
|
}
|
|
238
227
|
|
|
239
|
-
/** Whether or not this session is configured for snapshot reads */
|
|
240
|
-
get snapshotEnabled(): boolean {
|
|
241
|
-
return this[kSnapshotEnabled];
|
|
242
|
-
}
|
|
243
|
-
|
|
244
228
|
get loadBalanced(): boolean {
|
|
245
229
|
return this.client.topology?.description.type === TopologyType.LoadBalanced;
|
|
246
230
|
}
|
|
247
231
|
|
|
248
|
-
/** @internal */
|
|
249
|
-
get pinnedConnection(): Connection | undefined {
|
|
250
|
-
return this[kPinnedConnection];
|
|
251
|
-
}
|
|
252
|
-
|
|
253
232
|
/** @internal */
|
|
254
233
|
pin(conn: Connection): void {
|
|
255
|
-
if (this
|
|
234
|
+
if (this.pinnedConnection) {
|
|
256
235
|
throw TypeError('Cannot pin multiple connections to the same session');
|
|
257
236
|
}
|
|
258
237
|
|
|
259
|
-
this
|
|
238
|
+
this.pinnedConnection = conn;
|
|
260
239
|
conn.emit(
|
|
261
240
|
PINNED,
|
|
262
241
|
this.inTransaction() ? ConnectionPoolMetrics.TXN : ConnectionPoolMetrics.CURSOR
|
|
@@ -273,7 +252,7 @@ export class ClientSession
|
|
|
273
252
|
}
|
|
274
253
|
|
|
275
254
|
get isPinned(): boolean {
|
|
276
|
-
return this.loadBalanced ? !!this
|
|
255
|
+
return this.loadBalanced ? !!this.pinnedConnection : this.transaction.isPinned;
|
|
277
256
|
}
|
|
278
257
|
|
|
279
258
|
/**
|
|
@@ -295,12 +274,12 @@ export class ClientSession
|
|
|
295
274
|
squashError(error);
|
|
296
275
|
} finally {
|
|
297
276
|
if (!this.hasEnded) {
|
|
298
|
-
const serverSession = this
|
|
277
|
+
const serverSession = this.serverSession;
|
|
299
278
|
if (serverSession != null) {
|
|
300
279
|
// release the server session back to the pool
|
|
301
280
|
this.sessionPool.release(serverSession);
|
|
302
281
|
// Store a clone of the server session for reference (debugging)
|
|
303
|
-
this
|
|
282
|
+
this._serverSession = new ServerSession(serverSession);
|
|
304
283
|
}
|
|
305
284
|
// mark the session as ended, and emit a signal
|
|
306
285
|
this.hasEnded = true;
|
|
@@ -391,7 +370,7 @@ export class ClientSession
|
|
|
391
370
|
* This is because the serverSession is lazily acquired after a connection is obtained
|
|
392
371
|
*/
|
|
393
372
|
incrementTransactionNumber(): void {
|
|
394
|
-
this
|
|
373
|
+
this.txnNumberIncrement += 1;
|
|
395
374
|
}
|
|
396
375
|
|
|
397
376
|
/** @returns whether this session is currently in a transaction or not */
|
|
@@ -410,7 +389,7 @@ export class ClientSession
|
|
|
410
389
|
* @param options - Options for the transaction
|
|
411
390
|
*/
|
|
412
391
|
startTransaction(options?: TransactionOptions): void {
|
|
413
|
-
if (this
|
|
392
|
+
if (this.snapshotEnabled) {
|
|
414
393
|
throw new MongoCompatibilityError('Transactions are not supported in snapshot sessions');
|
|
415
394
|
}
|
|
416
395
|
|
|
@@ -908,7 +887,7 @@ export function maybeClearPinnedConnection(
|
|
|
908
887
|
options?: EndSessionOptions
|
|
909
888
|
): void {
|
|
910
889
|
// unpin a connection if it has been pinned
|
|
911
|
-
const conn = session
|
|
890
|
+
const conn = session.pinnedConnection;
|
|
912
891
|
const error = options?.error;
|
|
913
892
|
|
|
914
893
|
if (
|
|
@@ -929,7 +908,7 @@ export function maybeClearPinnedConnection(
|
|
|
929
908
|
|
|
930
909
|
if (options?.error == null || options?.force) {
|
|
931
910
|
loadBalancer.pool.checkIn(conn);
|
|
932
|
-
session
|
|
911
|
+
session.pinnedConnection = undefined;
|
|
933
912
|
conn.emit(
|
|
934
913
|
UNPINNED,
|
|
935
914
|
session.transaction.state !== TxnState.NO_TRANSACTION
|
|
@@ -1123,8 +1102,8 @@ export function applySession(
|
|
|
1123
1102
|
const isRetryableWrite = !!options.willRetryWrite;
|
|
1124
1103
|
|
|
1125
1104
|
if (isRetryableWrite || inTxnOrTxnCommand) {
|
|
1126
|
-
serverSession.txnNumber += session
|
|
1127
|
-
session
|
|
1105
|
+
serverSession.txnNumber += session.txnNumberIncrement;
|
|
1106
|
+
session.txnNumberIncrement = 0;
|
|
1128
1107
|
// TODO(NODE-2674): Preserve int64 sent from MongoDB
|
|
1129
1108
|
command.txnNumber = Long.fromNumber(serverSession.txnNumber);
|
|
1130
1109
|
}
|
|
@@ -1141,10 +1120,10 @@ export function applySession(
|
|
|
1141
1120
|
) {
|
|
1142
1121
|
command.readConcern = command.readConcern || {};
|
|
1143
1122
|
Object.assign(command.readConcern, { afterClusterTime: session.operationTime });
|
|
1144
|
-
} else if (session
|
|
1123
|
+
} else if (session.snapshotEnabled) {
|
|
1145
1124
|
command.readConcern = command.readConcern || { level: ReadConcernLevel.snapshot };
|
|
1146
|
-
if (session
|
|
1147
|
-
Object.assign(command.readConcern, { atClusterTime: session
|
|
1125
|
+
if (session.snapshotTime != null) {
|
|
1126
|
+
Object.assign(command.readConcern, { atClusterTime: session.snapshotTime });
|
|
1148
1127
|
}
|
|
1149
1128
|
}
|
|
1150
1129
|
|
|
@@ -1187,12 +1166,12 @@ export function updateSessionFromResponse(session: ClientSession, document: Mong
|
|
|
1187
1166
|
session.transaction._recoveryToken = document.recoveryToken;
|
|
1188
1167
|
}
|
|
1189
1168
|
|
|
1190
|
-
if (session?.
|
|
1169
|
+
if (session?.snapshotEnabled && session.snapshotTime == null) {
|
|
1191
1170
|
// find and aggregate commands return atClusterTime on the cursor
|
|
1192
1171
|
// distinct includes it in the response body
|
|
1193
1172
|
const atClusterTime = document.atClusterTime;
|
|
1194
1173
|
if (atClusterTime) {
|
|
1195
|
-
session
|
|
1174
|
+
session.snapshotTime = atClusterTime;
|
|
1196
1175
|
}
|
|
1197
1176
|
}
|
|
1198
1177
|
}
|