mongodb 6.11.0 → 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.
Files changed (55) hide show
  1. package/README.md +9 -8
  2. package/lib/beta.d.ts +43 -85
  3. package/lib/bulk/common.js +5 -7
  4. package/lib/bulk/common.js.map +1 -1
  5. package/lib/change_stream.js +16 -26
  6. package/lib/change_stream.js.map +1 -1
  7. package/lib/cmap/connect.js +15 -28
  8. package/lib/cmap/connect.js.map +1 -1
  9. package/lib/cmap/connection.js +1 -1
  10. package/lib/cmap/connection.js.map +1 -1
  11. package/lib/cmap/connection_pool.js +83 -117
  12. package/lib/cmap/connection_pool.js.map +1 -1
  13. package/lib/collection.js +3 -0
  14. package/lib/collection.js.map +1 -1
  15. package/lib/encrypter.js +5 -9
  16. package/lib/encrypter.js.map +1 -1
  17. package/lib/error.js +37 -19
  18. package/lib/error.js.map +1 -1
  19. package/lib/index.js +3 -2
  20. package/lib/index.js.map +1 -1
  21. package/lib/mongo_client.js +20 -26
  22. package/lib/mongo_client.js.map +1 -1
  23. package/lib/operations/operation.js +4 -5
  24. package/lib/operations/operation.js.map +1 -1
  25. package/lib/sdam/monitor.js +25 -31
  26. package/lib/sdam/monitor.js.map +1 -1
  27. package/lib/sdam/server.js +1 -1
  28. package/lib/sdam/server.js.map +1 -1
  29. package/lib/sdam/server_description.js +3 -0
  30. package/lib/sdam/server_description.js.map +1 -1
  31. package/lib/sdam/topology.js +13 -16
  32. package/lib/sdam/topology.js.map +1 -1
  33. package/lib/sdam/topology_description.js +9 -3
  34. package/lib/sdam/topology_description.js.map +1 -1
  35. package/lib/sessions.js +24 -48
  36. package/lib/sessions.js.map +1 -1
  37. package/mongodb.d.ts +43 -85
  38. package/package.json +5 -5
  39. package/src/bulk/common.ts +6 -9
  40. package/src/change_stream.ts +21 -33
  41. package/src/cmap/connect.ts +33 -34
  42. package/src/cmap/connection.ts +1 -1
  43. package/src/cmap/connection_pool.ts +104 -142
  44. package/src/collection.ts +3 -0
  45. package/src/encrypter.ts +6 -11
  46. package/src/error.ts +48 -25
  47. package/src/index.ts +1 -0
  48. package/src/mongo_client.ts +26 -32
  49. package/src/operations/operation.ts +5 -7
  50. package/src/sdam/monitor.ts +30 -38
  51. package/src/sdam/server.ts +2 -2
  52. package/src/sdam/server_description.ts +3 -0
  53. package/src/sdam/topology.ts +15 -19
  54. package/src/sdam/topology_description.ts +13 -4
  55. package/src/sessions.ts +37 -58
package/src/error.ts CHANGED
@@ -1,18 +1,15 @@
1
- import type { Document } from './bson';
1
+ import type { Document, ObjectId } from './bson';
2
2
  import {
3
3
  type ClientBulkWriteError,
4
4
  type ClientBulkWriteResult
5
5
  } from './operations/client_bulk_write/common';
6
6
  import type { ServerType } from './sdam/common';
7
- import type { TopologyVersion } from './sdam/server_description';
7
+ import type { ServerDescription, TopologyVersion } from './sdam/server_description';
8
8
  import type { TopologyDescription } from './sdam/topology_description';
9
9
 
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
- [kErrorLabels]: Set<string>;
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[kErrorLabels].has(label);
191
+ return this.errorLabelSet.has(label);
192
192
  }
193
193
 
194
194
  addErrorLabel(label: string): void {
195
- this[kErrorLabels].add(label);
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
- this[kErrorLabels] = new Set(message.errorLabels);
229
+ for (const label of message.errorLabels) this.addErrorLabel(label);
233
230
  }
234
231
 
235
232
  this.errorResponse = message;
@@ -340,6 +337,41 @@ export class MongoRuntimeError extends MongoDriverError {
340
337
  }
341
338
  }
342
339
 
