mongodb 6.12.0-dev.20250125.sha.c1bcf0de → 6.12.0-dev.20250128.sha.654069fc
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 +55 -13
- 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/connection.js +28 -22
- package/lib/cmap/connection.js.map +1 -1
- package/lib/cmap/connection_pool.js +5 -0
- 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/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/mongo_client.js +4 -0
- package/lib/mongo_client.js.map +1 -1
- package/lib/operations/execute_operation.js +7 -3
- package/lib/operations/execute_operation.js.map +1 -1
- package/lib/operations/list_collections.js.map +1 -1
- package/lib/operations/operation.js.map +1 -1
- package/lib/sdam/server.js +26 -16
- package/lib/sdam/server.js.map +1 -1
- package/lib/sdam/topology.js +5 -0
- package/lib/sdam/topology.js.map +1 -1
- package/lib/utils.js +64 -7
- package/lib/utils.js.map +1 -1
- package/mongodb.d.ts +55 -13
- package/package.json +1 -1
- 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/connection.ts +37 -29
- package/src/cmap/connection_pool.ts +17 -3
- package/src/cmap/wire_protocol/on_data.ts +9 -2
- package/src/collection.ts +15 -8
- 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/index.ts +1 -0
- package/src/mongo_client.ts +13 -0
- package/src/mongo_types.ts +38 -0
- package/src/operations/execute_operation.ts +9 -4
- package/src/operations/list_collections.ts +4 -1
- package/src/operations/operation.ts +3 -2
- package/src/sdam/server.ts +31 -18
- package/src/sdam/topology.ts +10 -2
- package/src/utils.ts +79 -6
package/src/sdam/server.ts
CHANGED
|
@@ -36,12 +36,13 @@ import {
|
|
|
36
36
|
needsRetryableWriteLabel
|
|
37
37
|
} from '../error';
|
|
38
38
|
import type { ServerApi } from '../mongo_client';
|
|
39
|
-
import { TypedEventEmitter } from '../mongo_types';
|
|
39
|
+
import { type Abortable, TypedEventEmitter } from '../mongo_types';
|
|
40
40
|
import type { GetMoreOptions } from '../operations/get_more';
|
|
41
41
|
import type { ClientSession } from '../sessions';
|
|
42
42
|
import { type TimeoutContext } from '../timeout';
|
|
43
43
|
import { isTransactionCommand } from '../transactions';
|
|
44
44
|
import {
|
|
45
|
+
abortable,
|
|
45
46
|
type EventEmitterWithState,
|
|
46
47
|
makeStateMachine,
|
|
47
48
|
maxWireVersion,
|
|
@@ -107,7 +108,7 @@ export type ServerEvents = {
|
|
|
107
108
|
/** @internal */
|
|
108
109
|
export type ServerCommandOptions = Omit<CommandOptions, 'timeoutContext' | 'socketTimeoutMS'> & {
|
|
109
110
|
timeoutContext: TimeoutContext;
|
|
110
|
-
};
|
|
111
|
+
} & Abortable;
|
|
111
112
|
|
|
112
113
|
/** @internal */
|
|
113
114
|
export class Server extends TypedEventEmitter<ServerEvents> {
|
|
@@ -285,7 +286,7 @@ export class Server extends TypedEventEmitter<ServerEvents> {
|
|
|
285
286
|
public async command(
|
|
286
287
|
ns: MongoDBNamespace,
|
|
287
288
|
cmd: Document,
|
|
288
|
-
options: ServerCommandOptions,
|
|
289
|
+
{ ...options }: ServerCommandOptions,
|
|
289
290
|
responseType?: MongoDBResponseConstructor
|
|
290
291
|
): Promise<Document> {
|
|
291
292
|
if (ns.db == null || typeof ns === 'string') {
|
|
@@ -296,25 +297,21 @@ export class Server extends TypedEventEmitter<ServerEvents> {
|
|
|
296
297
|
throw new MongoServerClosedError();
|
|
297
298
|
}
|
|
298
299
|
|
|
299
|
-
|
|
300
|
-
const finalOptions = Object.assign({}, options, {
|
|
301
|
-
wireProtocolCommand: false,
|
|
302
|
-
directConnection: this.topology.s.options.directConnection
|
|
303
|
-
});
|
|
300
|
+
options.directConnection = this.topology.s.options.directConnection;
|
|
304
301
|
|
|
305
302
|
// There are cases where we need to flag the read preference not to get sent in
|
|
306
303
|
// the command, such as pre-5.0 servers attempting to perform an aggregate write
|
|
307
304
|
// with a non-primary read preference. In this case the effective read preference
|
|
308
305
|
// (primary) is not the same as the provided and must be removed completely.
|
|
309
|
-
if (
|
|
310
|
-
delete
|
|
306
|
+
if (options.omitReadPreference) {
|
|
307
|
+
delete options.readPreference;
|
|
311
308
|
}
|
|
312
309
|
|
|
313
310
|
if (this.description.iscryptd) {
|
|
314
|
-
|
|
311
|
+
options.omitMaxTimeMS = true;
|
|
315
312
|
}
|
|
316
313
|
|
|
317
|
-
const session =
|
|
314
|
+
const session = options.session;
|
|
318
315
|
let conn = session?.pinnedConnection;
|
|
319
316
|
|
|
320
317
|
this.incrementOperationCount();
|
|
@@ -331,26 +328,35 @@ export class Server extends TypedEventEmitter<ServerEvents> {
|
|
|
331
328
|
}
|
|
332
329
|
}
|
|
333
330
|
|
|
331
|
+
let reauthPromise: Promise<void> | null = null;
|
|
332
|
+
|
|
334
333
|
try {
|
|
335
334
|
try {
|
|
336
|
-
const res = await conn.command(ns, cmd,
|
|
335
|
+
const res = await conn.command(ns, cmd, options, responseType);
|
|
337
336
|
throwIfWriteConcernError(res);
|
|
338
337
|
return res;
|
|
339
338
|
} catch (commandError) {
|
|
340
|
-
throw this.decorateCommandError(conn, cmd,
|
|
339
|
+
throw this.decorateCommandError(conn, cmd, options, commandError);
|
|
341
340
|
}
|
|
342
341
|
} catch (operationError) {
|
|
343
342
|
if (
|
|
344
343
|
operationError instanceof MongoError &&
|
|
345
344
|
operationError.code === MONGODB_ERROR_CODES.Reauthenticate
|
|
346
345
|
) {
|
|
347
|
-
|
|
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
|
+
|
|
348
354
|
try {
|
|
349
|
-
const res = await conn.command(ns, cmd,
|
|
355
|
+
const res = await conn.command(ns, cmd, options, responseType);
|
|
350
356
|
throwIfWriteConcernError(res);
|
|
351
357
|
return res;
|
|
352
358
|
} catch (commandError) {
|
|
353
|
-
throw this.decorateCommandError(conn, cmd,
|
|
359
|
+
throw this.decorateCommandError(conn, cmd, options, commandError);
|
|
354
360
|
}
|
|
355
361
|
} else {
|
|
356
362
|
throw operationError;
|
|
@@ -358,7 +364,14 @@ export class Server extends TypedEventEmitter<ServerEvents> {
|
|
|
358
364
|
} finally {
|
|
359
365
|
this.decrementOperationCount();
|
|
360
366
|
if (session?.pinnedConnection !== conn) {
|
|
361
|
-
|
|
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
|
+
}
|
|
362
375
|
}
|
|
363
376
|
}
|
|
364
377
|
}
|
package/src/sdam/topology.ts
CHANGED
|
@@ -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,
|
|
@@ -525,7 +527,7 @@ export class Topology extends TypedEventEmitter<TopologyEvents> {
|
|
|
525
527
|
*/
|
|
526
528
|
async selectServer(
|
|
527
529
|
selector: string | ReadPreference | ServerSelector,
|
|
528
|
-
options: SelectServerOptions
|
|
530
|
+
options: SelectServerOptions & Abortable
|
|
529
531
|
): Promise<Server> {
|
|
530
532
|
let serverSelector;
|
|
531
533
|
if (typeof selector !== 'function') {
|
|
@@ -602,6 +604,11 @@ export class Topology extends TypedEventEmitter<TopologyEvents> {
|
|
|
602
604
|
previousServer: options.previousServer
|
|
603
605
|
};
|
|
604
606
|
|
|
607
|
+
const abortListener = addAbortListener(options.signal, function () {
|
|
608
|
+
waitQueueMember.cancelled = true;
|
|
609
|
+
reject(this.reason);
|
|
610
|
+
});
|
|
611
|
+
|
|
605
612
|
this.waitQueue.push(waitQueueMember);
|
|
606
613
|
processWaitQueue(this);
|
|
607
614
|
|
|
@@ -647,6 +654,7 @@ export class Topology extends TypedEventEmitter<TopologyEvents> {
|
|
|
647
654
|
// Other server selection error
|
|
648
655
|
throw error;
|
|
649
656
|
} finally {
|
|
657
|
+
abortListener?.[kDispose]();
|
|
650
658
|
if (options.timeoutContext?.clearServerSelectionTimeout) timeout?.clear();
|
|
651
659
|
}
|
|
652
660
|
}
|
package/src/utils.ts
CHANGED
|
@@ -27,6 +27,7 @@ import {
|
|
|
27
27
|
MongoRuntimeError
|
|
28
28
|
} from './error';
|
|
29
29
|
import type { MongoClient } from './mongo_client';
|
|
30
|
+
import { type Abortable } from './mongo_types';
|
|
30
31
|
import type { CommandOperationOptions, OperationParent } from './operations/command';
|
|
31
32
|
import type { Hint, OperationOptions } from './operations/operation';
|
|
32
33
|
import { ReadConcern } from './read_concern';
|
|
@@ -1312,19 +1313,24 @@ export const randomBytes = promisify(crypto.randomBytes);
|
|
|
1312
1313
|
* @param ee - An event emitter that may emit `ev`
|
|
1313
1314
|
* @param name - An event name to wait for
|
|
1314
1315
|
*/
|
|
1315
|
-
export async function once<T>(ee: EventEmitter, name: string): Promise<T> {
|
|
1316
|
+
export async function once<T>(ee: EventEmitter, name: string, options?: Abortable): Promise<T> {
|
|
1317
|
+
options?.signal?.throwIfAborted();
|
|
1318
|
+
|
|
1316
1319
|
const { promise, resolve, reject } = promiseWithResolvers<T>();
|
|
1317
1320
|
const onEvent = (data: T) => resolve(data);
|
|
1318
1321
|
const onError = (error: Error) => reject(error);
|
|
1322
|
+
const abortListener = addAbortListener(options?.signal, function () {
|
|
1323
|
+
reject(this.reason);
|
|
1324
|
+
});
|
|
1319
1325
|
|
|
1320
1326
|
ee.once(name, onEvent).once('error', onError);
|
|
1327
|
+
|
|
1321
1328
|
try {
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
return res;
|
|
1325
|
-
} catch (error) {
|
|
1329
|
+
return await promise;
|
|
1330
|
+
} finally {
|
|
1326
1331
|
ee.off(name, onEvent);
|
|
1327
|
-
|
|
1332
|
+
ee.off('error', onError);
|
|
1333
|
+
abortListener?.[kDispose]();
|
|
1328
1334
|
}
|
|
1329
1335
|
}
|
|
1330
1336
|
|
|
@@ -1431,3 +1437,70 @@ export function decorateDecryptionResult(
|
|
|
1431
1437
|
decorateDecryptionResult(decrypted[k], originalValue, false);
|
|
1432
1438
|
}
|
|
1433
1439
|
}
|
|
1440
|
+
|
|
1441
|
+
/** @internal */
|
|
1442
|
+
export const kDispose: unique symbol = (Symbol.dispose as any) ?? Symbol('dispose');
|
|
1443
|
+
|
|
1444
|
+
/** @internal */
|
|
1445
|
+
export interface Disposable {
|
|
1446
|
+
[kDispose](): void;
|
|
1447
|
+
}
|
|
1448
|
+
|
|
1449
|
+
/**
|
|
1450
|
+
* A utility that helps with writing listener code idiomatically
|
|
1451
|
+
*
|
|
1452
|
+
* @example
|
|
1453
|
+
* ```js
|
|
1454
|
+
* using listener = addAbortListener(signal, function () {
|
|
1455
|
+
* console.log('aborted', this.reason);
|
|
1456
|
+
* });
|
|
1457
|
+
* ```
|
|
1458
|
+
*
|
|
1459
|
+
* @param signal - if exists adds an abort listener
|
|
1460
|
+
* @param listener - the listener to be added to signal
|
|
1461
|
+
* @returns A disposable that will remove the abort listener
|
|
1462
|
+
*/
|
|
1463
|
+
export function addAbortListener(
|
|
1464
|
+
signal: AbortSignal | undefined | null,
|
|
1465
|
+
listener: (this: AbortSignal, event: Event) => void
|
|
1466
|
+
): Disposable | undefined {
|
|
1467
|
+
if (signal == null) return;
|
|
1468
|
+
signal.addEventListener('abort', listener, { once: true });
|
|
1469
|
+
return { [kDispose]: () => signal.removeEventListener('abort', listener) };
|
|
1470
|
+
}
|
|
1471
|
+
|
|
1472
|
+
/**
|
|
1473
|
+
* Takes a promise and races it with a promise wrapping the abort event of the optionally provided signal.
|
|
1474
|
+
* The given promise is _always_ ordered before the signal's abort promise.
|
|
1475
|
+
* When given an already rejected promise and an already aborted signal, the promise's rejection takes precedence.
|
|
1476
|
+
*
|
|
1477
|
+
* Any asynchronous processing in `promise` will continue even after the abort signal has fired,
|
|
1478
|
+
* but control will be returned to the caller
|
|
1479
|
+
*
|
|
1480
|
+
* @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/race
|
|
1481
|
+
*
|
|
1482
|
+
* @param promise - A promise to discard if the signal aborts
|
|
1483
|
+
* @param options - An options object carrying an optional signal
|
|
1484
|
+
*/
|
|
1485
|
+
export async function abortable<T>(
|
|
1486
|
+
promise: Promise<T>,
|
|
1487
|
+
{ signal }: { signal?: AbortSignal }
|
|
1488
|
+
): Promise<T> {
|
|
1489
|
+
if (signal == null) {
|
|
1490
|
+
return await promise;
|
|
1491
|
+
}
|
|
1492
|
+
|
|
1493
|
+
const { promise: aborted, reject } = promiseWithResolvers<never>();
|
|
1494
|
+
|
|
1495
|
+
const abortListener = signal.aborted
|
|
1496
|
+
? reject(signal.reason)
|
|
1497
|
+
: addAbortListener(signal, function () {
|
|
1498
|
+
reject(this.reason);
|
|
1499
|
+
});
|
|
1500
|
+
|
|
1501
|
+
try {
|
|
1502
|
+
return await Promise.race([promise, aborted]);
|
|
1503
|
+
} finally {
|
|
1504
|
+
abortListener?.[kDispose]();
|
|
1505
|
+
}
|
|
1506
|
+
}
|