mongodb 6.12.0-dev.20241211.sha.2f9ad4d4 → 6.12.0-dev.20241212.sha.f6d7868f
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 +16 -85
- 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/cmap/connection_pool.js +83 -117
- package/lib/cmap/connection_pool.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/mongo_client.js +20 -26
- package/lib/mongo_client.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 +1 -1
- package/lib/sdam/server.js.map +1 -1
- package/lib/sdam/topology.js +13 -16
- package/lib/sdam/topology.js.map +1 -1
- package/lib/sessions.js +24 -48
- package/lib/sessions.js.map +1 -1
- package/mongodb.d.ts +16 -85
- package/package.json +1 -1
- package/src/bulk/common.ts +6 -9
- package/src/change_stream.ts +21 -33
- package/src/cmap/connection_pool.ts +104 -142
- package/src/encrypter.ts +6 -11
- package/src/error.ts +11 -23
- package/src/mongo_client.ts +26 -32
- package/src/operations/operation.ts +5 -7
- package/src/sdam/monitor.ts +30 -38
- package/src/sdam/server.ts +2 -2
- package/src/sdam/topology.ts +15 -19
- package/src/sessions.ts +37 -58
package/src/error.ts
CHANGED
|
@@ -10,9 +10,6 @@ import type { TopologyDescription } from './sdam/topology_description';
|
|
|
10
10
|
/** @public */
|
|
11
11
|
export type AnyError = MongoError | Error;
|
|
12
12
|
|
|
13
|
-
/** @internal */
|
|
14
|
-
const kErrorLabels = Symbol('errorLabels');
|
|
15
|
-
|
|
16
13
|
/**
|
|
17
14
|
* @internal
|
|
18
15
|
* The legacy error message from the server that indicates the node is not a writable primary
|
|
@@ -129,7 +126,11 @@ function isAggregateError(e: unknown): e is Error & { errors: Error[] } {
|
|
|
129
126
|
*/
|
|
130
127
|
export class MongoError extends Error {
|
|
131
128
|
/** @internal */
|
|
132
|
-
|
|
129
|
+
private readonly errorLabelSet: Set<string> = new Set();
|
|
130
|
+
public get errorLabels(): string[] {
|
|
131
|
+
return Array.from(this.errorLabelSet);
|
|
132
|
+
}
|
|
133
|
+
|
|
133
134
|
/**
|
|
134
135
|
* This is a number in MongoServerError and a string in MongoDriverError
|
|
135
136
|
* @privateRemarks
|
|
@@ -153,7 +154,6 @@ export class MongoError extends Error {
|
|
|
153
154
|
**/
|
|
154
155
|
constructor(message: string, options?: { cause?: Error }) {
|
|
155
156
|
super(message, options);
|
|
156
|
-
this[kErrorLabels] = new Set();
|
|
157
157
|
}
|
|
158
158
|
|
|
159
159
|
/** @internal */
|
|
@@ -188,15 +188,11 @@ export class MongoError extends Error {
|
|
|
188
188
|
* @returns returns true if the error has the provided error label
|
|
189
189
|
*/
|
|
190
190
|
hasErrorLabel(label: string): boolean {
|
|
191
|
-
return this
|
|
191
|
+
return this.errorLabelSet.has(label);
|
|
192
192
|
}
|
|
193
193
|
|
|
194
194
|
addErrorLabel(label: string): void {
|
|
195
|
-
this
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
get errorLabels(): string[] {
|
|
199
|
-
return Array.from(this[kErrorLabels]);
|
|
195
|
+
this.errorLabelSet.add(label);
|
|
200
196
|
}
|
|
201
197
|
}
|
|
202
198
|
|
|
@@ -228,8 +224,9 @@ export class MongoServerError extends MongoError {
|
|
|
228
224
|
**/
|
|
229
225
|
constructor(message: ErrorDescription) {
|
|
230
226
|
super(message.message || message.errmsg || message.$err || 'n/a');
|
|
227
|
+
|
|
231
228
|
if (message.errorLabels) {
|
|
232
|
-
|
|
229
|
+
for (const label of message.errorLabels) this.addErrorLabel(label);
|
|
233
230
|
}
|
|
234
231
|
|
|
235
232
|
this.errorResponse = message;
|
|
@@ -1028,12 +1025,6 @@ export class MongoTopologyClosedError extends MongoAPIError {
|
|
|
1028
1025
|
}
|
|
1029
1026
|
}
|
|
1030
1027
|
|
|
1031
|
-
/** @internal */
|
|
1032
|
-
const kBeforeHandshake = Symbol('beforeHandshake');
|
|
1033
|
-
export function isNetworkErrorBeforeHandshake(err: MongoNetworkError): boolean {
|
|
1034
|
-
return err[kBeforeHandshake] === true;
|
|
1035
|
-
}
|
|
1036
|
-
|
|
1037
1028
|
/** @public */
|
|
1038
1029
|
export interface MongoNetworkErrorOptions {
|
|
1039
1030
|
/** Indicates the timeout happened before a connection handshake completed */
|
|
@@ -1048,7 +1039,7 @@ export interface MongoNetworkErrorOptions {
|
|
|
1048
1039
|
*/
|
|
1049
1040
|
export class MongoNetworkError extends MongoError {
|
|
1050
1041
|
/** @internal */
|
|
1051
|
-
|
|
1042
|
+
public readonly beforeHandshake: boolean;
|
|
1052
1043
|
|
|
1053
1044
|
/**
|
|
1054
1045
|
* **Do not use this constructor!**
|
|
@@ -1063,10 +1054,7 @@ export class MongoNetworkError extends MongoError {
|
|
|
1063
1054
|
**/
|
|
1064
1055
|
constructor(message: string, options?: MongoNetworkErrorOptions) {
|
|
1065
1056
|
super(message, { cause: options?.cause });
|
|
1066
|
-
|
|
1067
|
-
if (options && typeof options.beforeHandshake === 'boolean') {
|
|
1068
|
-
this[kBeforeHandshake] = options.beforeHandshake;
|
|
1069
|
-
}
|
|
1057
|
+
this.beforeHandshake = !!options?.beforeHandshake;
|
|
1070
1058
|
}
|
|
1071
1059
|
|
|
1072
1060
|
override get name(): string {
|
package/src/mongo_client.ts
CHANGED
|
@@ -333,9 +333,6 @@ export type MongoClientEvents = Pick<TopologyEvents, (typeof MONGO_CLIENT_EVENTS
|
|
|
333
333
|
open(mongoClient: MongoClient): void;
|
|
334
334
|
};
|
|
335
335
|
|
|
336
|
-
/** @internal */
|
|
337
|
-
const kOptions = Symbol('options');
|
|
338
|
-
|
|
339
336
|
/**
|
|
340
337
|
* The **MongoClient** class is a class that allows for making Connections to MongoDB.
|
|
341
338
|
* @public
|
|
@@ -367,20 +364,22 @@ export class MongoClient extends TypedEventEmitter<MongoClientEvents> implements
|
|
|
367
364
|
|
|
368
365
|
/**
|
|
369
366
|
* The consolidate, parsed, transformed and merged options.
|
|
370
|
-
* @internal
|
|
371
367
|
*/
|
|
372
|
-
|
|
368
|
+
public readonly options: Readonly<
|
|
369
|
+
Omit<MongoOptions, 'monitorCommands' | 'ca' | 'crl' | 'key' | 'cert'>
|
|
370
|
+
> &
|
|
371
|
+
Pick<MongoOptions, 'monitorCommands' | 'ca' | 'crl' | 'key' | 'cert'>;
|
|
373
372
|
|
|
374
373
|
constructor(url: string, options?: MongoClientOptions) {
|
|
375
374
|
super();
|
|
376
375
|
|
|
377
|
-
this
|
|
376
|
+
this.options = parseOptions(url, this, options);
|
|
378
377
|
|
|
379
|
-
const shouldSetLogger = Object.values(
|
|
380
|
-
|
|
381
|
-
)
|
|
378
|
+
const shouldSetLogger = Object.values(this.options.mongoLoggerOptions.componentSeverities).some(
|
|
379
|
+
value => value !== SeverityLevel.OFF
|
|
380
|
+
);
|
|
382
381
|
this.mongoLogger = shouldSetLogger
|
|
383
|
-
? new MongoLogger(this
|
|
382
|
+
? new MongoLogger(this.options.mongoLoggerOptions)
|
|
384
383
|
: undefined;
|
|
385
384
|
|
|
386
385
|
// eslint-disable-next-line @typescript-eslint/no-this-alias
|
|
@@ -389,7 +388,7 @@ export class MongoClient extends TypedEventEmitter<MongoClientEvents> implements
|
|
|
389
388
|
// The internal state
|
|
390
389
|
this.s = {
|
|
391
390
|
url,
|
|
392
|
-
bsonOptions: resolveBSONOptions(this
|
|
391
|
+
bsonOptions: resolveBSONOptions(this.options),
|
|
393
392
|
namespace: ns('admin'),
|
|
394
393
|
hasBeenClosed: false,
|
|
395
394
|
sessionPool: new ServerSessionPool(this),
|
|
@@ -397,16 +396,16 @@ export class MongoClient extends TypedEventEmitter<MongoClientEvents> implements
|
|
|
397
396
|
authProviders: new MongoClientAuthProviders(),
|
|
398
397
|
|
|
399
398
|
get options() {
|
|
400
|
-
return client
|
|
399
|
+
return client.options;
|
|
401
400
|
},
|
|
402
401
|
get readConcern() {
|
|
403
|
-
return client
|
|
402
|
+
return client.options.readConcern;
|
|
404
403
|
},
|
|
405
404
|
get writeConcern() {
|
|
406
|
-
return client
|
|
405
|
+
return client.options.writeConcern;
|
|
407
406
|
},
|
|
408
407
|
get readPreference() {
|
|
409
|
-
return client
|
|
408
|
+
return client.options.readPreference;
|
|
410
409
|
},
|
|
411
410
|
get isMongoClient(): true {
|
|
412
411
|
return true;
|
|
@@ -428,15 +427,15 @@ export class MongoClient extends TypedEventEmitter<MongoClientEvents> implements
|
|
|
428
427
|
|
|
429
428
|
/** @internal */
|
|
430
429
|
private checkForNonGenuineHosts() {
|
|
431
|
-
const documentDBHostnames = this
|
|
430
|
+
const documentDBHostnames = this.options.hosts.filter((hostAddress: HostAddress) =>
|
|
432
431
|
isHostMatch(DOCUMENT_DB_CHECK, hostAddress.host)
|
|
433
432
|
);
|
|
434
|
-
const srvHostIsDocumentDB = isHostMatch(DOCUMENT_DB_CHECK, this
|
|
433
|
+
const srvHostIsDocumentDB = isHostMatch(DOCUMENT_DB_CHECK, this.options.srvHost);
|
|
435
434
|
|
|
436
|
-
const cosmosDBHostnames = this
|
|
435
|
+
const cosmosDBHostnames = this.options.hosts.filter((hostAddress: HostAddress) =>
|
|
437
436
|
isHostMatch(COSMOS_DB_CHECK, hostAddress.host)
|
|
438
437
|
);
|
|
439
|
-
const srvHostIsCosmosDB = isHostMatch(COSMOS_DB_CHECK, this
|
|
438
|
+
const srvHostIsCosmosDB = isHostMatch(COSMOS_DB_CHECK, this.options.srvHost);
|
|
440
439
|
|
|
441
440
|
if (documentDBHostnames.length !== 0 || srvHostIsDocumentDB) {
|
|
442
441
|
this.mongoLogger?.info('client', DOCUMENT_DB_MSG);
|
|
@@ -445,28 +444,23 @@ export class MongoClient extends TypedEventEmitter<MongoClientEvents> implements
|
|
|
445
444
|
}
|
|
446
445
|
}
|
|
447
446
|
|
|
448
|
-
/** @see MongoOptions */
|
|
449
|
-
get options(): Readonly<MongoOptions> {
|
|
450
|
-
return Object.freeze({ ...this[kOptions] });
|
|
451
|
-
}
|
|
452
|
-
|
|
453
447
|
get serverApi(): Readonly<ServerApi | undefined> {
|
|
454
|
-
return this
|
|
448
|
+
return this.options.serverApi && Object.freeze({ ...this.options.serverApi });
|
|
455
449
|
}
|
|
456
450
|
/**
|
|
457
451
|
* Intended for APM use only
|
|
458
452
|
* @internal
|
|
459
453
|
*/
|
|
460
454
|
get monitorCommands(): boolean {
|
|
461
|
-
return this
|
|
455
|
+
return this.options.monitorCommands;
|
|
462
456
|
}
|
|
463
457
|
set monitorCommands(value: boolean) {
|
|
464
|
-
this
|
|
458
|
+
this.options.monitorCommands = value;
|
|
465
459
|
}
|
|
466
460
|
|
|
467
461
|
/** @internal */
|
|
468
462
|
get autoEncrypter(): AutoEncrypter | undefined {
|
|
469
|
-
return this
|
|
463
|
+
return this.options.autoEncrypter;
|
|
470
464
|
}
|
|
471
465
|
|
|
472
466
|
get readConcern(): ReadConcern | undefined {
|
|
@@ -551,7 +545,7 @@ export class MongoClient extends TypedEventEmitter<MongoClientEvents> implements
|
|
|
551
545
|
return this;
|
|
552
546
|
}
|
|
553
547
|
|
|
554
|
-
const options = this
|
|
548
|
+
const options = this.options;
|
|
555
549
|
|
|
556
550
|
if (options.tls) {
|
|
557
551
|
if (typeof options.tlsCAFile === 'string') {
|
|
@@ -685,7 +679,7 @@ export class MongoClient extends TypedEventEmitter<MongoClientEvents> implements
|
|
|
685
679
|
|
|
686
680
|
topology.close();
|
|
687
681
|
|
|
688
|
-
const { encrypter } = this
|
|
682
|
+
const { encrypter } = this.options;
|
|
689
683
|
if (encrypter) {
|
|
690
684
|
await encrypter.close(this, force);
|
|
691
685
|
}
|
|
@@ -706,7 +700,7 @@ export class MongoClient extends TypedEventEmitter<MongoClientEvents> implements
|
|
|
706
700
|
}
|
|
707
701
|
|
|
708
702
|
// Copy the options and add out internal override of the not shared flag
|
|
709
|
-
const finalOptions = Object.assign({}, this
|
|
703
|
+
const finalOptions = Object.assign({}, this.options, options);
|
|
710
704
|
|
|
711
705
|
// Return the db object
|
|
712
706
|
const db = new Db(this, dbName, finalOptions);
|
|
@@ -748,7 +742,7 @@ export class MongoClient extends TypedEventEmitter<MongoClientEvents> implements
|
|
|
748
742
|
this,
|
|
749
743
|
this.s.sessionPool,
|
|
750
744
|
{ explicit: true, ...options },
|
|
751
|
-
this
|
|
745
|
+
this.options
|
|
752
746
|
);
|
|
753
747
|
this.s.activeSessions.add(session);
|
|
754
748
|
session.once('ended', () => {
|
|
@@ -42,9 +42,6 @@ export interface OperationOptions extends BSONSerializeOptions {
|
|
|
42
42
|
timeoutMS?: number;
|
|
43
43
|
}
|
|
44
44
|
|
|
45
|
-
/** @internal */
|
|
46
|
-
const kSession = Symbol('session');
|
|
47
|
-
|
|
48
45
|
/**
|
|
49
46
|
* This class acts as a parent class for any operation and is responsible for setting this.options,
|
|
50
47
|
* as well as setting and getting a session.
|
|
@@ -67,7 +64,7 @@ export abstract class AbstractOperation<TResult = any> {
|
|
|
67
64
|
/** Specifies the time an operation will run until it throws a timeout error. */
|
|
68
65
|
timeoutMS?: number;
|
|
69
66
|
|
|
70
|
-
|
|
67
|
+
private _session: ClientSession | undefined;
|
|
71
68
|
|
|
72
69
|
static aspects?: Set<symbol>;
|
|
73
70
|
|
|
@@ -79,7 +76,7 @@ export abstract class AbstractOperation<TResult = any> {
|
|
|
79
76
|
// Pull the BSON serialize options from the already-resolved options
|
|
80
77
|
this.bsonOptions = resolveBSONOptions(options);
|
|
81
78
|
|
|
82
|
-
this
|
|
79
|
+
this._session = options.session != null ? options.session : undefined;
|
|
83
80
|
|
|
84
81
|
this.options = options;
|
|
85
82
|
this.bypassPinningCheck = !!options.bypassPinningCheck;
|
|
@@ -105,12 +102,13 @@ export abstract class AbstractOperation<TResult = any> {
|
|
|
105
102
|
return ctor.aspects.has(aspect);
|
|
106
103
|
}
|
|
107
104
|
|
|
105
|
+
// Make sure the session is not writable from outside this class.
|
|
108
106
|
get session(): ClientSession | undefined {
|
|
109
|
-
return this
|
|
107
|
+
return this._session;
|
|
110
108
|
}
|
|
111
109
|
|
|
112
110
|
clearSession() {
|
|
113
|
-
this
|
|
111
|
+
this._session = undefined;
|
|
114
112
|
}
|
|
115
113
|
|
|
116
114
|
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
|
);
|
package/src/sdam/server.ts
CHANGED
|
@@ -22,7 +22,6 @@ import {
|
|
|
22
22
|
} from '../constants';
|
|
23
23
|
import {
|
|
24
24
|
type AnyError,
|
|
25
|
-
isNetworkErrorBeforeHandshake,
|
|
26
25
|
isNodeShuttingDownError,
|
|
27
26
|
isSDAMUnrecoverableError,
|
|
28
27
|
MONGODB_ERROR_CODES,
|
|
@@ -381,7 +380,8 @@ export class Server extends TypedEventEmitter<ServerEvents> {
|
|
|
381
380
|
|
|
382
381
|
const isNetworkNonTimeoutError =
|
|
383
382
|
error instanceof MongoNetworkError && !(error instanceof MongoNetworkTimeoutError);
|
|
384
|
-
const isNetworkTimeoutBeforeHandshakeError =
|
|
383
|
+
const isNetworkTimeoutBeforeHandshakeError =
|
|
384
|
+
error instanceof MongoNetworkError && error.beforeHandshake;
|
|
385
385
|
const isAuthHandshakeError = error.hasErrorLabel(MongoErrorLabel.HandshakeError);
|
|
386
386
|
if (isNetworkNonTimeoutError || isNetworkTimeoutBeforeHandshakeError || isAuthHandshakeError) {
|
|
387
387
|
// In load balanced mode we never mark the server as unknown and always
|
package/src/sdam/topology.ts
CHANGED
|
@@ -88,11 +88,6 @@ const stateTransition = makeStateMachine({
|
|
|
88
88
|
[STATE_CLOSING]: [STATE_CLOSING, STATE_CLOSED]
|
|
89
89
|
});
|
|
90
90
|
|
|
91
|
-
/** @internal */
|
|
92
|
-
const kCancelled = Symbol('cancelled');
|
|
93
|
-
/** @internal */
|
|
94
|
-
const kWaitQueue = Symbol('waitQueue');
|
|
95
|
-
|
|
96
91
|
/** @internal */
|
|
97
92
|
export type ServerSelectionCallback = Callback<Server>;
|
|
98
93
|
|
|
@@ -105,7 +100,7 @@ export interface ServerSelectionRequest {
|
|
|
105
100
|
startTime: number;
|
|
106
101
|
resolve: (server: Server) => void;
|
|
107
102
|
reject: (error: MongoError) => void;
|
|
108
|
-
|
|
103
|
+
cancelled: boolean;
|
|
109
104
|
operationName: string;
|
|
110
105
|
waitingLogged: boolean;
|
|
111
106
|
previousServer?: ServerDescription;
|
|
@@ -208,7 +203,7 @@ export class Topology extends TypedEventEmitter<TopologyEvents> {
|
|
|
208
203
|
/** @internal */
|
|
209
204
|
s: TopologyPrivate;
|
|
210
205
|
/** @internal */
|
|
211
|
-
|
|
206
|
+
waitQueue: List<ServerSelectionRequest>;
|
|
212
207
|
/** @internal */
|
|
213
208
|
hello?: Document;
|
|
214
209
|
/** @internal */
|
|
@@ -293,7 +288,7 @@ export class Topology extends TypedEventEmitter<TopologyEvents> {
|
|
|
293
288
|
serverDescriptions.set(hostAddress.toString(), new ServerDescription(hostAddress));
|
|
294
289
|
}
|
|
295
290
|
|
|
296
|
-
this
|
|
291
|
+
this.waitQueue = new List();
|
|
297
292
|
this.s = {
|
|
298
293
|
// the id of this topology
|
|
299
294
|
id: topologyId,
|
|
@@ -506,7 +501,7 @@ export class Topology extends TypedEventEmitter<TopologyEvents> {
|
|
|
506
501
|
|
|
507
502
|
stateTransition(this, STATE_CLOSING);
|
|
508
503
|
|
|
509
|
-
drainWaitQueue(this
|
|
504
|
+
drainWaitQueue(this.waitQueue, new MongoTopologyClosedError());
|
|
510
505
|
|
|
511
506
|
if (this.s.srvPoller) {
|
|
512
507
|
this.s.srvPoller.stop();
|
|
@@ -601,13 +596,14 @@ export class Topology extends TypedEventEmitter<TopologyEvents> {
|
|
|
601
596
|
transaction,
|
|
602
597
|
resolve,
|
|
603
598
|
reject,
|
|
599
|
+
cancelled: false,
|
|
604
600
|
startTime: now(),
|
|
605
601
|
operationName: options.operationName,
|
|
606
602
|
waitingLogged: false,
|
|
607
603
|
previousServer: options.previousServer
|
|
608
604
|
};
|
|
609
605
|
|
|
610
|
-
this
|
|
606
|
+
this.waitQueue.push(waitQueueMember);
|
|
611
607
|
processWaitQueue(this);
|
|
612
608
|
|
|
613
609
|
try {
|
|
@@ -620,7 +616,7 @@ export class Topology extends TypedEventEmitter<TopologyEvents> {
|
|
|
620
616
|
} catch (error) {
|
|
621
617
|
if (TimeoutError.is(error)) {
|
|
622
618
|
// Timeout
|
|
623
|
-
waitQueueMember
|
|
619
|
+
waitQueueMember.cancelled = true;
|
|
624
620
|
const timeoutError = new MongoServerSelectionError(
|
|
625
621
|
`Server selection timed out after ${timeout?.duration} ms`,
|
|
626
622
|
this.description
|
|
@@ -721,7 +717,7 @@ export class Topology extends TypedEventEmitter<TopologyEvents> {
|
|
|
721
717
|
updateServers(this, serverDescription);
|
|
722
718
|
|
|
723
719
|
// attempt to resolve any outstanding server selection attempts
|
|
724
|
-
if (this
|
|
720
|
+
if (this.waitQueue.length > 0) {
|
|
725
721
|
processWaitQueue(this);
|
|
726
722
|
}
|
|
727
723
|
|
|
@@ -910,7 +906,7 @@ function drainWaitQueue(queue: List<ServerSelectionRequest>, drainError: MongoDr
|
|
|
910
906
|
continue;
|
|
911
907
|
}
|
|
912
908
|
|
|
913
|
-
if (!waitQueueMember
|
|
909
|
+
if (!waitQueueMember.cancelled) {
|
|
914
910
|
if (
|
|
915
911
|
waitQueueMember.mongoLogger?.willLog(
|
|
916
912
|
MongoLoggableComponent.SERVER_SELECTION,
|
|
@@ -934,20 +930,20 @@ function drainWaitQueue(queue: List<ServerSelectionRequest>, drainError: MongoDr
|
|
|
934
930
|
|
|
935
931
|
function processWaitQueue(topology: Topology) {
|
|
936
932
|
if (topology.s.state === STATE_CLOSED) {
|
|
937
|
-
drainWaitQueue(topology
|
|
933
|
+
drainWaitQueue(topology.waitQueue, new MongoTopologyClosedError());
|
|
938
934
|
return;
|
|
939
935
|
}
|
|
940
936
|
|
|
941
937
|
const isSharded = topology.description.type === TopologyType.Sharded;
|
|
942
938
|
const serverDescriptions = Array.from(topology.description.servers.values());
|
|
943
|
-
const membersToProcess = topology
|
|
939
|
+
const membersToProcess = topology.waitQueue.length;
|
|
944
940
|
for (let i = 0; i < membersToProcess; ++i) {
|
|
945
|
-
const waitQueueMember = topology
|
|
941
|
+
const waitQueueMember = topology.waitQueue.shift();
|
|
946
942
|
if (!waitQueueMember) {
|
|
947
943
|
continue;
|
|
948
944
|
}
|
|
949
945
|
|
|
950
|
-
if (waitQueueMember
|
|
946
|
+
if (waitQueueMember.cancelled) {
|
|
951
947
|
continue;
|
|
952
948
|
}
|
|
953
949
|
|
|
@@ -1006,7 +1002,7 @@ function processWaitQueue(topology: Topology) {
|
|
|
1006
1002
|
}
|
|
1007
1003
|
waitQueueMember.waitingLogged = true;
|
|
1008
1004
|
}
|
|
1009
|
-
topology
|
|
1005
|
+
topology.waitQueue.push(waitQueueMember);
|
|
1010
1006
|
continue;
|
|
1011
1007
|
} else if (selectedDescriptions.length === 1) {
|
|
1012
1008
|
selectedServer = topology.s.servers.get(selectedDescriptions[0].address);
|
|
@@ -1069,7 +1065,7 @@ function processWaitQueue(topology: Topology) {
|
|
|
1069
1065
|
waitQueueMember.resolve(selectedServer);
|
|
1070
1066
|
}
|
|
1071
1067
|
|
|
1072
|
-
if (topology
|
|
1068
|
+
if (topology.waitQueue.length > 0) {
|
|
1073
1069
|
// ensure all server monitors attempt monitoring soon
|
|
1074
1070
|
for (const [, server] of topology.s.servers) {
|
|
1075
1071
|
process.nextTick(function scheduleServerCheck() {
|