mongodb 6.12.0 → 6.13.0-dev.20250201.sha.35c703e3
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_aws.js +1 -1
- package/lib/cmap/auth/mongodb_aws.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 +2 -2
- 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_aws.ts +1 -1
- 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/mongo_logger.ts
CHANGED
|
@@ -1,6 +1,24 @@
|
|
|
1
1
|
import { inspect, promisify } from 'util';
|
|
2
|
+
import { isUint8Array } from 'util/types';
|
|
2
3
|
|
|
3
|
-
import {
|
|
4
|
+
import {
|
|
5
|
+
type Binary,
|
|
6
|
+
type BSONRegExp,
|
|
7
|
+
type BSONSymbol,
|
|
8
|
+
type Code,
|
|
9
|
+
type DBRef,
|
|
10
|
+
type Decimal128,
|
|
11
|
+
type Document,
|
|
12
|
+
type Double,
|
|
13
|
+
EJSON,
|
|
14
|
+
type EJSONOptions,
|
|
15
|
+
type Int32,
|
|
16
|
+
type Long,
|
|
17
|
+
type MaxKey,
|
|
18
|
+
type MinKey,
|
|
19
|
+
type ObjectId,
|
|
20
|
+
type Timestamp
|
|
21
|
+
} from './bson';
|
|
4
22
|
import type { CommandStartedEvent } from './cmap/command_monitoring_events';
|
|
5
23
|
import type {
|
|
6
24
|
ConnectionCheckedInEvent,
|
|
@@ -59,7 +77,11 @@ import type {
|
|
|
59
77
|
} from './sdam/server_selection_events';
|
|
60
78
|
import { HostAddress, isPromiseLike, parseUnsignedInteger } from './utils';
|
|
61
79
|
|
|
62
|
-
/**
|
|
80
|
+
/**
|
|
81
|
+
* @public
|
|
82
|
+
* Severity levels align with unix syslog.
|
|
83
|
+
* Most typical driver functions will log to debug.
|
|
84
|
+
*/
|
|
63
85
|
export const SeverityLevel = Object.freeze({
|
|
64
86
|
EMERGENCY: 'emergency',
|
|
65
87
|
ALERT: 'alert',
|
|
@@ -75,7 +97,7 @@ export const SeverityLevel = Object.freeze({
|
|
|
75
97
|
|
|
76
98
|
/** @internal */
|
|
77
99
|
export const DEFAULT_MAX_DOCUMENT_LENGTH = 1000;
|
|
78
|
-
/** @
|
|
100
|
+
/** @public */
|
|
79
101
|
export type SeverityLevel = (typeof SeverityLevel)[keyof typeof SeverityLevel];
|
|
80
102
|
|
|
81
103
|
/** @internal */
|
|
@@ -113,7 +135,7 @@ export const SEVERITY_LEVEL_MAP = new SeverityLevelMap([
|
|
|
113
135
|
[SeverityLevel.TRACE, 8]
|
|
114
136
|
]);
|
|
115
137
|
|
|
116
|
-
/** @
|
|
138
|
+
/** @public */
|
|
117
139
|
export const MongoLoggableComponent = Object.freeze({
|
|
118
140
|
COMMAND: 'command',
|
|
119
141
|
TOPOLOGY: 'topology',
|
|
@@ -122,7 +144,7 @@ export const MongoLoggableComponent = Object.freeze({
|
|
|
122
144
|
CLIENT: 'client'
|
|
123
145
|
} as const);
|
|
124
146
|
|
|
125
|
-
/** @
|
|
147
|
+
/** @public */
|
|
126
148
|
export type MongoLoggableComponent =
|
|
127
149
|
(typeof MongoLoggableComponent)[keyof typeof MongoLoggableComponent];
|
|
128
150
|
|
|
@@ -146,13 +168,13 @@ export interface MongoLoggerEnvOptions {
|
|
|
146
168
|
MONGODB_LOG_PATH?: string;
|
|
147
169
|
}
|
|
148
170
|
|
|
149
|
-
/** @
|
|
171
|
+
/** @public */
|
|
150
172
|
export interface LogComponentSeveritiesClientOptions {
|
|
151
173
|
/** Optional severity level for command component */
|
|
152
174
|
command?: SeverityLevel;
|
|
153
175
|
/** Optional severity level for topology component */
|
|
154
176
|
topology?: SeverityLevel;
|
|
155
|
-
/**
|
|
177
|
+
/** Optional severity level for server selection component */
|
|
156
178
|
serverSelection?: SeverityLevel;
|
|
157
179
|
/** Optional severity level for connection component */
|
|
158
180
|
connection?: SeverityLevel;
|
|
@@ -274,7 +296,7 @@ function resolveSeverityConfiguration(
|
|
|
274
296
|
);
|
|
275
297
|
}
|
|
276
298
|
|
|
277
|
-
/** @
|
|
299
|
+
/** @public */
|
|
278
300
|
export interface Log extends Record<string, any> {
|
|
279
301
|
t: Date;
|
|
280
302
|
c: MongoLoggableComponent;
|
|
@@ -283,10 +305,27 @@ export interface Log extends Record<string, any> {
|
|
|
283
305
|
}
|
|
284
306
|
|
|
285
307
|
/**
|
|
286
|
-
* @
|
|
287
|
-
*
|
|
308
|
+
* @public
|
|
309
|
+
*
|
|
310
|
+
* A custom destination for structured logging messages.
|
|
288
311
|
*/
|
|
289
312
|
export interface MongoDBLogWritable {
|
|
313
|
+
/**
|
|
314
|
+
* This function will be called for every enabled log message.
|
|
315
|
+
*
|
|
316
|
+
* It can be sync or async:
|
|
317
|
+
* - If it is synchronous it will block the driver from proceeding until this method returns.
|
|
318
|
+
* - If it is asynchronous the driver will not await the returned promise. It will attach fulfillment handling (`.then`).
|
|
319
|
+
* If the promise rejects the logger will write an error message to stderr and stop functioning.
|
|
320
|
+
* If the promise resolves the driver proceeds to the next log message (or waits for new ones to occur).
|
|
321
|
+
*
|
|
322
|
+
* Tips:
|
|
323
|
+
* - We recommend writing an async `write` function that _never_ rejects.
|
|
324
|
+
* Instead handle logging errors as necessary to your use case and make the write function a noop, until it can be recovered.
|
|
325
|
+
* - The Log messages are structured but **subject to change** since the intended purpose is informational.
|
|
326
|
+
* Program against this defensively and err on the side of stringifying whatever is passed in to write in some form or another.
|
|
327
|
+
*
|
|
328
|
+
*/
|
|
290
329
|
write(log: Log): PromiseLike<unknown> | unknown;
|
|
291
330
|
}
|
|
292
331
|
|
|
@@ -413,6 +452,20 @@ export interface LogConvertible extends Record<string, any> {
|
|
|
413
452
|
toLog(): Record<string, any>;
|
|
414
453
|
}
|
|
415
454
|
|
|
455
|
+
type BSONObject =
|
|
456
|
+
| BSONRegExp
|
|
457
|
+
| BSONSymbol
|
|
458
|
+
| Code
|
|
459
|
+
| DBRef
|
|
460
|
+
| Decimal128
|
|
461
|
+
| Double
|
|
462
|
+
| Int32
|
|
463
|
+
| Long
|
|
464
|
+
| MaxKey
|
|
465
|
+
| MinKey
|
|
466
|
+
| ObjectId
|
|
467
|
+
| Timestamp
|
|
468
|
+
| Binary;
|
|
416
469
|
/** @internal */
|
|
417
470
|
export function stringifyWithMaxLen(
|
|
418
471
|
value: any,
|
|
@@ -421,13 +474,107 @@ export function stringifyWithMaxLen(
|
|
|
421
474
|
): string {
|
|
422
475
|
let strToTruncate = '';
|
|
423
476
|
|
|
477
|
+
let currentLength = 0;
|
|
478
|
+
const maxDocumentLengthEnsurer = function maxDocumentLengthEnsurer(key: string, value: any) {
|
|
479
|
+
if (currentLength >= maxDocumentLength) {
|
|
480
|
+
return undefined;
|
|
481
|
+
}
|
|
482
|
+
// Account for root document
|
|
483
|
+
if (key === '') {
|
|
484
|
+
// Account for starting brace
|
|
485
|
+
currentLength += 1;
|
|
486
|
+
return value;
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
// +4 accounts for 2 quotation marks, colon and comma after value
|
|
490
|
+
// Note that this potentially undercounts since it does not account for escape sequences which
|
|
491
|
+
// will have an additional backslash added to them once passed through JSON.stringify.
|
|
492
|
+
currentLength += key.length + 4;
|
|
493
|
+
|
|
494
|
+
if (value == null) return value;
|
|
495
|
+
|
|
496
|
+
switch (typeof value) {
|
|
497
|
+
case 'string':
|
|
498
|
+
// +2 accounts for quotes
|
|
499
|
+
// Note that this potentially undercounts similarly to the key length calculation
|
|
500
|
+
currentLength += value.length + 2;
|
|
501
|
+
break;
|
|
502
|
+
case 'number':
|
|
503
|
+
case 'bigint':
|
|
504
|
+
currentLength += String(value).length;
|
|
505
|
+
break;
|
|
506
|
+
case 'boolean':
|
|
507
|
+
currentLength += value ? 4 : 5;
|
|
508
|
+
break;
|
|
509
|
+
case 'object':
|
|
510
|
+
if (isUint8Array(value)) {
|
|
511
|
+
// '{"$binary":{"base64":"<base64 string>","subType":"XX"}}'
|
|
512
|
+
// This is an estimate based on the fact that the base64 is approximately 1.33x the length of
|
|
513
|
+
// the actual binary sequence https://en.wikipedia.org/wiki/Base64
|
|
514
|
+
currentLength += (22 + value.byteLength + value.byteLength * 0.33 + 18) | 0;
|
|
515
|
+
} else if ('_bsontype' in value) {
|
|
516
|
+
const v = value as BSONObject;
|
|
517
|
+
switch (v._bsontype) {
|
|
518
|
+
case 'Int32':
|
|
519
|
+
currentLength += String(v.value).length;
|
|
520
|
+
break;
|
|
521
|
+
case 'Double':
|
|
522
|
+
// Account for representing integers as <value>.0
|
|
523
|
+
currentLength +=
|
|
524
|
+
(v.value | 0) === v.value ? String(v.value).length + 2 : String(v.value).length;
|
|
525
|
+
break;
|
|
526
|
+
case 'Long':
|
|
527
|
+
currentLength += v.toString().length;
|
|
528
|
+
break;
|
|
529
|
+
case 'ObjectId':
|
|
530
|
+
// '{"$oid":"XXXXXXXXXXXXXXXXXXXXXXXX"}'
|
|
531
|
+
currentLength += 35;
|
|
532
|
+
break;
|
|
533
|
+
case 'MaxKey':
|
|
534
|
+
case 'MinKey':
|
|
535
|
+
// '{"$maxKey":1}' or '{"$minKey":1}'
|
|
536
|
+
currentLength += 13;
|
|
537
|
+
break;
|
|
538
|
+
case 'Binary':
|
|
539
|
+
// '{"$binary":{"base64":"<base64 string>","subType":"XX"}}'
|
|
540
|
+
// This is an estimate based on the fact that the base64 is approximately 1.33x the length of
|
|
541
|
+
// the actual binary sequence https://en.wikipedia.org/wiki/Base64
|
|
542
|
+
currentLength += (22 + value.position + value.position * 0.33 + 18) | 0;
|
|
543
|
+
break;
|
|
544
|
+
case 'Timestamp':
|
|
545
|
+
// '{"$timestamp":{"t":<t>,"i":<i>}}'
|
|
546
|
+
currentLength += 19 + String(v.t).length + 5 + String(v.i).length + 2;
|
|
547
|
+
break;
|
|
548
|
+
case 'Code':
|
|
549
|
+
// '{"$code":"<code>"}' or '{"$code":"<code>","$scope":<scope>}'
|
|
550
|
+
if (v.scope == null) {
|
|
551
|
+
currentLength += v.code.length + 10 + 2;
|
|
552
|
+
} else {
|
|
553
|
+
// Ignoring actual scope object, so this undercounts by a significant amount
|
|
554
|
+
currentLength += v.code.length + 10 + 11;
|
|
555
|
+
}
|
|
556
|
+
break;
|
|
557
|
+
case 'BSONRegExp':
|
|
558
|
+
// '{"$regularExpression":{"pattern":"<pattern>","options":"<options>"}}'
|
|
559
|
+
currentLength += 34 + v.pattern.length + 13 + v.options.length + 3;
|
|
560
|
+
break;
|
|
561
|
+
}
|
|
562
|
+
}
|
|
563
|
+
}
|
|
564
|
+
return value;
|
|
565
|
+
};
|
|
566
|
+
|
|
424
567
|
if (typeof value === 'string') {
|
|
425
568
|
strToTruncate = value;
|
|
426
569
|
} else if (typeof value === 'function') {
|
|
427
570
|
strToTruncate = value.name;
|
|
428
571
|
} else {
|
|
429
572
|
try {
|
|
430
|
-
|
|
573
|
+
if (maxDocumentLength !== 0) {
|
|
574
|
+
strToTruncate = EJSON.stringify(value, maxDocumentLengthEnsurer, 0, options);
|
|
575
|
+
} else {
|
|
576
|
+
strToTruncate = EJSON.stringify(value, options);
|
|
577
|
+
}
|
|
431
578
|
} catch (e) {
|
|
432
579
|
strToTruncate = `Extended JSON serialization failed with: ${e.message}`;
|
|
433
580
|
}
|
package/src/mongo_types.ts
CHANGED
|
@@ -474,6 +474,44 @@ export class TypedEventEmitter<Events extends EventsDescription> extends EventEm
|
|
|
474
474
|
/** @public */
|
|
475
475
|
export class CancellationToken extends TypedEventEmitter<{ cancel(): void }> {}
|
|
476
476
|
|
|
477
|
+
/** @public */
|
|
478
|
+
export type Abortable = {
|
|
479
|
+
/**
|
|
480
|
+
* @experimental
|
|
481
|
+
* When provided, the corresponding `AbortController` can be used to abort an asynchronous action.
|
|
482
|
+
*
|
|
483
|
+
* The `signal.reason` value is used as the error thrown.
|
|
484
|
+
*
|
|
485
|
+
* @remarks
|
|
486
|
+
* **NOTE:** If an abort signal aborts an operation while the driver is writing to the underlying
|
|
487
|
+
* socket or reading the response from the server, the socket will be closed.
|
|
488
|
+
* If signals are aborted at a high rate during socket read/writes this can lead to a high rate of connection reestablishment.
|
|
489
|
+
*
|
|
490
|
+
* We plan to mitigate this in a future release, please follow NODE-6062 (`timeoutMS` expiration suffers the same limitation).
|
|
491
|
+
*
|
|
492
|
+
* AbortSignals are likely a best fit for human interactive interruption (ex. ctrl-C) where the frequency
|
|
493
|
+
* of cancellation is reasonably low. If a signal is programmatically aborted for 100s of operations you can empty
|
|
494
|
+
* the driver's connection pool.
|
|
495
|
+
*
|
|
496
|
+
* @example
|
|
497
|
+
* ```js
|
|
498
|
+
* const controller = new AbortController();
|
|
499
|
+
* const { signal } = controller;
|
|
500
|
+
* process.on('SIGINT', () => controller.abort(new Error('^C pressed')));
|
|
501
|
+
*
|
|
502
|
+
* try {
|
|
503
|
+
* const res = await fetch('...', { signal });
|
|
504
|
+
* await collection.findOne(await res.json(), { signal });
|
|
505
|
+
* catch (error) {
|
|
506
|
+
* if (error === signal.reason) {
|
|
507
|
+
* // signal abort error handling
|
|
508
|
+
* }
|
|
509
|
+
* }
|
|
510
|
+
* ```
|
|
511
|
+
*/
|
|
512
|
+
signal?: AbortSignal | undefined;
|
|
513
|
+
};
|
|
514
|
+
|
|
477
515
|
/**
|
|
478
516
|
* Helper types for dot-notation filter attributes
|
|
479
517
|
*/
|
|
@@ -25,7 +25,7 @@ import {
|
|
|
25
25
|
import type { Topology } from '../sdam/topology';
|
|
26
26
|
import type { ClientSession } from '../sessions';
|
|
27
27
|
import { TimeoutContext } from '../timeout';
|
|
28
|
-
import { supportsRetryableWrites } from '../utils';
|
|
28
|
+
import { abortable, supportsRetryableWrites } from '../utils';
|
|
29
29
|
import { AbstractOperation, Aspect } from './operation';
|
|
30
30
|
|
|
31
31
|
const MMAPv1_RETRY_WRITES_ERROR_CODE = MONGODB_ERROR_CODES.IllegalOperation;
|
|
@@ -64,7 +64,10 @@ export async function executeOperation<
|
|
|
64
64
|
throw new MongoRuntimeError('This method requires a valid operation instance');
|
|
65
65
|
}
|
|
66
66
|
|
|
67
|
-
const topology =
|
|
67
|
+
const topology =
|
|
68
|
+
client.topology == null
|
|
69
|
+
? await abortable(autoConnect(client), operation.options)
|
|
70
|
+
: client.topology;
|
|
68
71
|
|
|
69
72
|
// The driver sessions spec mandates that we implicitly create sessions for operations
|
|
70
73
|
// that are not explicitly provided with a session.
|
|
@@ -131,7 +134,7 @@ async function autoConnect(client: MongoClient): Promise<Topology> {
|
|
|
131
134
|
if (client.s.hasBeenClosed) {
|
|
132
135
|
throw new MongoNotConnectedError('Client must be connected before running operations');
|
|
133
136
|
}
|
|
134
|
-
client.s.options
|
|
137
|
+
client.s.options.__skipPingOnConnect = true;
|
|
135
138
|
try {
|
|
136
139
|
await client.connect();
|
|
137
140
|
if (client.topology == null) {
|
|
@@ -141,7 +144,7 @@ async function autoConnect(client: MongoClient): Promise<Topology> {
|
|
|
141
144
|
}
|
|
142
145
|
return client.topology;
|
|
143
146
|
} finally {
|
|
144
|
-
delete client.s.options
|
|
147
|
+
delete client.s.options.__skipPingOnConnect;
|
|
145
148
|
}
|
|
146
149
|
}
|
|
147
150
|
return client.topology;
|
|
@@ -198,7 +201,8 @@ async function tryOperation<
|
|
|
198
201
|
let server = await topology.selectServer(selector, {
|
|
199
202
|
session,
|
|
200
203
|
operationName: operation.commandName,
|
|
201
|
-
timeoutContext
|
|
204
|
+
timeoutContext,
|
|
205
|
+
signal: operation.options.signal
|
|
202
206
|
});
|
|
203
207
|
|
|
204
208
|
const hasReadAspect = operation.hasAspect(Aspect.READ_OPERATION);
|
|
@@ -260,7 +264,8 @@ async function tryOperation<
|
|
|
260
264
|
server = await topology.selectServer(selector, {
|
|
261
265
|
session,
|
|
262
266
|
operationName: operation.commandName,
|
|
263
|
-
previousServer
|
|
267
|
+
previousServer,
|
|
268
|
+
signal: operation.options.signal
|
|
264
269
|
});
|
|
265
270
|
|
|
266
271
|
if (hasWriteAspect && !supportsRetryableWrites(server)) {
|
|
@@ -2,6 +2,7 @@ import type { Binary, Document } from '../bson';
|
|
|
2
2
|
import { CursorResponse } from '../cmap/wire_protocol/responses';
|
|
3
3
|
import { type CursorTimeoutContext, type CursorTimeoutMode } from '../cursor/abstract_cursor';
|
|
4
4
|
import type { Db } from '../db';
|
|
5
|
+
import { type Abortable } from '../mongo_types';
|
|
5
6
|
import type { Server } from '../sdam/server';
|
|
6
7
|
import type { ClientSession } from '../sessions';
|
|
7
8
|
import { type TimeoutContext } from '../timeout';
|
|
@@ -10,7 +11,9 @@ import { CommandOperation, type CommandOperationOptions } from './command';
|
|
|
10
11
|
import { Aspect, defineAspects } from './operation';
|
|
11
12
|
|
|
12
13
|
/** @public */
|
|
13
|
-
export interface ListCollectionsOptions
|
|
14
|
+
export interface ListCollectionsOptions
|
|
15
|
+
extends Omit<CommandOperationOptions, 'writeConcern'>,
|
|
16
|
+
Abortable {
|
|
14
17
|
/** Since 4.0: If true, will only return the collection name in the response, and will omit additional info */
|
|
15
18
|
nameOnly?: boolean;
|
|
16
19
|
/** Since 4.0: If true and nameOnly is true, allows a user without the required privilege (i.e. listCollections action on the database) to run the command when access control is enforced. */
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { type BSONSerializeOptions, type Document, resolveBSONOptions } from '../bson';
|
|
2
|
+
import { type Abortable } from '../mongo_types';
|
|
2
3
|
import { ReadPreference, type ReadPreferenceLike } from '../read_preference';
|
|
3
4
|
import type { Server } from '../sdam/server';
|
|
4
5
|
import type { ClientSession } from '../sessions';
|
|
@@ -42,9 +43,6 @@ export interface OperationOptions extends BSONSerializeOptions {
|
|
|
42
43
|
timeoutMS?: number;
|
|
43
44
|
}
|
|
44
45
|
|
|
45
|
-
/** @internal */
|
|
46
|
-
const kSession = Symbol('session');
|
|
47
|
-
|
|
48
46
|
/**
|
|
49
47
|
* This class acts as a parent class for any operation and is responsible for setting this.options,
|
|
50
48
|
* as well as setting and getting a session.
|
|
@@ -62,16 +60,16 @@ export abstract class AbstractOperation<TResult = any> {
|
|
|
62
60
|
// BSON serialization options
|
|
63
61
|
bsonOptions?: BSONSerializeOptions;
|
|
64
62
|
|
|
65
|
-
options: OperationOptions;
|
|
63
|
+
options: OperationOptions & Abortable;
|
|
66
64
|
|
|
67
65
|
/** Specifies the time an operation will run until it throws a timeout error. */
|
|
68
66
|
timeoutMS?: number;
|
|
69
67
|
|
|
70
|
-
|
|
68
|
+
private _session: ClientSession | undefined;
|
|
71
69
|
|
|
72
70
|
static aspects?: Set<symbol>;
|
|
73
71
|
|
|
74
|
-
constructor(options: OperationOptions = {}) {
|
|
72
|
+
constructor(options: OperationOptions & Abortable = {}) {
|
|
75
73
|
this.readPreference = this.hasAspect(Aspect.WRITE_OPERATION)
|
|
76
74
|
? ReadPreference.primary
|
|
77
75
|
: (ReadPreference.fromOptions(options) ?? ReadPreference.primary);
|
|
@@ -79,7 +77,7 @@ export abstract class AbstractOperation<TResult = any> {
|
|
|
79
77
|
// Pull the BSON serialize options from the already-resolved options
|
|
80
78
|
this.bsonOptions = resolveBSONOptions(options);
|
|
81
79
|
|
|
82
|
-
this
|
|
80
|
+
this._session = options.session != null ? options.session : undefined;
|
|
83
81
|
|
|
84
82
|
this.options = options;
|
|
85
83
|
this.bypassPinningCheck = !!options.bypassPinningCheck;
|
|
@@ -105,12 +103,13 @@ export abstract class AbstractOperation<TResult = any> {
|
|
|
105
103
|
return ctor.aspects.has(aspect);
|
|
106
104
|
}
|
|
107
105
|
|
|
106
|
+
// Make sure the session is not writable from outside this class.
|
|
108
107
|
get session(): ClientSession | undefined {
|
|
109
|
-
return this
|
|
108
|
+
return this._session;
|
|
110
109
|
}
|
|
111
110
|
|
|
112
111
|
clearSession() {
|
|
113
|
-
this
|
|
112
|
+
this._session = undefined;
|
|
114
113
|
}
|
|
115
114
|
|
|
116
115
|
resetBatch(): boolean {
|
package/src/sdam/monitor.ts
CHANGED
|
@@ -25,13 +25,6 @@ import {
|
|
|
25
25
|
import { Server } from './server';
|
|
26
26
|
import type { TopologyVersion } from './server_description';
|
|
27
27
|
|
|
28
|
-
/** @internal */
|
|
29
|
-
const kServer = Symbol('server');
|
|
30
|
-
/** @internal */
|
|
31
|
-
const kMonitorId = Symbol('monitorId');
|
|
32
|
-
/** @internal */
|
|
33
|
-
const kCancellationToken = Symbol('cancellationToken');
|
|
34
|
-
|
|
35
28
|
const STATE_IDLE = 'idle';
|
|
36
29
|
const STATE_MONITORING = 'monitoring';
|
|
37
30
|
const stateTransition = makeStateMachine({
|
|
@@ -96,11 +89,11 @@ export class Monitor extends TypedEventEmitter<MonitorEvents> {
|
|
|
96
89
|
>;
|
|
97
90
|
connectOptions: ConnectionOptions;
|
|
98
91
|
isRunningInFaasEnv: boolean;
|
|
99
|
-
|
|
92
|
+
server: Server;
|
|
100
93
|
connection: Connection | null;
|
|
101
|
-
|
|
94
|
+
cancellationToken: CancellationToken;
|
|
102
95
|
/** @internal */
|
|
103
|
-
|
|
96
|
+
monitorId?: MonitorInterval;
|
|
104
97
|
rttPinger?: RTTPinger;
|
|
105
98
|
/** @internal */
|
|
106
99
|
override component = MongoLoggableComponent.TOPOLOGY;
|
|
@@ -110,11 +103,11 @@ export class Monitor extends TypedEventEmitter<MonitorEvents> {
|
|
|
110
103
|
constructor(server: Server, options: MonitorOptions) {
|
|
111
104
|
super();
|
|
112
105
|
|
|
113
|
-
this
|
|
106
|
+
this.server = server;
|
|
114
107
|
this.connection = null;
|
|
115
|
-
this
|
|
116
|
-
this
|
|
117
|
-
this
|
|
108
|
+
this.cancellationToken = new CancellationToken();
|
|
109
|
+
this.cancellationToken.setMaxListeners(Infinity);
|
|
110
|
+
this.monitorId = undefined;
|
|
118
111
|
this.s = {
|
|
119
112
|
state: STATE_CLOSED
|
|
120
113
|
};
|
|
@@ -126,10 +119,10 @@ export class Monitor extends TypedEventEmitter<MonitorEvents> {
|
|
|
126
119
|
serverMonitoringMode: options.serverMonitoringMode
|
|
127
120
|
});
|
|
128
121
|
this.isRunningInFaasEnv = getFAASEnv() != null;
|
|
129
|
-
this.mongoLogger = this
|
|
122
|
+
this.mongoLogger = this.server.topology.client?.mongoLogger;
|
|
130
123
|
this.rttSampler = new RTTSampler(10);
|
|
131
124
|
|
|
132
|
-
const cancellationToken = this
|
|
125
|
+
const cancellationToken = this.cancellationToken;
|
|
133
126
|
// TODO: refactor this to pull it directly from the pool, requires new ConnectionPool integration
|
|
134
127
|
const connectOptions = {
|
|
135
128
|
id: '<monitor>' as const,
|
|
@@ -162,7 +155,7 @@ export class Monitor extends TypedEventEmitter<MonitorEvents> {
|
|
|
162
155
|
// start
|
|
163
156
|
const heartbeatFrequencyMS = this.options.heartbeatFrequencyMS;
|
|
164
157
|
const minHeartbeatFrequencyMS = this.options.minHeartbeatFrequencyMS;
|
|
165
|
-
this
|
|
158
|
+
this.monitorId = new MonitorInterval(monitorServer(this), {
|
|
166
159
|
heartbeatFrequencyMS: heartbeatFrequencyMS,
|
|
167
160
|
minHeartbeatFrequencyMS: minHeartbeatFrequencyMS,
|
|
168
161
|
immediate: true
|
|
@@ -174,11 +167,11 @@ export class Monitor extends TypedEventEmitter<MonitorEvents> {
|
|
|
174
167
|
return;
|
|
175
168
|
}
|
|
176
169
|
|
|
177
|
-
this
|
|
170
|
+
this.monitorId?.wake();
|
|
178
171
|
}
|
|
179
172
|
|
|
180
173
|
reset(): void {
|
|
181
|
-
const topologyVersion = this
|
|
174
|
+
const topologyVersion = this.server.description.topologyVersion;
|
|
182
175
|
if (isInCloseState(this) || topologyVersion == null) {
|
|
183
176
|
return;
|
|
184
177
|
}
|
|
@@ -192,7 +185,7 @@ export class Monitor extends TypedEventEmitter<MonitorEvents> {
|
|
|
192
185
|
// restart monitoring
|
|
193
186
|
const heartbeatFrequencyMS = this.options.heartbeatFrequencyMS;
|
|
194
187
|
const minHeartbeatFrequencyMS = this.options.minHeartbeatFrequencyMS;
|
|
195
|
-
this
|
|
188
|
+
this.monitorId = new MonitorInterval(monitorServer(this), {
|
|
196
189
|
heartbeatFrequencyMS: heartbeatFrequencyMS,
|
|
197
190
|
minHeartbeatFrequencyMS: minHeartbeatFrequencyMS
|
|
198
191
|
});
|
|
@@ -233,13 +226,13 @@ export class Monitor extends TypedEventEmitter<MonitorEvents> {
|
|
|
233
226
|
}
|
|
234
227
|
|
|
235
228
|
function resetMonitorState(monitor: Monitor) {
|
|
236
|
-
monitor
|
|
237
|
-
monitor
|
|
229
|
+
monitor.monitorId?.stop();
|
|
230
|
+
monitor.monitorId = undefined;
|
|
238
231
|
|
|
239
232
|
monitor.rttPinger?.close();
|
|
240
233
|
monitor.rttPinger = undefined;
|
|
241
234
|
|
|
242
|
-
monitor
|
|
235
|
+
monitor.cancellationToken.emit('cancel');
|
|
243
236
|
|
|
244
237
|
monitor.connection?.destroy();
|
|
245
238
|
monitor.connection = null;
|
|
@@ -266,11 +259,11 @@ function useStreamingProtocol(monitor: Monitor, topologyVersion: TopologyVersion
|
|
|
266
259
|
function checkServer(monitor: Monitor, callback: Callback<Document | null>) {
|
|
267
260
|
let start: number;
|
|
268
261
|
let awaited: boolean;
|
|
269
|
-
const topologyVersion = monitor
|
|
262
|
+
const topologyVersion = monitor.server.description.topologyVersion;
|
|
270
263
|
const isAwaitable = useStreamingProtocol(monitor, topologyVersion);
|
|
271
264
|
monitor.emitAndLogHeartbeat(
|
|
272
265
|
Server.SERVER_HEARTBEAT_STARTED,
|
|
273
|
-
monitor
|
|
266
|
+
monitor.server.topology.s.id,
|
|
274
267
|
undefined,
|
|
275
268
|
new ServerHeartbeatStartedEvent(monitor.address, isAwaitable)
|
|
276
269
|
);
|
|
@@ -280,7 +273,7 @@ function checkServer(monitor: Monitor, callback: Callback<Document | null>) {
|
|
|
280
273
|
monitor.connection = null;
|
|
281
274
|
monitor.emitAndLogHeartbeat(
|
|
282
275
|
Server.SERVER_HEARTBEAT_FAILED,
|
|
283
|
-
monitor
|
|
276
|
+
monitor.server.topology.s.id,
|
|
284
277
|
undefined,
|
|
285
278
|
new ServerHeartbeatFailedEvent(monitor.address, calculateDurationInMs(start), err, awaited)
|
|
286
279
|
);
|
|
@@ -315,7 +308,7 @@ function checkServer(monitor: Monitor, callback: Callback<Document | null>) {
|
|
|
315
308
|
|
|
316
309
|
monitor.emitAndLogHeartbeat(
|
|
317
310
|
Server.SERVER_HEARTBEAT_SUCCEEDED,
|
|
318
|
-
monitor
|
|
311
|
+
monitor.server.topology.s.id,
|
|
319
312
|
hello.connectionId,
|
|
320
313
|
new ServerHeartbeatSucceededEvent(monitor.address, duration, hello, isAwaitable)
|
|
321
314
|
);
|
|
@@ -325,7 +318,7 @@ function checkServer(monitor: Monitor, callback: Callback<Document | null>) {
|
|
|
325
318
|
// event, otherwise the "check" is complete and return to the main monitor loop
|
|
326
319
|
monitor.emitAndLogHeartbeat(
|
|
327
320
|
Server.SERVER_HEARTBEAT_STARTED,
|
|
328
|
-
monitor
|
|
321
|
+
monitor.server.topology.s.id,
|
|
329
322
|
undefined,
|
|
330
323
|
new ServerHeartbeatStartedEvent(monitor.address, true)
|
|
331
324
|
);
|
|
@@ -378,7 +371,6 @@ function checkServer(monitor: Monitor, callback: Callback<Document | null>) {
|
|
|
378
371
|
awaited = false;
|
|
379
372
|
connection
|
|
380
373
|
.command(ns('admin.$cmd'), cmd, options)
|
|
381
|
-
|
|
382
374
|
.then(onHeartbeatSucceeded, onHeartbeatFailed);
|
|
383
375
|
|
|
384
376
|
return;
|
|
@@ -409,7 +401,7 @@ function checkServer(monitor: Monitor, callback: Callback<Document | null>) {
|
|
|
409
401
|
monitor.connection = connection;
|
|
410
402
|
monitor.emitAndLogHeartbeat(
|
|
411
403
|
Server.SERVER_HEARTBEAT_SUCCEEDED,
|
|
412
|
-
monitor
|
|
404
|
+
monitor.server.topology.s.id,
|
|
413
405
|
connection.hello?.connectionId,
|
|
414
406
|
new ServerHeartbeatSucceededEvent(
|
|
415
407
|
monitor.address,
|
|
@@ -447,7 +439,7 @@ function monitorServer(monitor: Monitor) {
|
|
|
447
439
|
checkServer(monitor, (err, hello) => {
|
|
448
440
|
if (err) {
|
|
449
441
|
// otherwise an error occurred on initial discovery, also bail
|
|
450
|
-
if (monitor
|
|
442
|
+
if (monitor.server.description.type === ServerType.Unknown) {
|
|
451
443
|
return done();
|
|
452
444
|
}
|
|
453
445
|
}
|
|
@@ -456,7 +448,7 @@ function monitorServer(monitor: Monitor) {
|
|
|
456
448
|
if (useStreamingProtocol(monitor, hello?.topologyVersion)) {
|
|
457
449
|
setTimeout(() => {
|
|
458
450
|
if (!isInCloseState(monitor)) {
|
|
459
|
-
monitor
|
|
451
|
+
monitor.monitorId?.wake();
|
|
460
452
|
}
|
|
461
453
|
}, 0);
|
|
462
454
|
}
|
|
@@ -484,9 +476,9 @@ export interface RTTPingerOptions extends ConnectionOptions {
|
|
|
484
476
|
export class RTTPinger {
|
|
485
477
|
connection?: Connection;
|
|
486
478
|
/** @internal */
|
|
487
|
-
|
|
479
|
+
cancellationToken: CancellationToken;
|
|
488
480
|
/** @internal */
|
|
489
|
-
|
|
481
|
+
monitorId: NodeJS.Timeout;
|
|
490
482
|
/** @internal */
|
|
491
483
|
monitor: Monitor;
|
|
492
484
|
closed: boolean;
|
|
@@ -495,13 +487,13 @@ export class RTTPinger {
|
|
|
495
487
|
|
|
496
488
|
constructor(monitor: Monitor) {
|
|
497
489
|
this.connection = undefined;
|
|
498
|
-
this
|
|
490
|
+
this.cancellationToken = monitor.cancellationToken;
|
|
499
491
|
this.closed = false;
|
|
500
492
|
this.monitor = monitor;
|
|
501
493
|
this.latestRtt = monitor.latestRtt ?? undefined;
|
|
502
494
|
|
|
503
495
|
const heartbeatFrequencyMS = monitor.options.heartbeatFrequencyMS;
|
|
504
|
-
this
|
|
496
|
+
this.monitorId = setTimeout(() => this.measureRoundTripTime(), heartbeatFrequencyMS);
|
|
505
497
|
}
|
|
506
498
|
|
|
507
499
|
get roundTripTime(): number {
|
|
@@ -514,7 +506,7 @@ export class RTTPinger {
|
|
|
514
506
|
|
|
515
507
|
close(): void {
|
|
516
508
|
this.closed = true;
|
|
517
|
-
clearTimeout(this
|
|
509
|
+
clearTimeout(this.monitorId);
|
|
518
510
|
|
|
519
511
|
this.connection?.destroy();
|
|
520
512
|
this.connection = undefined;
|
|
@@ -531,7 +523,7 @@ export class RTTPinger {
|
|
|
531
523
|
}
|
|
532
524
|
|
|
533
525
|
this.latestRtt = calculateDurationInMs(start);
|
|
534
|
-
this
|
|
526
|
+
this.monitorId = setTimeout(
|
|
535
527
|
() => this.measureRoundTripTime(),
|
|
536
528
|
this.monitor.options.heartbeatFrequencyMS
|
|
537
529
|
);
|