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
package/src/bulk/common.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { promisify } from 'util';
|
|
2
2
|
|
|
3
|
-
import { type BSONSerializeOptions, type Document,
|
|
3
|
+
import { type BSONSerializeOptions, type Document, resolveBSONOptions } from '../bson';
|
|
4
4
|
import type { Collection } from '../collection';
|
|
5
5
|
import {
|
|
6
6
|
type AnyError,
|
|
@@ -12,6 +12,7 @@ import {
|
|
|
12
12
|
} from '../error';
|
|
13
13
|
import type { Filter, OneOrMore, OptionalId, UpdateFilter, WithoutId } from '../mongo_types';
|
|
14
14
|
import type { CollationOptions, CommandOperationOptions } from '../operations/command';
|
|
15
|
+
import { maybeAddIdToDocuments } from '../operations/common_functions';
|
|
15
16
|
import { DeleteOperation, type DeleteStatement, makeDeleteStatement } from '../operations/delete';
|
|
16
17
|
import { executeOperation } from '../operations/execute_operation';
|
|
17
18
|
import { InsertOperation } from '../operations/insert';
|
|
@@ -917,7 +918,7 @@ export abstract class BulkOperationBase {
|
|
|
917
918
|
* Create a new OrderedBulkOperation or UnorderedBulkOperation instance
|
|
918
919
|
* @internal
|
|
919
920
|
*/
|
|
920
|
-
constructor(collection: Collection, options: BulkWriteOptions, isOrdered: boolean) {
|
|
921
|
+
constructor(private collection: Collection, options: BulkWriteOptions, isOrdered: boolean) {
|
|
921
922
|
// determine whether bulkOperation is ordered or unordered
|
|
922
923
|
this.isOrdered = isOrdered;
|
|
923
924
|
|
|
@@ -1032,9 +1033,9 @@ export abstract class BulkOperationBase {
|
|
|
1032
1033
|
* ```
|
|
1033
1034
|
*/
|
|
1034
1035
|
insert(document: Document): BulkOperationBase {
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
}
|
|
1036
|
+
maybeAddIdToDocuments(this.collection, document, {
|
|
1037
|
+
forceServerObjectId: this.shouldForceServerObjectId()
|
|
1038
|
+
});
|
|
1038
1039
|
|
|
1039
1040
|
return this.addToOperationsList(BatchType.INSERT, document);
|
|
1040
1041
|
}
|
|
@@ -1093,21 +1094,16 @@ export abstract class BulkOperationBase {
|
|
|
1093
1094
|
throw new MongoInvalidArgumentError('Operation must be an object with an operation key');
|
|
1094
1095
|
}
|
|
1095
1096
|
if ('insertOne' in op) {
|
|
1096
|
-
const forceServerObjectId = shouldForceServerObjectId(
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
return this.addToOperationsList(BatchType.INSERT, op.insertOne);
|
|
1104
|
-
}
|
|
1097
|
+
const forceServerObjectId = this.shouldForceServerObjectId();
|
|
1098
|
+
const document =
|
|
1099
|
+
op.insertOne && op.insertOne.document == null
|
|
1100
|
+
? // TODO(NODE-6003): remove support for omitting the `documents` subdocument in bulk inserts
|
|
1101
|
+
(op.insertOne as Document)
|
|
1102
|
+
: op.insertOne.document;
|
|
1105
1103
|
|
|
1106
|
-
|
|
1107
|
-
op.insertOne.document._id = new ObjectId();
|
|
1108
|
-
}
|
|
1104
|
+
maybeAddIdToDocuments(this.collection, document, { forceServerObjectId });
|
|
1109
1105
|
|
|
1110
|
-
return this.addToOperationsList(BatchType.INSERT,
|
|
1106
|
+
return this.addToOperationsList(BatchType.INSERT, document);
|
|
1111
1107
|
}
|
|
1112
1108
|
|
|
1113
1109
|
if ('replaceOne' in op || 'updateOne' in op || 'updateMany' in op) {
|
|
@@ -1268,6 +1264,13 @@ export abstract class BulkOperationBase {
|
|
|
1268
1264
|
batchType: BatchType,
|
|
1269
1265
|
document: Document | UpdateStatement | DeleteStatement
|
|
1270
1266
|
): this;
|
|
1267
|
+
|
|
1268
|
+
private shouldForceServerObjectId(): boolean {
|
|
1269
|
+
return (
|
|
1270
|
+
this.s.options.forceServerObjectId === true ||
|
|
1271
|
+
this.s.collection.s.db.options?.forceServerObjectId === true
|
|
1272
|
+
);
|
|
1273
|
+
}
|
|
1271
1274
|
}
|
|
1272
1275
|
|
|
1273
1276
|
Object.defineProperty(BulkOperationBase.prototype, 'length', {
|
|
@@ -1277,18 +1280,6 @@ Object.defineProperty(BulkOperationBase.prototype, 'length', {
|
|
|
1277
1280
|
}
|
|
1278
1281
|
});
|
|
1279
1282
|
|
|
1280
|
-
function shouldForceServerObjectId(bulkOperation: BulkOperationBase): boolean {
|
|
1281
|
-
if (typeof bulkOperation.s.options.forceServerObjectId === 'boolean') {
|
|
1282
|
-
return bulkOperation.s.options.forceServerObjectId;
|
|
1283
|
-
}
|
|
1284
|
-
|
|
1285
|
-
if (typeof bulkOperation.s.collection.s.db.options?.forceServerObjectId === 'boolean') {
|
|
1286
|
-
return bulkOperation.s.collection.s.db.options?.forceServerObjectId;
|
|
1287
|
-
}
|
|
1288
|
-
|
|
1289
|
-
return false;
|
|
1290
|
-
}
|
|
1291
|
-
|
|
1292
1283
|
function isInsertBatch(batch: Batch): boolean {
|
|
1293
1284
|
return batch.batchType === BatchType.INSERT;
|
|
1294
1285
|
}
|
package/src/cmap/commands.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { BSONSerializeOptions, Document, Long } from '../bson';
|
|
2
2
|
import * as BSON from '../bson';
|
|
3
3
|
import { MongoInvalidArgumentError, MongoRuntimeError } from '../error';
|
|
4
|
-
import { ReadPreference } from '../read_preference';
|
|
4
|
+
import { type ReadPreference } from '../read_preference';
|
|
5
5
|
import type { ClientSession } from '../sessions';
|
|
6
6
|
import type { CommandOptions } from './connection';
|
|
7
7
|
import {
|
|
@@ -51,7 +51,6 @@ export interface OpQueryOptions extends CommandOptions {
|
|
|
51
51
|
requestId?: number;
|
|
52
52
|
moreToCome?: boolean;
|
|
53
53
|
exhaustAllowed?: boolean;
|
|
54
|
-
readPreference?: ReadPreference;
|
|
55
54
|
}
|
|
56
55
|
|
|
57
56
|
/**************************************************************
|
|
@@ -77,7 +76,6 @@ export class OpQueryRequest {
|
|
|
77
76
|
awaitData: boolean;
|
|
78
77
|
exhaust: boolean;
|
|
79
78
|
partial: boolean;
|
|
80
|
-
documentsReturnedIn?: string;
|
|
81
79
|
|
|
82
80
|
constructor(public databaseName: string, public query: Document, options: OpQueryOptions) {
|
|
83
81
|
// Basic options needed to be passed in
|
|
@@ -503,10 +501,6 @@ export class OpMsgRequest {
|
|
|
503
501
|
// Basic options
|
|
504
502
|
this.command.$db = databaseName;
|
|
505
503
|
|
|
506
|
-
if (options.readPreference && options.readPreference.mode !== ReadPreference.PRIMARY) {
|
|
507
|
-
this.command.$readPreference = options.readPreference.toJSON();
|
|
508
|
-
}
|
|
509
|
-
|
|
510
504
|
// Ensure empty options
|
|
511
505
|
this.options = options ?? {};
|
|
512
506
|
|
package/src/cmap/connect.ts
CHANGED
|
@@ -25,7 +25,6 @@ import {
|
|
|
25
25
|
type ConnectionOptions,
|
|
26
26
|
CryptoConnection
|
|
27
27
|
} from './connection';
|
|
28
|
-
import type { ClientMetadata } from './handshake/client_metadata';
|
|
29
28
|
import {
|
|
30
29
|
MAX_SUPPORTED_SERVER_VERSION,
|
|
31
30
|
MAX_SUPPORTED_WIRE_VERSION,
|
|
@@ -44,7 +43,7 @@ export async function connect(options: ConnectionOptions): Promise<Connection> {
|
|
|
44
43
|
await performInitialHandshake(connection, options);
|
|
45
44
|
return connection;
|
|
46
45
|
} catch (error) {
|
|
47
|
-
connection?.destroy(
|
|
46
|
+
connection?.destroy();
|
|
48
47
|
throw error;
|
|
49
48
|
}
|
|
50
49
|
}
|
|
@@ -183,7 +182,7 @@ export interface HandshakeDocument extends Document {
|
|
|
183
182
|
ismaster?: boolean;
|
|
184
183
|
hello?: boolean;
|
|
185
184
|
helloOk?: boolean;
|
|
186
|
-
client:
|
|
185
|
+
client: Document;
|
|
187
186
|
compression: string[];
|
|
188
187
|
saslSupportedMechs?: string;
|
|
189
188
|
loadBalanced?: boolean;
|
|
@@ -200,11 +199,12 @@ export async function prepareHandshakeDocument(
|
|
|
200
199
|
const options = authContext.options;
|
|
201
200
|
const compressors = options.compressors ? options.compressors : [];
|
|
202
201
|
const { serverApi } = authContext.connection;
|
|
202
|
+
const clientMetadata: Document = await options.extendedMetadata;
|
|
203
203
|
|
|
204
204
|
const handshakeDoc: HandshakeDocument = {
|
|
205
205
|
[serverApi?.version || options.loadBalanced === true ? 'hello' : LEGACY_HELLO_COMMAND]: 1,
|
|
206
206
|
helloOk: true,
|
|
207
|
-
client:
|
|
207
|
+
client: clientMetadata,
|
|
208
208
|
compression: compressors
|
|
209
209
|
};
|
|
210
210
|
|
|
@@ -319,7 +319,6 @@ export async function makeSocket(options: MakeConnectionOptions): Promise<Stream
|
|
|
319
319
|
const useTLS = options.tls ?? false;
|
|
320
320
|
const noDelay = options.noDelay ?? true;
|
|
321
321
|
const connectTimeoutMS = options.connectTimeoutMS ?? 30000;
|
|
322
|
-
const rejectUnauthorized = options.rejectUnauthorized ?? true;
|
|
323
322
|
const existingSocket = options.existingSocket;
|
|
324
323
|
|
|
325
324
|
let socket: Stream;
|
|
@@ -375,10 +374,6 @@ export async function makeSocket(options: MakeConnectionOptions): Promise<Stream
|
|
|
375
374
|
return socket;
|
|
376
375
|
} catch (error) {
|
|
377
376
|
socket.destroy();
|
|
378
|
-
if ('authorizationError' in socket && socket.authorizationError != null && rejectUnauthorized) {
|
|
379
|
-
// TODO(NODE-5192): wrap this with a MongoError subclass
|
|
380
|
-
throw socket.authorizationError;
|
|
381
|
-
}
|
|
382
377
|
throw error;
|
|
383
378
|
} finally {
|
|
384
379
|
socket.setTimeout(0);
|
package/src/cmap/connection.ts
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { type Readable, Transform, type TransformCallback } from 'stream';
|
|
2
2
|
import { clearTimeout, setTimeout } from 'timers';
|
|
3
|
-
import { promisify } from 'util';
|
|
4
3
|
|
|
5
4
|
import type { BSONSerializeOptions, Document, ObjectId } from '../bson';
|
|
6
5
|
import type { AutoEncrypter } from '../client-side-encryption/auto_encrypter';
|
|
@@ -27,7 +26,8 @@ import type { ServerApi, SupportedNodeConnectionOptions } from '../mongo_client'
|
|
|
27
26
|
import { type MongoClientAuthProviders } from '../mongo_client_auth_providers';
|
|
28
27
|
import { MongoLoggableComponent, type MongoLogger, SeverityLevel } from '../mongo_logger';
|
|
29
28
|
import { type CancellationToken, TypedEventEmitter } from '../mongo_types';
|
|
30
|
-
import type
|
|
29
|
+
import { ReadPreference, type ReadPreferenceLike } from '../read_preference';
|
|
30
|
+
import { ServerType } from '../sdam/common';
|
|
31
31
|
import { applySession, type ClientSession, updateSessionFromResponse } from '../sessions';
|
|
32
32
|
import {
|
|
33
33
|
BufferPool,
|
|
@@ -37,7 +37,7 @@ import {
|
|
|
37
37
|
maxWireVersion,
|
|
38
38
|
type MongoDBNamespace,
|
|
39
39
|
now,
|
|
40
|
-
|
|
40
|
+
once,
|
|
41
41
|
uuidV4
|
|
42
42
|
} from '../utils';
|
|
43
43
|
import type { WriteConcern } from '../write_concern';
|
|
@@ -84,6 +84,8 @@ export interface CommandOptions extends BSONSerializeOptions {
|
|
|
84
84
|
willRetryWrite?: boolean;
|
|
85
85
|
|
|
86
86
|
writeConcern?: WriteConcern;
|
|
87
|
+
|
|
88
|
+
directConnection?: boolean;
|
|
87
89
|
}
|
|
88
90
|
|
|
89
91
|
/** @public */
|
|
@@ -119,15 +121,11 @@ export interface ConnectionOptions
|
|
|
119
121
|
cancellationToken?: CancellationToken;
|
|
120
122
|
metadata: ClientMetadata;
|
|
121
123
|
/** @internal */
|
|
124
|
+
extendedMetadata: Promise<Document>;
|
|
125
|
+
/** @internal */
|
|
122
126
|
mongoLogger?: MongoLogger | undefined;
|
|
123
127
|
}
|
|
124
128
|
|
|
125
|
-
/** @internal */
|
|
126
|
-
export interface DestroyOptions {
|
|
127
|
-
/** Force the destruction. */
|
|
128
|
-
force: boolean;
|
|
129
|
-
}
|
|
130
|
-
|
|
131
129
|
/** @public */
|
|
132
130
|
export type ConnectionEvents = {
|
|
133
131
|
commandStarted(event: CommandStartedEvent): void;
|
|
@@ -180,18 +178,18 @@ export class Connection extends TypedEventEmitter<ConnectionEvents> {
|
|
|
180
178
|
* Once connection is established, command logging can log events (if enabled)
|
|
181
179
|
*/
|
|
182
180
|
public established: boolean;
|
|
181
|
+
/** Indicates that the connection (including underlying TCP socket) has been closed. */
|
|
182
|
+
public closed = false;
|
|
183
183
|
|
|
184
184
|
private lastUseTime: number;
|
|
185
185
|
private clusterTime: Document | null = null;
|
|
186
|
+
private error: Error | null = null;
|
|
187
|
+
private dataEvents: AsyncGenerator<Buffer, void, void> | null = null;
|
|
186
188
|
|
|
187
189
|
private readonly socketTimeoutMS: number;
|
|
188
190
|
private readonly monitorCommands: boolean;
|
|
189
191
|
private readonly socket: Stream;
|
|
190
|
-
private readonly controller: AbortController;
|
|
191
|
-
private readonly signal: AbortSignal;
|
|
192
192
|
private readonly messageStream: Readable;
|
|
193
|
-
private readonly socketWrite: (buffer: Uint8Array) => Promise<void>;
|
|
194
|
-
private readonly aborted: Promise<never>;
|
|
195
193
|
|
|
196
194
|
/** @event */
|
|
197
195
|
static readonly COMMAND_STARTED = COMMAND_STARTED;
|
|
@@ -211,6 +209,7 @@ export class Connection extends TypedEventEmitter<ConnectionEvents> {
|
|
|
211
209
|
constructor(stream: Stream, options: ConnectionOptions) {
|
|
212
210
|
super();
|
|
213
211
|
|
|
212
|
+
this.socket = stream;
|
|
214
213
|
this.id = options.id;
|
|
215
214
|
this.address = streamIdentifier(stream, options);
|
|
216
215
|
this.socketTimeoutMS = options.socketTimeoutMS ?? 0;
|
|
@@ -223,39 +222,12 @@ export class Connection extends TypedEventEmitter<ConnectionEvents> {
|
|
|
223
222
|
this.generation = options.generation;
|
|
224
223
|
this.lastUseTime = now();
|
|
225
224
|
|
|
226
|
-
this.socket = stream;
|
|
227
|
-
|
|
228
|
-
// TODO: Remove signal from connection layer
|
|
229
|
-
this.controller = new AbortController();
|
|
230
|
-
const { signal } = this.controller;
|
|
231
|
-
this.signal = signal;
|
|
232
|
-
const { promise: aborted, reject } = promiseWithResolvers<never>();
|
|
233
|
-
aborted.then(undefined, () => null); // Prevent unhandled rejection
|
|
234
|
-
this.signal.addEventListener(
|
|
235
|
-
'abort',
|
|
236
|
-
function onAbort() {
|
|
237
|
-
reject(signal.reason);
|
|
238
|
-
},
|
|
239
|
-
{ once: true }
|
|
240
|
-
);
|
|
241
|
-
this.aborted = aborted;
|
|
242
|
-
|
|
243
225
|
this.messageStream = this.socket
|
|
244
226
|
.on('error', this.onError.bind(this))
|
|
245
227
|
.pipe(new SizedMessageTransform({ connection: this }))
|
|
246
228
|
.on('error', this.onError.bind(this));
|
|
247
229
|
this.socket.on('close', this.onClose.bind(this));
|
|
248
230
|
this.socket.on('timeout', this.onTimeout.bind(this));
|
|
249
|
-
|
|
250
|
-
const socketWrite = promisify(this.socket.write.bind(this.socket));
|
|
251
|
-
this.socketWrite = async buffer => {
|
|
252
|
-
return Promise.race([socketWrite(buffer), this.aborted]);
|
|
253
|
-
};
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
/** Indicates that the connection (including underlying TCP socket) has been closed. */
|
|
257
|
-
public get closed(): boolean {
|
|
258
|
-
return this.signal.aborted;
|
|
259
231
|
}
|
|
260
232
|
|
|
261
233
|
public get hello() {
|
|
@@ -306,7 +278,7 @@ export class Connection extends TypedEventEmitter<ConnectionEvents> {
|
|
|
306
278
|
this.lastUseTime = now();
|
|
307
279
|
}
|
|
308
280
|
|
|
309
|
-
public onError(error
|
|
281
|
+
public onError(error: Error) {
|
|
310
282
|
this.cleanup(error);
|
|
311
283
|
}
|
|
312
284
|
|
|
@@ -323,14 +295,10 @@ export class Connection extends TypedEventEmitter<ConnectionEvents> {
|
|
|
323
295
|
}, 1).unref(); // No need for this timer to hold the event loop open
|
|
324
296
|
}
|
|
325
297
|
|
|
326
|
-
public destroy(
|
|
298
|
+
public destroy(): void {
|
|
327
299
|
if (this.closed) {
|
|
328
|
-
if (typeof callback === 'function') process.nextTick(callback);
|
|
329
300
|
return;
|
|
330
301
|
}
|
|
331
|
-
if (typeof callback === 'function') {
|
|
332
|
-
this.once('close', () => process.nextTick(() => callback()));
|
|
333
|
-
}
|
|
334
302
|
|
|
335
303
|
// load balanced mode requires that these listeners remain on the connection
|
|
336
304
|
// after cleanup on timeouts, errors or close so we remove them before calling
|
|
@@ -349,13 +317,15 @@ export class Connection extends TypedEventEmitter<ConnectionEvents> {
|
|
|
349
317
|
*
|
|
350
318
|
* This method does nothing if the connection is already closed.
|
|
351
319
|
*/
|
|
352
|
-
private cleanup(error
|
|
320
|
+
private cleanup(error: Error): void {
|
|
353
321
|
if (this.closed) {
|
|
354
322
|
return;
|
|
355
323
|
}
|
|
356
324
|
|
|
357
325
|
this.socket.destroy();
|
|
358
|
-
this.
|
|
326
|
+
this.error = error;
|
|
327
|
+
this.dataEvents?.throw(error).then(undefined, () => null); // squash unhandled rejection
|
|
328
|
+
this.closed = true;
|
|
359
329
|
this.emit(Connection.CLOSE);
|
|
360
330
|
}
|
|
361
331
|
|
|
@@ -394,16 +364,34 @@ export class Connection extends TypedEventEmitter<ConnectionEvents> {
|
|
|
394
364
|
cmd.$clusterTime = clusterTime;
|
|
395
365
|
}
|
|
396
366
|
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
367
|
+
// For standalone, drivers MUST NOT set $readPreference.
|
|
368
|
+
if (this.description.type !== ServerType.Standalone) {
|
|
369
|
+
if (
|
|
370
|
+
!isSharded(this) &&
|
|
371
|
+
!this.description.loadBalanced &&
|
|
372
|
+
this.supportsOpMsg &&
|
|
373
|
+
options.directConnection === true &&
|
|
374
|
+
readPreference?.mode === 'primary'
|
|
375
|
+
) {
|
|
376
|
+
// For mongos and load balancers with 'primary' mode, drivers MUST NOT set $readPreference.
|
|
377
|
+
// For all other types with a direct connection, if the read preference is 'primary'
|
|
378
|
+
// (driver sets 'primary' as default if no read preference is configured),
|
|
379
|
+
// the $readPreference MUST be set to 'primaryPreferred'
|
|
380
|
+
// to ensure that any server type can handle the request.
|
|
381
|
+
cmd.$readPreference = ReadPreference.primaryPreferred.toJSON();
|
|
382
|
+
} else if (isSharded(this) && !this.supportsOpMsg && readPreference?.mode !== 'primary') {
|
|
383
|
+
// When sending a read operation via OP_QUERY and the $readPreference modifier,
|
|
384
|
+
// the query MUST be provided using the $query modifier.
|
|
385
|
+
cmd = {
|
|
386
|
+
$query: cmd,
|
|
387
|
+
$readPreference: readPreference.toJSON()
|
|
388
|
+
};
|
|
389
|
+
} else if (readPreference?.mode !== 'primary') {
|
|
390
|
+
// For mode 'primary', drivers MUST NOT set $readPreference.
|
|
391
|
+
// For all other read preference modes (i.e. 'secondary', 'primaryPreferred', ...),
|
|
392
|
+
// drivers MUST set $readPreference
|
|
393
|
+
cmd.$readPreference = readPreference.toJSON();
|
|
394
|
+
}
|
|
407
395
|
}
|
|
408
396
|
|
|
409
397
|
const commandOptions = {
|
|
@@ -412,8 +400,7 @@ export class Connection extends TypedEventEmitter<ConnectionEvents> {
|
|
|
412
400
|
checkKeys: false,
|
|
413
401
|
// This value is not overridable
|
|
414
402
|
secondaryOk: readPreference.secondaryOk(),
|
|
415
|
-
...options
|
|
416
|
-
readPreference // ensure we pass in ReadPreference instance
|
|
403
|
+
...options
|
|
417
404
|
};
|
|
418
405
|
|
|
419
406
|
const message = this.supportsOpMsg
|
|
@@ -596,7 +583,7 @@ export class Connection extends TypedEventEmitter<ConnectionEvents> {
|
|
|
596
583
|
}
|
|
597
584
|
|
|
598
585
|
private throwIfAborted() {
|
|
599
|
-
this.
|
|
586
|
+
if (this.error) throw this.error;
|
|
600
587
|
}
|
|
601
588
|
|
|
602
589
|
/**
|
|
@@ -619,7 +606,8 @@ export class Connection extends TypedEventEmitter<ConnectionEvents> {
|
|
|
619
606
|
|
|
620
607
|
const buffer = Buffer.concat(await finalCommand.toBin());
|
|
621
608
|
|
|
622
|
-
|
|
609
|
+
if (this.socket.write(buffer)) return;
|
|
610
|
+
return once(this.socket, 'drain');
|
|
623
611
|
}
|
|
624
612
|
|
|
625
613
|
/**
|
|
@@ -632,13 +620,19 @@ export class Connection extends TypedEventEmitter<ConnectionEvents> {
|
|
|
632
620
|
* Note that `for-await` loops call `return` automatically when the loop is exited.
|
|
633
621
|
*/
|
|
634
622
|
private async *readMany(): AsyncGenerator<OpMsgResponse | OpQueryResponse> {
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
623
|
+
try {
|
|
624
|
+
this.dataEvents = onData(this.messageStream);
|
|
625
|
+
for await (const message of this.dataEvents) {
|
|
626
|
+
const response = await decompressResponse(message);
|
|
627
|
+
yield response;
|
|
638
628
|
|
|
639
|
-
|
|
640
|
-
|
|
629
|
+
if (!response.moreToCome) {
|
|
630
|
+
return;
|
|
631
|
+
}
|
|
641
632
|
}
|
|
633
|
+
} finally {
|
|
634
|
+
this.dataEvents = null;
|
|
635
|
+
this.throwIfAborted();
|
|
642
636
|
}
|
|
643
637
|
}
|
|
644
638
|
}
|
|
@@ -28,7 +28,6 @@ import { CancellationToken, TypedEventEmitter } from '../mongo_types';
|
|
|
28
28
|
import type { Server } from '../sdam/server';
|
|
29
29
|
import {
|
|
30
30
|
type Callback,
|
|
31
|
-
eachAsync,
|
|
32
31
|
List,
|
|
33
32
|
makeCounter,
|
|
34
33
|
promiseWithResolvers,
|
|
@@ -119,7 +118,11 @@ export const PoolState = Object.freeze({
|
|
|
119
118
|
closed: 'closed'
|
|
120
119
|
} as const);
|
|
121
120
|
|
|
122
|
-
/**
|
|
121
|
+
/**
|
|
122
|
+
* @public
|
|
123
|
+
* @deprecated This interface is deprecated and will be removed in a future release as it is not used
|
|
124
|
+
* in the driver
|
|
125
|
+
*/
|
|
123
126
|
export interface CloseOptions {
|
|
124
127
|
force?: boolean;
|
|
125
128
|
}
|
|
@@ -233,8 +236,7 @@ export class ConnectionPool extends TypedEventEmitter<ConnectionPoolEvents> {
|
|
|
233
236
|
maxIdleTimeMS: options.maxIdleTimeMS ?? 0,
|
|
234
237
|
waitQueueTimeoutMS: options.waitQueueTimeoutMS ?? 0,
|
|
235
238
|
minPoolSizeCheckFrequencyMS: options.minPoolSizeCheckFrequencyMS ?? 100,
|
|
236
|
-
autoEncrypter: options.autoEncrypter
|
|
237
|
-
metadata: options.metadata
|
|
239
|
+
autoEncrypter: options.autoEncrypter
|
|
238
240
|
});
|
|
239
241
|
|
|
240
242
|
if (this.options.minPoolSize > this.options.maxPoolSize) {
|
|
@@ -494,25 +496,16 @@ export class ConnectionPool extends TypedEventEmitter<ConnectionPoolEvents> {
|
|
|
494
496
|
private interruptInUseConnections(minGeneration: number) {
|
|
495
497
|
for (const connection of this[kCheckedOut]) {
|
|
496
498
|
if (connection.generation <= minGeneration) {
|
|
497
|
-
this.checkIn(connection);
|
|
498
499
|
connection.onError(new PoolClearedOnNetworkError(this));
|
|
500
|
+
this.checkIn(connection);
|
|
499
501
|
}
|
|
500
502
|
}
|
|
501
503
|
}
|
|
502
504
|
|
|
503
505
|
/** Close the pool */
|
|
504
|
-
close(
|
|
505
|
-
close(options: CloseOptions, callback: Callback<void>): void;
|
|
506
|
-
close(_options?: CloseOptions | Callback<void>, _cb?: Callback<void>): void {
|
|
507
|
-
let options = _options as CloseOptions;
|
|
508
|
-
const callback = (_cb ?? _options) as Callback<void>;
|
|
509
|
-
if (typeof options === 'function') {
|
|
510
|
-
options = {};
|
|
511
|
-
}
|
|
512
|
-
|
|
513
|
-
options = Object.assign({ force: false }, options);
|
|
506
|
+
close(): void {
|
|
514
507
|
if (this.closed) {
|
|
515
|
-
return
|
|
508
|
+
return;
|
|
516
509
|
}
|
|
517
510
|
|
|
518
511
|
// immediately cancel any in-flight connections
|
|
@@ -527,21 +520,15 @@ export class ConnectionPool extends TypedEventEmitter<ConnectionPoolEvents> {
|
|
|
527
520
|
this.clearMinPoolSizeTimer();
|
|
528
521
|
this.processWaitQueue();
|
|
529
522
|
|
|
530
|
-
|
|
531
|
-
this
|
|
532
|
-
|
|
533
|
-
this
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
err => {
|
|
540
|
-
this[kConnections].clear();
|
|
541
|
-
this.emitAndLog(ConnectionPool.CONNECTION_POOL_CLOSED, new ConnectionPoolClosedEvent(this));
|
|
542
|
-
callback(err);
|
|
543
|
-
}
|
|
544
|
-
);
|
|
523
|
+
for (const conn of this[kConnections]) {
|
|
524
|
+
this.emitAndLog(
|
|
525
|
+
ConnectionPool.CONNECTION_CLOSED,
|
|
526
|
+
new ConnectionClosedEvent(this, conn, 'poolClosed')
|
|
527
|
+
);
|
|
528
|
+
conn.destroy();
|
|
529
|
+
}
|
|
530
|
+
this[kConnections].clear();
|
|
531
|
+
this.emitAndLog(ConnectionPool.CONNECTION_POOL_CLOSED, new ConnectionPoolClosedEvent(this));
|
|
545
532
|
}
|
|
546
533
|
|
|
547
534
|
/**
|
|
@@ -593,7 +580,7 @@ export class ConnectionPool extends TypedEventEmitter<ConnectionPoolEvents> {
|
|
|
593
580
|
new ConnectionClosedEvent(this, connection, reason)
|
|
594
581
|
);
|
|
595
582
|
// destroy the connection
|
|
596
|
-
|
|
583
|
+
connection.destroy();
|
|
597
584
|
}
|
|
598
585
|
|
|
599
586
|
private connectionIsStale(connection: Connection) {
|
|
@@ -649,7 +636,7 @@ export class ConnectionPool extends TypedEventEmitter<ConnectionPoolEvents> {
|
|
|
649
636
|
// The pool might have closed since we started trying to create a connection
|
|
650
637
|
if (this[kPoolState] !== PoolState.ready) {
|
|
651
638
|
this[kPending]--;
|
|
652
|
-
connection.destroy(
|
|
639
|
+
connection.destroy();
|
|
653
640
|
callback(this.closed ? new PoolClosedError(this) : new PoolClearedError(this));
|
|
654
641
|
return;
|
|
655
642
|
}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
+
import { promises as fs } from 'fs';
|
|
1
2
|
import * as os from 'os';
|
|
2
3
|
import * as process from 'process';
|
|
3
4
|
|
|
4
|
-
import { BSON, Int32 } from '../../bson';
|
|
5
|
+
import { BSON, type Document, Int32 } from '../../bson';
|
|
5
6
|
import { MongoInvalidArgumentError } from '../../error';
|
|
6
7
|
import type { MongoOptions } from '../../mongo_client';
|
|
7
8
|
|
|
@@ -71,13 +72,13 @@ export class LimitedSizeDocument {
|
|
|
71
72
|
return true;
|
|
72
73
|
}
|
|
73
74
|
|
|
74
|
-
toObject():
|
|
75
|
+
toObject(): Document {
|
|
75
76
|
return BSON.deserialize(BSON.serialize(this.document), {
|
|
76
77
|
promoteLongs: false,
|
|
77
78
|
promoteBuffers: false,
|
|
78
79
|
promoteValues: false,
|
|
79
80
|
useBigInt64: false
|
|
80
|
-
})
|
|
81
|
+
});
|
|
81
82
|
}
|
|
82
83
|
}
|
|
83
84
|
|
|
@@ -152,8 +153,57 @@ export function makeClientMetadata(options: MakeClientMetadataOptions): ClientMe
|
|
|
152
153
|
}
|
|
153
154
|
}
|
|
154
155
|
}
|
|
156
|
+
return metadataDocument.toObject() as ClientMetadata;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
let dockerPromise: Promise<boolean>;
|
|
160
|
+
/** @internal */
|
|
161
|
+
async function getContainerMetadata() {
|
|
162
|
+
const containerMetadata: Record<string, any> = {};
|
|
163
|
+
dockerPromise ??= fs.access('/.dockerenv').then(
|
|
164
|
+
() => true,
|
|
165
|
+
() => false
|
|
166
|
+
);
|
|
167
|
+
const isDocker = await dockerPromise;
|
|
168
|
+
|
|
169
|
+
const { KUBERNETES_SERVICE_HOST = '' } = process.env;
|
|
170
|
+
const isKubernetes = KUBERNETES_SERVICE_HOST.length > 0 ? true : false;
|
|
171
|
+
|
|
172
|
+
if (isDocker) containerMetadata.runtime = 'docker';
|
|
173
|
+
if (isKubernetes) containerMetadata.orchestrator = 'kubernetes';
|
|
174
|
+
|
|
175
|
+
return containerMetadata;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* @internal
|
|
180
|
+
* Re-add each metadata value.
|
|
181
|
+
* Attempt to add new env container metadata, but keep old data if it does not fit.
|
|
182
|
+
*/
|
|
183
|
+
export async function addContainerMetadata(originalMetadata: ClientMetadata) {
|
|
184
|
+
const containerMetadata = await getContainerMetadata();
|
|
185
|
+
if (Object.keys(containerMetadata).length === 0) return originalMetadata;
|
|
186
|
+
|
|
187
|
+
const extendedMetadata = new LimitedSizeDocument(512);
|
|
188
|
+
|
|
189
|
+
const extendedEnvMetadata = { ...originalMetadata?.env, container: containerMetadata };
|
|
190
|
+
|
|
191
|
+
for (const [key, val] of Object.entries(originalMetadata)) {
|
|
192
|
+
if (key !== 'env') {
|
|
193
|
+
extendedMetadata.ifItFitsItSits(key, val);
|
|
194
|
+
} else {
|
|
195
|
+
if (!extendedMetadata.ifItFitsItSits('env', extendedEnvMetadata)) {
|
|
196
|
+
// add in old data if newer / extended metadata does not fit
|
|
197
|
+
extendedMetadata.ifItFitsItSits('env', val);
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
if (!('env' in originalMetadata)) {
|
|
203
|
+
extendedMetadata.ifItFitsItSits('env', extendedEnvMetadata);
|
|
204
|
+
}
|
|
155
205
|
|
|
156
|
-
return
|
|
206
|
+
return extendedMetadata.toObject();
|
|
157
207
|
}
|
|
158
208
|
|
|
159
209
|
/**
|