mongodb 6.4.0 → 6.5.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/bulk/common.js +16 -24
- package/lib/bulk/common.js.map +1 -1
- package/lib/cmap/commands.js +0 -4
- package/lib/cmap/commands.js.map +1 -1
- package/lib/cmap/connect.js +3 -7
- package/lib/cmap/connect.js.map +1 -1
- package/lib/cmap/connection.js +57 -44
- package/lib/cmap/connection.js.map +1 -1
- package/lib/cmap/connection_pool.js +12 -20
- package/lib/cmap/connection_pool.js.map +1 -1
- package/lib/cmap/handshake/client_metadata.js +44 -1
- package/lib/cmap/handshake/client_metadata.js.map +1 -1
- package/lib/cmap/wire_protocol/on_data.js +2 -14
- package/lib/cmap/wire_protocol/on_data.js.map +1 -1
- package/lib/cmap/wire_protocol/shared.js +2 -6
- package/lib/cmap/wire_protocol/shared.js.map +1 -1
- package/lib/connection_string.js +12 -3
- package/lib/connection_string.js.map +1 -1
- package/lib/error.js +6 -1
- package/lib/error.js.map +1 -1
- package/lib/index.js.map +1 -1
- package/lib/mongo_client.js +6 -16
- package/lib/mongo_client.js.map +1 -1
- package/lib/mongo_logger.js +2 -1
- package/lib/mongo_logger.js.map +1 -1
- package/lib/operations/common_functions.js +7 -6
- package/lib/operations/common_functions.js.map +1 -1
- package/lib/operations/insert.js +4 -2
- package/lib/operations/insert.js.map +1 -1
- package/lib/sdam/monitor.js +7 -7
- package/lib/sdam/monitor.js.map +1 -1
- package/lib/sdam/server.js +8 -17
- package/lib/sdam/server.js.map +1 -1
- package/lib/sdam/topology.js +26 -34
- package/lib/sdam/topology.js.map +1 -1
- package/lib/sdam/topology_description.js.map +1 -1
- package/lib/utils.js +26 -34
- package/lib/utils.js.map +1 -1
- package/mongodb.d.ts +13 -4
- package/package.json +2 -2
- package/src/bulk/common.ts +21 -30
- package/src/cmap/commands.ts +1 -7
- package/src/cmap/connect.ts +4 -9
- package/src/cmap/connection.ts +61 -67
- package/src/cmap/connection_pool.ts +20 -33
- package/src/cmap/handshake/client_metadata.ts +54 -4
- package/src/cmap/stream_description.ts +1 -1
- package/src/cmap/wire_protocol/on_data.ts +2 -16
- package/src/cmap/wire_protocol/shared.ts +2 -6
- package/src/connection_string.ts +15 -4
- package/src/error.ts +11 -1
- package/src/index.ts +0 -1
- package/src/mongo_client.ts +11 -15
- package/src/mongo_logger.ts +2 -1
- package/src/operations/common_functions.ts +16 -5
- package/src/operations/insert.ts +5 -3
- package/src/sdam/monitor.ts +7 -7
- package/src/sdam/server.ts +12 -24
- package/src/sdam/topology.ts +31 -47
- package/src/sdam/topology_description.ts +1 -1
- package/src/utils.ts +25 -40
|
@@ -16,11 +16,9 @@ type PendingPromises = Omit<
|
|
|
16
16
|
* https://nodejs.org/api/events.html#eventsonemitter-eventname-options
|
|
17
17
|
*
|
|
18
18
|
* Returns an AsyncIterator that iterates each 'data' event emitted from emitter.
|
|
19
|
-
* It will reject upon an error event
|
|
19
|
+
* It will reject upon an error event.
|
|
20
20
|
*/
|
|
21
|
-
export function onData(emitter: EventEmitter
|
|
22
|
-
const signal = options.signal;
|
|
23
|
-
|
|
21
|
+
export function onData(emitter: EventEmitter) {
|
|
24
22
|
// Setup pending events and pending promise lists
|
|
25
23
|
/**
|
|
26
24
|
* When the caller has not yet called .next(), we store the
|
|
@@ -89,19 +87,8 @@ export function onData(emitter: EventEmitter, options: { signal: AbortSignal })
|
|
|
89
87
|
emitter.on('data', eventHandler);
|
|
90
88
|
emitter.on('error', errorHandler);
|
|
91
89
|
|
|
92
|
-
if (signal.aborted) {
|
|
93
|
-
// If the signal is aborted, set up the first .next() call to be a rejection
|
|
94
|
-
queueMicrotask(abortListener);
|
|
95
|
-
} else {
|
|
96
|
-
signal.addEventListener('abort', abortListener, { once: true });
|
|
97
|
-
}
|
|
98
|
-
|
|
99
90
|
return iterator;
|
|
100
91
|
|
|
101
|
-
function abortListener() {
|
|
102
|
-
errorHandler(signal.reason);
|
|
103
|
-
}
|
|
104
|
-
|
|
105
92
|
function eventHandler(value: Buffer) {
|
|
106
93
|
const promise = unconsumedPromises.shift();
|
|
107
94
|
if (promise != null) promise.resolve({ value, done: false });
|
|
@@ -119,7 +106,6 @@ export function onData(emitter: EventEmitter, options: { signal: AbortSignal })
|
|
|
119
106
|
// Adding event handlers
|
|
120
107
|
emitter.off('data', eventHandler);
|
|
121
108
|
emitter.off('error', errorHandler);
|
|
122
|
-
signal.removeEventListener('abort', abortListener);
|
|
123
109
|
finished = true;
|
|
124
110
|
const doneResult = { value: undefined, done: finished } as const;
|
|
125
111
|
|
|
@@ -13,12 +13,8 @@ export interface ReadPreferenceOption {
|
|
|
13
13
|
}
|
|
14
14
|
|
|
15
15
|
export function getReadPreference(options?: ReadPreferenceOption): ReadPreference {
|
|
16
|
-
// Default to command version of the readPreference
|
|
16
|
+
// Default to command version of the readPreference.
|
|
17
17
|
let readPreference = options?.readPreference ?? ReadPreference.primary;
|
|
18
|
-
// If we have an option readPreference override the command one
|
|
19
|
-
if (options?.readPreference) {
|
|
20
|
-
readPreference = options.readPreference;
|
|
21
|
-
}
|
|
22
18
|
|
|
23
19
|
if (typeof readPreference === 'string') {
|
|
24
20
|
readPreference = ReadPreference.fromString(readPreference);
|
|
@@ -43,7 +39,7 @@ export function isSharded(topologyOrServer?: Topology | Server | Connection): bo
|
|
|
43
39
|
}
|
|
44
40
|
|
|
45
41
|
// NOTE: This is incredibly inefficient, and should be removed once command construction
|
|
46
|
-
//
|
|
42
|
+
// happens based on `Server` not `Topology`.
|
|
47
43
|
if (topologyOrServer.description && topologyOrServer.description instanceof TopologyDescription) {
|
|
48
44
|
const servers: ServerDescription[] = Array.from(topologyOrServer.description.servers.values());
|
|
49
45
|
return servers.some((server: ServerDescription) => server.type === ServerType.Mongos);
|
package/src/connection_string.ts
CHANGED
|
@@ -5,7 +5,7 @@ import { URLSearchParams } from 'url';
|
|
|
5
5
|
import type { Document } from './bson';
|
|
6
6
|
import { MongoCredentials } from './cmap/auth/mongo_credentials';
|
|
7
7
|
import { AUTH_MECHS_AUTH_SRC_EXTERNAL, AuthMechanism } from './cmap/auth/providers';
|
|
8
|
-
import { makeClientMetadata } from './cmap/handshake/client_metadata';
|
|
8
|
+
import { addContainerMetadata, makeClientMetadata } from './cmap/handshake/client_metadata';
|
|
9
9
|
import { Compressor, type CompressorName } from './cmap/wire_protocol/compression';
|
|
10
10
|
import { Encrypter } from './encrypter';
|
|
11
11
|
import {
|
|
@@ -68,8 +68,15 @@ export async function resolveSRVRecord(options: MongoOptions): Promise<HostAddre
|
|
|
68
68
|
throw new MongoAPIError('URI must include hostname, domain name, and tld');
|
|
69
69
|
}
|
|
70
70
|
|
|
71
|
-
//
|
|
71
|
+
// Asynchronously start TXT resolution so that we do not have to wait until
|
|
72
|
+
// the SRV record is resolved before starting a second DNS query.
|
|
72
73
|
const lookupAddress = options.srvHost;
|
|
74
|
+
const txtResolutionPromise = dns.promises.resolveTxt(lookupAddress);
|
|
75
|
+
txtResolutionPromise.catch(() => {
|
|
76
|
+
/* rejections will be handled later */
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
// Resolve the SRV record and use the result as the list of hosts to connect to.
|
|
73
80
|
const addresses = await dns.promises.resolveSrv(
|
|
74
81
|
`_${options.srvServiceName}._tcp.${lookupAddress}`
|
|
75
82
|
);
|
|
@@ -88,10 +95,10 @@ export async function resolveSRVRecord(options: MongoOptions): Promise<HostAddre
|
|
|
88
95
|
|
|
89
96
|
validateLoadBalancedOptions(hostAddresses, options, true);
|
|
90
97
|
|
|
91
|
-
//
|
|
98
|
+
// Use the result of resolving the TXT record and add options from there if they exist.
|
|
92
99
|
let record;
|
|
93
100
|
try {
|
|
94
|
-
record = await
|
|
101
|
+
record = await txtResolutionPromise;
|
|
95
102
|
} catch (error) {
|
|
96
103
|
if (error.code !== 'ENODATA' && error.code !== 'ENOTFOUND') {
|
|
97
104
|
throw error;
|
|
@@ -545,6 +552,10 @@ export function parseOptions(
|
|
|
545
552
|
|
|
546
553
|
mongoOptions.metadata = makeClientMetadata(mongoOptions);
|
|
547
554
|
|
|
555
|
+
mongoOptions.extendedMetadata = addContainerMetadata(mongoOptions.metadata).catch(() => {
|
|
556
|
+
/* rejections will be handled later */
|
|
557
|
+
});
|
|
558
|
+
|
|
548
559
|
return mongoOptions;
|
|
549
560
|
}
|
|
550
561
|
|
package/src/error.ts
CHANGED
|
@@ -200,6 +200,8 @@ export class MongoError extends Error {
|
|
|
200
200
|
* @category Error
|
|
201
201
|
*/
|
|
202
202
|
export class MongoServerError extends MongoError {
|
|
203
|
+
/** Raw error result document returned by server. */
|
|
204
|
+
errorResponse: ErrorDescription;
|
|
203
205
|
codeName?: string;
|
|
204
206
|
writeConcernError?: Document;
|
|
205
207
|
errInfo?: Document;
|
|
@@ -223,9 +225,17 @@ export class MongoServerError extends MongoError {
|
|
|
223
225
|
this[kErrorLabels] = new Set(message.errorLabels);
|
|
224
226
|
}
|
|
225
227
|
|
|
228
|
+
this.errorResponse = message;
|
|
229
|
+
|
|
226
230
|
for (const name in message) {
|
|
227
|
-
if (
|
|
231
|
+
if (
|
|
232
|
+
name !== 'errorLabels' &&
|
|
233
|
+
name !== 'errmsg' &&
|
|
234
|
+
name !== 'message' &&
|
|
235
|
+
name !== 'errorResponse'
|
|
236
|
+
) {
|
|
228
237
|
this[name] = message[name];
|
|
238
|
+
}
|
|
229
239
|
}
|
|
230
240
|
}
|
|
231
241
|
|
package/src/index.ts
CHANGED
package/src/mongo_client.ts
CHANGED
|
@@ -86,7 +86,7 @@ export interface Auth {
|
|
|
86
86
|
|
|
87
87
|
/** @public */
|
|
88
88
|
export interface PkFactory {
|
|
89
|
-
createPk(): any;
|
|
89
|
+
createPk(): any;
|
|
90
90
|
}
|
|
91
91
|
|
|
92
92
|
/** @public */
|
|
@@ -552,7 +552,7 @@ export class MongoClient extends TypedEventEmitter<MongoClientEvents> {
|
|
|
552
552
|
try {
|
|
553
553
|
await promisify(callback => this.topology?.connect(options, callback))();
|
|
554
554
|
} catch (error) {
|
|
555
|
-
this.topology?.close(
|
|
555
|
+
this.topology?.close();
|
|
556
556
|
throw error;
|
|
557
557
|
}
|
|
558
558
|
};
|
|
@@ -614,19 +614,12 @@ export class MongoClient extends TypedEventEmitter<MongoClientEvents> {
|
|
|
614
614
|
const topology = this.topology;
|
|
615
615
|
this.topology = undefined;
|
|
616
616
|
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
if (error) return reject(error);
|
|
624
|
-
resolve();
|
|
625
|
-
});
|
|
626
|
-
}
|
|
627
|
-
resolve();
|
|
628
|
-
});
|
|
629
|
-
});
|
|
617
|
+
topology.close();
|
|
618
|
+
|
|
619
|
+
const { encrypter } = this[kOptions];
|
|
620
|
+
if (encrypter) {
|
|
621
|
+
await encrypter.close(this, force);
|
|
622
|
+
}
|
|
630
623
|
}
|
|
631
624
|
|
|
632
625
|
/**
|
|
@@ -821,12 +814,15 @@ export interface MongoOptions
|
|
|
821
814
|
readPreference: ReadPreference;
|
|
822
815
|
readConcern: ReadConcern;
|
|
823
816
|
loadBalanced: boolean;
|
|
817
|
+
directConnection: boolean;
|
|
824
818
|
serverApi: ServerApi;
|
|
825
819
|
compressors: CompressorName[];
|
|
826
820
|
writeConcern: WriteConcern;
|
|
827
821
|
dbName: string;
|
|
828
822
|
metadata: ClientMetadata;
|
|
829
823
|
/** @internal */
|
|
824
|
+
extendedMetadata: Promise<Document>;
|
|
825
|
+
/** @internal */
|
|
830
826
|
autoEncrypter?: AutoEncrypter;
|
|
831
827
|
proxyHost?: string;
|
|
832
828
|
proxyPort?: number;
|
package/src/mongo_logger.ts
CHANGED
|
@@ -220,7 +220,8 @@ export function createStdioLogger(stream: {
|
|
|
220
220
|
}): MongoDBLogWritable {
|
|
221
221
|
return {
|
|
222
222
|
write: promisify((log: Log, cb: (error?: Error) => void): unknown => {
|
|
223
|
-
|
|
223
|
+
const logLine = inspect(log, { compact: true, breakLength: Infinity });
|
|
224
|
+
stream.write(`${logLine}\n`, 'utf-8', cb);
|
|
224
225
|
return;
|
|
225
226
|
})
|
|
226
227
|
};
|
|
@@ -43,11 +43,21 @@ export async function indexInformation(
|
|
|
43
43
|
return info;
|
|
44
44
|
}
|
|
45
45
|
|
|
46
|
-
export function
|
|
46
|
+
export function maybeAddIdToDocuments(
|
|
47
47
|
coll: Collection,
|
|
48
48
|
docs: Document[],
|
|
49
49
|
options: { forceServerObjectId?: boolean }
|
|
50
|
-
): Document[]
|
|
50
|
+
): Document[];
|
|
51
|
+
export function maybeAddIdToDocuments(
|
|
52
|
+
coll: Collection,
|
|
53
|
+
docs: Document,
|
|
54
|
+
options: { forceServerObjectId?: boolean }
|
|
55
|
+
): Document;
|
|
56
|
+
export function maybeAddIdToDocuments(
|
|
57
|
+
coll: Collection,
|
|
58
|
+
docOrDocs: Document[] | Document,
|
|
59
|
+
options: { forceServerObjectId?: boolean }
|
|
60
|
+
): Document[] | Document {
|
|
51
61
|
const forceServerObjectId =
|
|
52
62
|
typeof options.forceServerObjectId === 'boolean'
|
|
53
63
|
? options.forceServerObjectId
|
|
@@ -55,14 +65,15 @@ export function prepareDocs(
|
|
|
55
65
|
|
|
56
66
|
// no need to modify the docs if server sets the ObjectId
|
|
57
67
|
if (forceServerObjectId === true) {
|
|
58
|
-
return
|
|
68
|
+
return docOrDocs;
|
|
59
69
|
}
|
|
60
70
|
|
|
61
|
-
|
|
71
|
+
const transform = (doc: Document): Document => {
|
|
62
72
|
if (doc._id == null) {
|
|
63
73
|
doc._id = coll.s.pkFactory.createPk();
|
|
64
74
|
}
|
|
65
75
|
|
|
66
76
|
return doc;
|
|
67
|
-
}
|
|
77
|
+
};
|
|
78
|
+
return Array.isArray(docOrDocs) ? docOrDocs.map(transform) : transform(docOrDocs);
|
|
68
79
|
}
|
package/src/operations/insert.ts
CHANGED
|
@@ -9,7 +9,7 @@ import type { MongoDBNamespace } from '../utils';
|
|
|
9
9
|
import { WriteConcern } from '../write_concern';
|
|
10
10
|
import { BulkWriteOperation } from './bulk_write';
|
|
11
11
|
import { CommandOperation, type CommandOperationOptions } from './command';
|
|
12
|
-
import {
|
|
12
|
+
import { maybeAddIdToDocuments } from './common_functions';
|
|
13
13
|
import { AbstractOperation, Aspect, defineAspects } from './operation';
|
|
14
14
|
|
|
15
15
|
/** @internal */
|
|
@@ -69,7 +69,7 @@ export interface InsertOneResult<TSchema = Document> {
|
|
|
69
69
|
|
|
70
70
|
export class InsertOneOperation extends InsertOperation {
|
|
71
71
|
constructor(collection: Collection, doc: Document, options: InsertOneOptions) {
|
|
72
|
-
super(collection.s.namespace,
|
|
72
|
+
super(collection.s.namespace, maybeAddIdToDocuments(collection, [doc], options), options);
|
|
73
73
|
}
|
|
74
74
|
|
|
75
75
|
override async execute(
|
|
@@ -131,7 +131,9 @@ export class InsertManyOperation extends AbstractOperation<InsertManyResult> {
|
|
|
131
131
|
const writeConcern = WriteConcern.fromOptions(options);
|
|
132
132
|
const bulkWriteOperation = new BulkWriteOperation(
|
|
133
133
|
coll,
|
|
134
|
-
|
|
134
|
+
this.docs.map(document => ({
|
|
135
|
+
insertOne: { document }
|
|
136
|
+
})),
|
|
135
137
|
options
|
|
136
138
|
);
|
|
137
139
|
|
package/src/sdam/monitor.ts
CHANGED
|
@@ -214,7 +214,7 @@ function resetMonitorState(monitor: Monitor) {
|
|
|
214
214
|
|
|
215
215
|
monitor[kCancellationToken].emit('cancel');
|
|
216
216
|
|
|
217
|
-
monitor.connection?.destroy(
|
|
217
|
+
monitor.connection?.destroy();
|
|
218
218
|
monitor.connection = null;
|
|
219
219
|
}
|
|
220
220
|
|
|
@@ -247,7 +247,7 @@ function checkServer(monitor: Monitor, callback: Callback<Document | null>) {
|
|
|
247
247
|
);
|
|
248
248
|
|
|
249
249
|
function onHeartbeatFailed(err: Error) {
|
|
250
|
-
monitor.connection?.destroy(
|
|
250
|
+
monitor.connection?.destroy();
|
|
251
251
|
monitor.connection = null;
|
|
252
252
|
|
|
253
253
|
monitor.emitAndLogHeartbeat(
|
|
@@ -366,13 +366,13 @@ function checkServer(monitor: Monitor, callback: Callback<Document | null>) {
|
|
|
366
366
|
await performInitialHandshake(connection, monitor.connectOptions);
|
|
367
367
|
return connection;
|
|
368
368
|
} catch (error) {
|
|
369
|
-
connection.destroy(
|
|
369
|
+
connection.destroy();
|
|
370
370
|
throw error;
|
|
371
371
|
}
|
|
372
372
|
})().then(
|
|
373
373
|
connection => {
|
|
374
374
|
if (isInCloseState(monitor)) {
|
|
375
|
-
connection.destroy(
|
|
375
|
+
connection.destroy();
|
|
376
376
|
return;
|
|
377
377
|
}
|
|
378
378
|
|
|
@@ -479,7 +479,7 @@ export class RTTPinger {
|
|
|
479
479
|
this.closed = true;
|
|
480
480
|
clearTimeout(this[kMonitorId]);
|
|
481
481
|
|
|
482
|
-
this.connection?.destroy(
|
|
482
|
+
this.connection?.destroy();
|
|
483
483
|
this.connection = undefined;
|
|
484
484
|
}
|
|
485
485
|
}
|
|
@@ -495,7 +495,7 @@ function measureRoundTripTime(rttPinger: RTTPinger, options: RTTPingerOptions) {
|
|
|
495
495
|
|
|
496
496
|
function measureAndReschedule(conn?: Connection) {
|
|
497
497
|
if (rttPinger.closed) {
|
|
498
|
-
conn?.destroy(
|
|
498
|
+
conn?.destroy();
|
|
499
499
|
return;
|
|
500
500
|
}
|
|
501
501
|
|
|
@@ -529,7 +529,7 @@ function measureRoundTripTime(rttPinger: RTTPinger, options: RTTPingerOptions) {
|
|
|
529
529
|
connection.command(ns('admin.$cmd'), { [commandName]: 1 }, undefined).then(
|
|
530
530
|
() => measureAndReschedule(),
|
|
531
531
|
() => {
|
|
532
|
-
rttPinger.connection?.destroy(
|
|
532
|
+
rttPinger.connection?.destroy();
|
|
533
533
|
rttPinger.connection = undefined;
|
|
534
534
|
rttPinger[kRoundTripTime] = 0;
|
|
535
535
|
return;
|
package/src/sdam/server.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { Document } from '../bson';
|
|
2
2
|
import { type AutoEncrypter } from '../client-side-encryption/auto_encrypter';
|
|
3
|
-
import { type CommandOptions, Connection
|
|
3
|
+
import { type CommandOptions, Connection } from '../cmap/connection';
|
|
4
4
|
import {
|
|
5
5
|
ConnectionPool,
|
|
6
6
|
type ConnectionPoolEvents,
|
|
@@ -41,7 +41,6 @@ import type { GetMoreOptions } from '../operations/get_more';
|
|
|
41
41
|
import type { ClientSession } from '../sessions';
|
|
42
42
|
import { isTransactionCommand } from '../transactions';
|
|
43
43
|
import {
|
|
44
|
-
type Callback,
|
|
45
44
|
type EventEmitterWithState,
|
|
46
45
|
makeStateMachine,
|
|
47
46
|
maxWireVersion,
|
|
@@ -171,7 +170,7 @@ export class Server extends TypedEventEmitter<ServerEvents> {
|
|
|
171
170
|
this.monitor.on(event, (e: any) => this.emit(event, e));
|
|
172
171
|
}
|
|
173
172
|
|
|
174
|
-
this.monitor.on('resetServer', (error:
|
|
173
|
+
this.monitor.on('resetServer', (error: MongoServerError) => markServerUnknown(this, error));
|
|
175
174
|
this.monitor.on(Server.SERVER_HEARTBEAT_SUCCEEDED, (event: ServerHeartbeatSucceededEvent) => {
|
|
176
175
|
this.emit(
|
|
177
176
|
Server.DESCRIPTION_RECEIVED,
|
|
@@ -236,18 +235,8 @@ export class Server extends TypedEventEmitter<ServerEvents> {
|
|
|
236
235
|
}
|
|
237
236
|
|
|
238
237
|
/** Destroy the server connection */
|
|
239
|
-
destroy(
|
|
240
|
-
if (typeof options === 'function') {
|
|
241
|
-
callback = options;
|
|
242
|
-
options = { force: false };
|
|
243
|
-
}
|
|
244
|
-
options = Object.assign({}, { force: false }, options);
|
|
245
|
-
|
|
238
|
+
destroy(): void {
|
|
246
239
|
if (this.s.state === STATE_CLOSED) {
|
|
247
|
-
if (typeof callback === 'function') {
|
|
248
|
-
callback();
|
|
249
|
-
}
|
|
250
|
-
|
|
251
240
|
return;
|
|
252
241
|
}
|
|
253
242
|
|
|
@@ -257,13 +246,9 @@ export class Server extends TypedEventEmitter<ServerEvents> {
|
|
|
257
246
|
this.monitor?.close();
|
|
258
247
|
}
|
|
259
248
|
|
|
260
|
-
this.pool.close(
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
if (typeof callback === 'function') {
|
|
264
|
-
callback(err);
|
|
265
|
-
}
|
|
266
|
-
});
|
|
249
|
+
this.pool.close();
|
|
250
|
+
stateTransition(this, STATE_CLOSED);
|
|
251
|
+
this.emit('closed');
|
|
267
252
|
}
|
|
268
253
|
|
|
269
254
|
/**
|
|
@@ -290,7 +275,10 @@ export class Server extends TypedEventEmitter<ServerEvents> {
|
|
|
290
275
|
}
|
|
291
276
|
|
|
292
277
|
// Clone the options
|
|
293
|
-
const finalOptions = Object.assign({}, options, {
|
|
278
|
+
const finalOptions = Object.assign({}, options, {
|
|
279
|
+
wireProtocolCommand: false,
|
|
280
|
+
directConnection: this.topology.s.options.directConnection
|
|
281
|
+
});
|
|
294
282
|
|
|
295
283
|
// There are cases where we need to flag the read preference not to get sent in
|
|
296
284
|
// the command, such as pre-5.0 servers attempting to perform an aggregate write
|
|
@@ -369,7 +357,7 @@ export class Server extends TypedEventEmitter<ServerEvents> {
|
|
|
369
357
|
// clear for the specific service id.
|
|
370
358
|
if (!this.loadBalanced) {
|
|
371
359
|
error.addErrorLabel(MongoErrorLabel.ResetPool);
|
|
372
|
-
markServerUnknown(this, error);
|
|
360
|
+
markServerUnknown(this, error as MongoServerError);
|
|
373
361
|
} else if (connection) {
|
|
374
362
|
this.pool.clear({ serviceId: connection.serviceId });
|
|
375
363
|
}
|
|
@@ -385,7 +373,7 @@ export class Server extends TypedEventEmitter<ServerEvents> {
|
|
|
385
373
|
if (shouldClearPool) {
|
|
386
374
|
error.addErrorLabel(MongoErrorLabel.ResetPool);
|
|
387
375
|
}
|
|
388
|
-
markServerUnknown(this, error);
|
|
376
|
+
markServerUnknown(this, error as MongoServerError);
|
|
389
377
|
process.nextTick(() => this.requestCheck());
|
|
390
378
|
}
|
|
391
379
|
}
|
package/src/sdam/topology.ts
CHANGED
|
@@ -2,8 +2,8 @@ import { promisify } from 'util';
|
|
|
2
2
|
|
|
3
3
|
import type { BSONSerializeOptions, Document } from '../bson';
|
|
4
4
|
import type { MongoCredentials } from '../cmap/auth/mongo_credentials';
|
|
5
|
-
import type { ConnectionEvents
|
|
6
|
-
import type {
|
|
5
|
+
import type { ConnectionEvents } from '../cmap/connection';
|
|
6
|
+
import type { ConnectionPoolEvents } from '../cmap/connection_pool';
|
|
7
7
|
import type { ClientMetadata } from '../cmap/handshake/client_metadata';
|
|
8
8
|
import { DEFAULT_OPTIONS, FEATURE_FLAGS } from '../connection_string';
|
|
9
9
|
import {
|
|
@@ -158,6 +158,7 @@ export interface TopologyOptions extends BSONSerializeOptions, ServerOptions {
|
|
|
158
158
|
directConnection: boolean;
|
|
159
159
|
loadBalanced: boolean;
|
|
160
160
|
metadata: ClientMetadata;
|
|
161
|
+
extendedMetadata: Promise<Document>;
|
|
161
162
|
serverMonitoringMode: ServerMonitoringMode;
|
|
162
163
|
/** MongoDB server API version */
|
|
163
164
|
serverApi?: ServerApi;
|
|
@@ -468,7 +469,8 @@ export class Topology extends TypedEventEmitter<TopologyEvents> {
|
|
|
468
469
|
selectServerOptions,
|
|
469
470
|
(err, server) => {
|
|
470
471
|
if (err) {
|
|
471
|
-
|
|
472
|
+
this.close();
|
|
473
|
+
return exitWithError(err);
|
|
472
474
|
}
|
|
473
475
|
|
|
474
476
|
const skipPingOnConnect = this.s.options[Symbol.for('@@mdb.skipPingOnConnect')] === true;
|
|
@@ -494,41 +496,33 @@ export class Topology extends TypedEventEmitter<TopologyEvents> {
|
|
|
494
496
|
}
|
|
495
497
|
|
|
496
498
|
/** Close this topology */
|
|
497
|
-
close(
|
|
498
|
-
close(options: CloseOptions, callback: Callback): void;
|
|
499
|
-
close(options?: CloseOptions, callback?: Callback): void {
|
|
500
|
-
options = options ?? { force: false };
|
|
501
|
-
|
|
499
|
+
close(): void {
|
|
502
500
|
if (this.s.state === STATE_CLOSED || this.s.state === STATE_CLOSING) {
|
|
503
|
-
return
|
|
501
|
+
return;
|
|
504
502
|
}
|
|
505
503
|
|
|
506
|
-
const
|
|
507
|
-
|
|
508
|
-
}
|
|
504
|
+
for (const server of this.s.servers.values()) {
|
|
505
|
+
destroyServer(server, this);
|
|
506
|
+
}
|
|
509
507
|
|
|
510
|
-
|
|
511
|
-
.then(() => {
|
|
512
|
-
this.s.servers.clear();
|
|
508
|
+
this.s.servers.clear();
|
|
513
509
|
|
|
514
|
-
|
|
510
|
+
stateTransition(this, STATE_CLOSING);
|
|
515
511
|
|
|
516
|
-
|
|
517
|
-
|
|
512
|
+
drainWaitQueue(this[kWaitQueue], new MongoTopologyClosedError());
|
|
513
|
+
drainTimerQueue(this.s.connectionTimers);
|
|
518
514
|
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
515
|
+
if (this.s.srvPoller) {
|
|
516
|
+
this.s.srvPoller.stop();
|
|
517
|
+
this.s.srvPoller.removeListener(SrvPoller.SRV_RECORD_DISCOVERY, this.s.detectSrvRecords);
|
|
518
|
+
}
|
|
523
519
|
|
|
524
|
-
|
|
520
|
+
this.removeListener(Topology.TOPOLOGY_DESCRIPTION_CHANGED, this.s.detectShardedTopology);
|
|
525
521
|
|
|
526
|
-
|
|
522
|
+
stateTransition(this, STATE_CLOSED);
|
|
527
523
|
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
})
|
|
531
|
-
.finally(() => callback?.());
|
|
524
|
+
// emit an event for close
|
|
525
|
+
this.emitAndLog(Topology.TOPOLOGY_CLOSED, new TopologyClosedEvent(this.s.id));
|
|
532
526
|
}
|
|
533
527
|
|
|
534
528
|
/**
|
|
@@ -772,30 +766,20 @@ export class Topology extends TypedEventEmitter<TopologyEvents> {
|
|
|
772
766
|
}
|
|
773
767
|
|
|
774
768
|
/** Destroys a server, and removes all event listeners from the instance */
|
|
775
|
-
function destroyServer(
|
|
776
|
-
server: Server,
|
|
777
|
-
topology: Topology,
|
|
778
|
-
options?: DestroyOptions,
|
|
779
|
-
callback?: Callback
|
|
780
|
-
) {
|
|
781
|
-
options = options ?? { force: false };
|
|
769
|
+
function destroyServer(server: Server, topology: Topology) {
|
|
782
770
|
for (const event of LOCAL_SERVER_EVENTS) {
|
|
783
771
|
server.removeAllListeners(event);
|
|
784
772
|
}
|
|
785
773
|
|
|
786
|
-
server.destroy(
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
774
|
+
server.destroy();
|
|
775
|
+
topology.emitAndLog(
|
|
776
|
+
Topology.SERVER_CLOSED,
|
|
777
|
+
new ServerClosedEvent(topology.s.id, server.description.address)
|
|
778
|
+
);
|
|
791
779
|
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
if (typeof callback === 'function') {
|
|
796
|
-
callback();
|
|
797
|
-
}
|
|
798
|
-
});
|
|
780
|
+
for (const event of SERVER_RELAY_EVENTS) {
|
|
781
|
+
server.removeAllListeners(event);
|
|
782
|
+
}
|
|
799
783
|
}
|
|
800
784
|
|
|
801
785
|
/** Predicts the TopologyType from options */
|
package/src/utils.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import * as crypto from 'crypto';
|
|
2
2
|
import type { SrvRecord } from 'dns';
|
|
3
|
+
import { type EventEmitter } from 'events';
|
|
3
4
|
import * as http from 'http';
|
|
4
5
|
import { clearTimeout, setTimeout } from 'timers';
|
|
5
6
|
import * as url from 'url';
|
|
@@ -385,46 +386,6 @@ export function maxWireVersion(topologyOrServer?: Connection | Topology | Server
|
|
|
385
386
|
return 0;
|
|
386
387
|
}
|
|
387
388
|
|
|
388
|
-
/**
|
|
389
|
-
* Applies the function `eachFn` to each item in `arr`, in parallel.
|
|
390
|
-
* @internal
|
|
391
|
-
*
|
|
392
|
-
* @param arr - An array of items to asynchronously iterate over
|
|
393
|
-
* @param eachFn - A function to call on each item of the array. The callback signature is `(item, callback)`, where the callback indicates iteration is complete.
|
|
394
|
-
* @param callback - The callback called after every item has been iterated
|
|
395
|
-
*/
|
|
396
|
-
export function eachAsync<T = Document>(
|
|
397
|
-
arr: T[],
|
|
398
|
-
eachFn: (item: T, callback: (err?: AnyError) => void) => void,
|
|
399
|
-
callback: Callback
|
|
400
|
-
): void {
|
|
401
|
-
arr = arr || [];
|
|
402
|
-
|
|
403
|
-
let idx = 0;
|
|
404
|
-
let awaiting = 0;
|
|
405
|
-
for (idx = 0; idx < arr.length; ++idx) {
|
|
406
|
-
awaiting++;
|
|
407
|
-
eachFn(arr[idx], eachCallback);
|
|
408
|
-
}
|
|
409
|
-
|
|
410
|
-
if (awaiting === 0) {
|
|
411
|
-
callback();
|
|
412
|
-
return;
|
|
413
|
-
}
|
|
414
|
-
|
|
415
|
-
function eachCallback(err?: AnyError) {
|
|
416
|
-
awaiting--;
|
|
417
|
-
if (err) {
|
|
418
|
-
callback(err);
|
|
419
|
-
return;
|
|
420
|
-
}
|
|
421
|
-
|
|
422
|
-
if (idx === arr.length && awaiting <= 0) {
|
|
423
|
-
callback();
|
|
424
|
-
}
|
|
425
|
-
}
|
|
426
|
-
}
|
|
427
|
-
|
|
428
389
|
/** @internal */
|
|
429
390
|
export function arrayStrictEqual(arr: unknown[], arr2: unknown[]): boolean {
|
|
430
391
|
if (!Array.isArray(arr) || !Array.isArray(arr2)) {
|
|
@@ -1295,3 +1256,27 @@ export function promiseWithResolvers<T>() {
|
|
|
1295
1256
|
}
|
|
1296
1257
|
|
|
1297
1258
|
export const randomBytes = promisify(crypto.randomBytes);
|
|
1259
|
+
|
|
1260
|
+
/**
|
|
1261
|
+
* Replicates the events.once helper.
|
|
1262
|
+
*
|
|
1263
|
+
* Removes unused signal logic and It **only** supports 0 or 1 argument events.
|
|
1264
|
+
*
|
|
1265
|
+
* @param ee - An event emitter that may emit `ev`
|
|
1266
|
+
* @param name - An event name to wait for
|
|
1267
|
+
*/
|
|
1268
|
+
export async function once<T>(ee: EventEmitter, name: string): Promise<T> {
|
|
1269
|
+
const { promise, resolve, reject } = promiseWithResolvers<T>();
|
|
1270
|
+
const onEvent = (data: T) => resolve(data);
|
|
1271
|
+
const onError = (error: Error) => reject(error);
|
|
1272
|
+
|
|
1273
|
+
ee.once(name, onEvent).once('error', onError);
|
|
1274
|
+
try {
|
|
1275
|
+
const res = await promise;
|
|
1276
|
+
ee.off('error', onError);
|
|
1277
|
+
return res;
|
|
1278
|
+
} catch (error) {
|
|
1279
|
+
ee.off(name, onEvent);
|
|
1280
|
+
throw error;
|
|
1281
|
+
}
|
|
1282
|
+
}
|