340
+ /**
341
+ * An error generated when a primary server is marked stale, never directly thrown
342
+ *
343
+ * @public
344
+ * @category Error
345
+ */
346
+ export class MongoStalePrimaryError extends MongoRuntimeError {
347
+ /**
348
+ * **Do not use this constructor!**
349
+ *
350
+ * Meant for internal use only.
351
+ *
352
+ * @remarks
353
+ * This class is only meant to be constructed within the driver. This constructor is
354
+ * not subject to semantic versioning compatibility guarantees and may change at any time.
355
+ *
356
+ * @public
357
+ **/
358
+ constructor(
359
+ serverDescription: ServerDescription,
360
+ maxSetVersion: number | null,
361
+ maxElectionId: ObjectId | null,
362
+ options?: { cause?: Error }
363
+ ) {
364
+ super(
365
+ `primary marked stale due to electionId/setVersion mismatch: server setVersion: ${serverDescription.setVersion}, server electionId: ${serverDescription.electionId}, topology setVersion: ${maxSetVersion}, topology electionId: ${maxElectionId}`,
366
+ options
367
+ );
368
+ }
369
+
370
+ override get name(): string {
371
+ return 'MongoStalePrimaryError';
372
+ }
373
+ }
374
+
343
375
  /**
344
376
  * An error generated when a batch command is re-executed after one of the commands in the batch
345
377
  * has failed
@@ -993,12 +1025,6 @@ export class MongoTopologyClosedError extends MongoAPIError {
993
1025
  }
994
1026
  }
995
1027
 
996
- /** @internal */
997
- const kBeforeHandshake = Symbol('beforeHandshake');
998
- export function isNetworkErrorBeforeHandshake(err: MongoNetworkError): boolean {
999
- return err[kBeforeHandshake] === true;
1000
- }
1001
-
1002
1028
  /** @public */
1003
1029
  export interface MongoNetworkErrorOptions {
1004
1030
  /** Indicates the timeout happened before a connection handshake completed */
@@ -1013,7 +1039,7 @@ export interface MongoNetworkErrorOptions {
1013
1039
  */
1014
1040
  export class MongoNetworkError extends MongoError {
1015
1041
  /** @internal */
1016
- [kBeforeHandshake]?: boolean;
1042
+ public readonly beforeHandshake: boolean;
1017
1043
 
1018
1044
  /**
1019
1045
  * **Do not use this constructor!**
@@ -1028,10 +1054,7 @@ export class MongoNetworkError extends MongoError {
1028
1054
  **/
1029
1055
  constructor(message: string, options?: MongoNetworkErrorOptions) {
1030
1056
  super(message, { cause: options?.cause });
1031
-
1032
- if (options && typeof options.beforeHandshake === 'boolean') {
1033
- this[kBeforeHandshake] = options.beforeHandshake;
1034
- }
1057
+ this.beforeHandshake = !!options?.beforeHandshake;
1035
1058
  }
1036
1059
 
1037
1060
  override get name(): string {
package/src/index.ts CHANGED
@@ -77,6 +77,7 @@ export {
77
77
  MongoServerClosedError,
78
78
  MongoServerError,
79
79
  MongoServerSelectionError,
80
+ MongoStalePrimaryError,
80
81
  MongoSystemError,
81
82
  MongoTailableCursorError,
82
83
  MongoTopologyClosedError,
@@ -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
- [kOptions]: MongoOptions;
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[kOptions] = parseOptions(url, this, options);
376
+ this.options = parseOptions(url, this, options);
378
377
 
379
- const shouldSetLogger = Object.values(
380
- this[kOptions].mongoLoggerOptions.componentSeverities
381
- ).some(value => value !== SeverityLevel.OFF);
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[kOptions].mongoLoggerOptions)
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[kOptions]),
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[kOptions];
399
+ return client.options;
401
400
  },
402
401
  get readConcern() {
403
- return client[kOptions].readConcern;
402
+ return client.options.readConcern;
404
403
  },
405
404
  get writeConcern() {
406
- return client[kOptions].writeConcern;
405
+ return client.options.writeConcern;
407
406
  },
408
407
  get readPreference() {
409
- return client[kOptions].readPreference;
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[kOptions].hosts.filter((hostAddress: HostAddress) =>
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[kOptions].srvHost);
433
+ const srvHostIsDocumentDB = isHostMatch(DOCUMENT_DB_CHECK, this.options.srvHost);
435
434
 
436
- const cosmosDBHostnames = this[kOptions].hosts.filter((hostAddress: HostAddress) =>
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[kOptions].srvHost);
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[kOptions].serverApi && Object.freeze({ ...this[kOptions].serverApi });
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[kOptions].monitorCommands;
455
+ return this.options.monitorCommands;
462
456
  }
463
457
  set monitorCommands(value: boolean) {
464
- this[kOptions].monitorCommands = value;
458
+ this.options.monitorCommands = value;
465
459
  }
466
460
 
467
461
  /** @internal */
468
462
  get autoEncrypter(): AutoEncrypter | undefined {
469
- return this[kOptions].autoEncrypter;
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[kOptions];
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[kOptions];
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[kOptions], options);
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[kOptions]
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
- [kSession]: ClientSession | undefined;
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[kSession] = options.session != null ? options.session : undefined;
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[kSession];
107
+ return this._session;
110
108
  }
111
109
 
112
110
  clearSession() {
113
- this[kSession] = undefined;
111
+ this._session = undefined;
114
112
  }
115
113
 
116
114
  resetBatch(): boolean {
@@ -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
- [kServer]: Server;
92
+ server: Server;
100
93
  connection: Connection | null;
101
- [kCancellationToken]: CancellationToken;
94
+ cancellationToken: CancellationToken;
102
95
  /** @internal */
103
- [kMonitorId]?: MonitorInterval;
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[kServer] = server;
106
+ this.server = server;
114
107
  this.connection = null;
115
- this[kCancellationToken] = new CancellationToken();
116
- this[kCancellationToken].setMaxListeners(Infinity);
117
- this[kMonitorId] = undefined;
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[kServer].topology.client?.mongoLogger;
122
+ this.mongoLogger = this.server.topology.client?.mongoLogger;
130
123
  this.rttSampler = new RTTSampler(10);
131
124
 
132
- const cancellationToken = this[kCancellationToken];
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[kMonitorId] = new MonitorInterval(monitorServer(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[kMonitorId]?.wake();
170
+ this.monitorId?.wake();
178
171
  }
179
172
 
180
173
  reset(): void {
181
- const topologyVersion = this[kServer].description.topologyVersion;
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[kMonitorId] = new MonitorInterval(monitorServer(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[kMonitorId]?.stop();
237
- monitor[kMonitorId] = undefined;
229
+ monitor.monitorId?.stop();
230
+ monitor.monitorId = undefined;
238
231
 
239
232
  monitor.rttPinger?.close();
240
233
  monitor.rttPinger = undefined;
241
234
 
242
- monitor[kCancellationToken].emit('cancel');
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[kServer].description.topologyVersion;
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[kServer].topology.s.id,
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[kServer].topology.s.id,
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[kServer].topology.s.id,
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[kServer].topology.s.id,
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[kServer].topology.s.id,
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[kServer].description.type === ServerType.Unknown) {
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[kMonitorId]?.wake();
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
- [kCancellationToken]: CancellationToken;
479
+ cancellationToken: CancellationToken;
488
480
  /** @internal */
489
- [kMonitorId]: NodeJS.Timeout;
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[kCancellationToken] = monitor[kCancellationToken];
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[kMonitorId] = setTimeout(() => this.measureRoundTripTime(), heartbeatFrequencyMS);
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[kMonitorId]);
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[kMonitorId] = setTimeout(
526
+ this.monitorId = setTimeout(
535
527
  () => this.measureRoundTripTime(),
536
528
  this.monitor.options.heartbeatFrequencyMS
537
529
  );
@@ -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 = isNetworkErrorBeforeHandshake(error);
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
@@ -112,7 +112,10 @@ export class ServerDescription {
112
112
  this.minRoundTripTime = options?.minRoundTripTime ?? 0;
113
113
  this.lastUpdateTime = now();
114
114
  this.lastWriteDate = hello?.lastWrite?.lastWriteDate ?? 0;
115
+ // NOTE: This actually builds the stack string instead of holding onto the getter and all its
116
+ // associated references. This is done to prevent a memory leak.
115
117
  this.error = options.error ?? null;
118
+ this.error?.stack;
116
119
  // TODO(NODE-2674): Preserve int64 sent from MongoDB
117
120
  this.topologyVersion = this.error?.topologyVersion ?? hello?.topologyVersion ?? null;
118
121
  this.setName = hello?.setName ?? null;