mongodb 4.7.0 → 4.9.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.
Files changed (127) hide show
  1. package/lib/bson.js +4 -2
  2. package/lib/bson.js.map +1 -1
  3. package/lib/bulk/common.js +1 -0
  4. package/lib/bulk/common.js.map +1 -1
  5. package/lib/change_stream.js +136 -271
  6. package/lib/change_stream.js.map +1 -1
  7. package/lib/cmap/command_monitoring_events.js +2 -32
  8. package/lib/cmap/command_monitoring_events.js.map +1 -1
  9. package/lib/cmap/commands.js +1 -153
  10. package/lib/cmap/commands.js.map +1 -1
  11. package/lib/cmap/connect.js +3 -6
  12. package/lib/cmap/connect.js.map +1 -1
  13. package/lib/cmap/connection.js +21 -84
  14. package/lib/cmap/connection.js.map +1 -1
  15. package/lib/cmap/connection_pool.js +196 -170
  16. package/lib/cmap/connection_pool.js.map +1 -1
  17. package/lib/cmap/message_stream.js.map +1 -1
  18. package/lib/cmap/wire_protocol/compression.js +2 -6
  19. package/lib/cmap/wire_protocol/compression.js.map +1 -1
  20. package/lib/cmap/wire_protocol/constants.js +1 -3
  21. package/lib/cmap/wire_protocol/constants.js.map +1 -1
  22. package/lib/collection.js +24 -9
  23. package/lib/collection.js.map +1 -1
  24. package/lib/connection_string.js.map +1 -1
  25. package/lib/cursor/abstract_cursor.js +62 -75
  26. package/lib/cursor/abstract_cursor.js.map +1 -1
  27. package/lib/cursor/change_stream_cursor.js +115 -0
  28. package/lib/cursor/change_stream_cursor.js.map +1 -0
  29. package/lib/cursor/list_collections_cursor.js +37 -0
  30. package/lib/cursor/list_collections_cursor.js.map +1 -0
  31. package/lib/cursor/list_indexes_cursor.js +36 -0
  32. package/lib/cursor/list_indexes_cursor.js.map +1 -0
  33. package/lib/db.js +2 -2
  34. package/lib/db.js.map +1 -1
  35. package/lib/deps.js.map +1 -1
  36. package/lib/encrypter.js +3 -13
  37. package/lib/encrypter.js.map +1 -1
  38. package/lib/index.js +28 -21
  39. package/lib/index.js.map +1 -1
  40. package/lib/mongo_client.js +62 -21
  41. package/lib/mongo_client.js.map +1 -1
  42. package/lib/mongo_types.js.map +1 -1
  43. package/lib/operations/common_functions.js.map +1 -1
  44. package/lib/operations/create_collection.js.map +1 -1
  45. package/lib/operations/distinct.js +5 -5
  46. package/lib/operations/distinct.js.map +1 -1
  47. package/lib/operations/estimated_document_count.js +5 -0
  48. package/lib/operations/estimated_document_count.js.map +1 -1
  49. package/lib/operations/execute_operation.js +17 -8
  50. package/lib/operations/execute_operation.js.map +1 -1
  51. package/lib/operations/find.js +3 -0
  52. package/lib/operations/find.js.map +1 -1
  53. package/lib/operations/get_more.js +32 -7
  54. package/lib/operations/get_more.js.map +1 -1
  55. package/lib/operations/indexes.js +39 -65
  56. package/lib/operations/indexes.js.map +1 -1
  57. package/lib/operations/kill_cursors.js +32 -0
  58. package/lib/operations/kill_cursors.js.map +1 -0
  59. package/lib/operations/list_collections.js +1 -33
  60. package/lib/operations/list_collections.js.map +1 -1
  61. package/lib/operations/operation.js +4 -1
  62. package/lib/operations/operation.js.map +1 -1
  63. package/lib/read_preference.js.map +1 -1
  64. package/lib/sdam/common.js +2 -1
  65. package/lib/sdam/common.js.map +1 -1
  66. package/lib/sdam/monitor.js +2 -1
  67. package/lib/sdam/monitor.js.map +1 -1
  68. package/lib/sdam/server.js +1 -52
  69. package/lib/sdam/server.js.map +1 -1
  70. package/lib/sdam/server_description.js +51 -58
  71. package/lib/sdam/server_description.js.map +1 -1
  72. package/lib/sdam/srv_polling.js +2 -2
  73. package/lib/sdam/srv_polling.js.map +1 -1
  74. package/lib/sdam/topology.js +28 -67
  75. package/lib/sdam/topology.js.map +1 -1
  76. package/lib/sdam/topology_description.js +24 -42
  77. package/lib/sdam/topology_description.js.map +1 -1
  78. package/lib/sessions.js +29 -31
  79. package/lib/sessions.js.map +1 -1
  80. package/lib/utils.js +65 -70
  81. package/lib/utils.js.map +1 -1
  82. package/mongodb.d.ts +136 -73
  83. package/package.json +23 -22
  84. package/src/bson.ts +4 -0
  85. package/src/bulk/common.ts +1 -0
  86. package/src/change_stream.ts +147 -373
  87. package/src/cmap/command_monitoring_events.ts +3 -37
  88. package/src/cmap/commands.ts +2 -190
  89. package/src/cmap/connect.ts +20 -25
  90. package/src/cmap/connection.ts +27 -139
  91. package/src/cmap/connection_pool.ts +208 -169
  92. package/src/cmap/message_stream.ts +2 -3
  93. package/src/cmap/wire_protocol/compression.ts +8 -6
  94. package/src/cmap/wire_protocol/constants.ts +0 -2
  95. package/src/collection.ts +27 -13
  96. package/src/connection_string.ts +1 -1
  97. package/src/cursor/abstract_cursor.ts +98 -87
  98. package/src/cursor/change_stream_cursor.ts +194 -0
  99. package/src/cursor/list_collections_cursor.ts +52 -0
  100. package/src/cursor/list_indexes_cursor.ts +41 -0
  101. package/src/db.ts +2 -5
  102. package/src/deps.ts +13 -22
  103. package/src/encrypter.ts +4 -14
  104. package/src/index.ts +13 -9
  105. package/src/mongo_client.ts +102 -33
  106. package/src/mongo_types.ts +81 -57
  107. package/src/operations/common_functions.ts +1 -1
  108. package/src/operations/create_collection.ts +1 -2
  109. package/src/operations/distinct.ts +7 -9
  110. package/src/operations/estimated_document_count.ts +6 -0
  111. package/src/operations/execute_operation.ts +17 -8
  112. package/src/operations/find.ts +9 -0
  113. package/src/operations/get_more.ts +56 -13
  114. package/src/operations/indexes.ts +52 -89
  115. package/src/operations/kill_cursors.ts +53 -0
  116. package/src/operations/list_collections.ts +0 -43
  117. package/src/operations/operation.ts +5 -1
  118. package/src/read_preference.ts +5 -9
  119. package/src/sdam/common.ts +2 -0
  120. package/src/sdam/monitor.ts +2 -1
  121. package/src/sdam/server.ts +4 -89
  122. package/src/sdam/server_description.ts +70 -80
  123. package/src/sdam/srv_polling.ts +1 -1
  124. package/src/sdam/topology.ts +37 -103
  125. package/src/sdam/topology_description.ts +44 -68
  126. package/src/sessions.ts +32 -39
  127. package/src/utils.ts +78 -75
@@ -1,5 +1,6 @@
1
1
  import Denque = require('denque');
2
- import { setTimeout } from 'timers';
2
+ import { clearTimeout, setTimeout } from 'timers';
3
+ import { promisify } from 'util';
3
4
 
4
5
  import type { BSONSerializeOptions, Document } from '../bson';
5
6
  import { deserialize, serialize } from '../bson';
@@ -29,20 +30,14 @@ import {
29
30
  MongoServerSelectionError,
30
31
  MongoTopologyClosedError
31
32
  } from '../error';
32
- import type { MongoClient, MongoOptions, ServerApi } from '../mongo_client';
33
+ import type { MongoClient, ServerApi } from '../mongo_client';
33
34
  import { TypedEventEmitter } from '../mongo_types';
34
35
  import { ReadPreference, ReadPreferenceLike } from '../read_preference';
35
- import {
36
- ClientSession,
37
- ClientSessionOptions,
38
- ServerSessionId,
39
- ServerSessionPool
40
- } from '../sessions';
36
+ import type { ClientSession } from '../sessions';
41
37
  import type { Transaction } from '../transactions';
42
38
  import {
43
39
  Callback,
44
40
  ClientMetadata,
45
- eachAsync,
46
41
  emitWarning,
47
42
  EventEmitterWithState,
48
43
  HostAddress,
@@ -120,10 +115,6 @@ export interface TopologyPrivate {
120
115
  minHeartbeatFrequencyMS: number;
121
116
  /** A map of server instances to normalized addresses */
122
117
  servers: Map<string, Server>;
123
- /** Server Session Pool */
124
- sessionPool: ServerSessionPool;
125
- /** Active client sessions */
126
- sessions: Set<ClientSession>;
127
118
  credentials?: MongoCredentials;
128
119
  clusterTime?: ClusterTime;
129
120
  /** timers created for the initial connect to a server */
@@ -316,10 +307,6 @@ export class Topology extends TypedEventEmitter<TopologyEvents> {
316
307
  minHeartbeatFrequencyMS: options.minHeartbeatFrequencyMS,
317
308
  // a map of server instances to normalized addresses
318
309
  servers: new Map(),
319
- // Server Session Pool
320
- sessionPool: new ServerSessionPool(this),
321
- // Active client sessions
322
- sessions: new Set(),
323
310
  credentials: options?.credentials,
324
311
  clusterTime: undefined,
325
312
 
@@ -443,13 +430,13 @@ export class Topology extends TypedEventEmitter<TopologyEvents> {
443
430
  }
444
431
  }
445
432
 
433
+ const exitWithError = (error: Error) =>
434
+ callback ? callback(error) : this.emit(Topology.ERROR, error);
435
+
446
436
  const readPreference = options.readPreference ?? ReadPreference.primary;
447
437
  this.selectServer(readPreferenceServerSelector(readPreference), options, (err, server) => {
448
438
  if (err) {
449
- this.close();
450
-
451
- typeof callback === 'function' ? callback(err) : this.emit(Topology.ERROR, err);
452
- return;
439
+ return this.close({ force: false }, () => exitWithError(err));
453
440
  }
454
441
 
455
442
  // TODO: NODE-2471
@@ -457,15 +444,14 @@ export class Topology extends TypedEventEmitter<TopologyEvents> {
457
444
  if (!skipPingOnConnect && server && this.s.credentials) {
458
445
  server.command(ns('admin.$cmd'), { ping: 1 }, {}, err => {
459
446
  if (err) {
460
- typeof callback === 'function' ? callback(err) : this.emit(Topology.ERROR, err);
461
- return;
447
+ return exitWithError(err);
462
448
  }
463
449
 
464
450
  stateTransition(this, STATE_CONNECTED);
465
451
  this.emit(Topology.OPEN, this);
466
452
  this.emit(Topology.CONNECT, this);
467
453
 
468
- if (typeof callback === 'function') callback(undefined, this);
454
+ callback?.(undefined, this);
469
455
  });
470
456
 
471
457
  return;
@@ -475,7 +461,7 @@ export class Topology extends TypedEventEmitter<TopologyEvents> {
475
461
  this.emit(Topology.OPEN, this);
476
462
  this.emit(Topology.CONNECT, this);
477
463
 
478
- if (typeof callback === 'function') callback(undefined, this);
464
+ callback?.(undefined, this);
479
465
  });
480
466
  }
481
467
 
@@ -489,52 +475,38 @@ export class Topology extends TypedEventEmitter<TopologyEvents> {
489
475
  if (typeof options === 'boolean') {
490
476
  options = { force: options };
491
477
  }
492
-
493
478
  options = options ?? {};
494
- if (this.s.state === STATE_CLOSED || this.s.state === STATE_CLOSING) {
495
- if (typeof callback === 'function') {
496
- callback();
497
- }
498
479
 
499
- return;
480
+ if (this.s.state === STATE_CLOSED || this.s.state === STATE_CLOSING) {
481
+ return callback?.();
500
482
  }
501
483
 
502
- stateTransition(this, STATE_CLOSING);
484
+ const destroyedServers = Array.from(this.s.servers.values(), server => {
485
+ return promisify(destroyServer)(server, this, options);
486
+ });
503
487
 
504
- drainWaitQueue(this[kWaitQueue], new MongoTopologyClosedError());
505
- drainTimerQueue(this.s.connectionTimers);
488
+ Promise.all(destroyedServers)
489
+ .then(() => {
490
+ this.s.servers.clear();
506
491
 
507
- if (this.s.srvPoller) {
508
- this.s.srvPoller.stop();
509
- this.s.srvPoller.removeListener(SrvPoller.SRV_RECORD_DISCOVERY, this.s.detectSrvRecords);
510
- }
492
+ stateTransition(this, STATE_CLOSING);
511
493
 
512
- this.removeListener(Topology.TOPOLOGY_DESCRIPTION_CHANGED, this.s.detectShardedTopology);
513
-
514
- eachAsync(
515
- Array.from(this.s.sessions.values()),
516
- (session, cb) => session.endSession(cb),
517
- () => {
518
- this.s.sessionPool.endAllPooledSessions(() => {
519
- eachAsync(
520
- Array.from(this.s.servers.values()),
521
- (server, cb) => destroyServer(server, this, options, cb),
522
- err => {
523
- this.s.servers.clear();
524
-
525
- // emit an event for close
526
- this.emit(Topology.TOPOLOGY_CLOSED, new TopologyClosedEvent(this.s.id));
527
-
528
- stateTransition(this, STATE_CLOSED);
529
-
530
- if (typeof callback === 'function') {
531
- callback(err);
532
- }
533
- }
534
- );
535
- });
536
- }
537
- );
494
+ drainWaitQueue(this[kWaitQueue], new MongoTopologyClosedError());
495
+ drainTimerQueue(this.s.connectionTimers);
496
+
497
+ if (this.s.srvPoller) {
498
+ this.s.srvPoller.stop();
499
+ this.s.srvPoller.removeListener(SrvPoller.SRV_RECORD_DISCOVERY, this.s.detectSrvRecords);
500
+ }
501
+
502
+ this.removeListener(Topology.TOPOLOGY_DESCRIPTION_CHANGED, this.s.detectShardedTopology);
503
+
504
+ stateTransition(this, STATE_CLOSED);
505
+
506
+ // emit an event for close
507
+ this.emit(Topology.TOPOLOGY_CLOSED, new TopologyClosedEvent(this.s.id));
508
+ })
509
+ .finally(() => callback?.());
538
510
  }
539
511
 
540
512
  /**
@@ -628,44 +600,6 @@ export class Topology extends TypedEventEmitter<TopologyEvents> {
628
600
  return this.loadBalanced || this.description.logicalSessionTimeoutMinutes != null;
629
601
  }
630
602
 
631
- /** Start a logical session */
632
- startSession(options: ClientSessionOptions, clientOptions?: MongoOptions): ClientSession {
633
- const session = new ClientSession(this.client, this.s.sessionPool, options, clientOptions);
634
- session.once('ended', () => {
635
- this.s.sessions.delete(session);
636
- });
637
-
638
- this.s.sessions.add(session);
639
- return session;
640
- }
641
-
642
- /** Send endSessions command(s) with the given session ids */
643
- endSessions(sessions: ServerSessionId[], callback?: Callback<Document>): void {
644
- if (!Array.isArray(sessions)) {
645
- sessions = [sessions];
646
- }
647
-
648
- this.selectServer(
649
- readPreferenceServerSelector(ReadPreference.primaryPreferred),
650
- {},
651
- (err, server) => {
652
- if (err || !server) {
653
- if (typeof callback === 'function') callback(err);
654
- return;
655
- }
656
-
657
- server.command(
658
- ns('admin.$cmd'),
659
- { endSessions: sessions },
660
- { noResponse: true },
661
- (err, result) => {
662
- if (typeof callback === 'function') callback(err, result);
663
- }
664
- );
665
- }
666
- );
667
- }
668
-
669
603
  /**
670
604
  * Update the internal TopologyDescription with a ServerDescription
671
605
  *
@@ -790,7 +724,7 @@ export class Topology extends TypedEventEmitter<TopologyEvents> {
790
724
  return this.description.commonWireVersion;
791
725
  }
792
726
 
793
- get logicalSessionTimeoutMinutes(): number | undefined {
727
+ get logicalSessionTimeoutMinutes(): number | null {
794
728
  return this.description.logicalSessionTimeoutMinutes;
795
729
  }
796
730
 
@@ -1,7 +1,7 @@
1
- import type { Document, ObjectId } from '../bson';
1
+ import type { ObjectId } from '../bson';
2
2
  import * as WIRE_CONSTANTS from '../cmap/wire_protocol/constants';
3
- import { MongoError, MongoRuntimeError } from '../error';
4
- import { shuffle } from '../utils';
3
+ import { MongoRuntimeError, MongoServerError } from '../error';
4
+ import { compareObjectId, shuffle } from '../utils';
5
5
  import { ServerType, TopologyType } from './common';
6
6
  import { ServerDescription } from './server_description';
7
7
  import type { SrvPollingEvent } from './srv_polling';
@@ -32,29 +32,29 @@ export interface TopologyDescriptionOptions {
32
32
  */
33
33
  export class TopologyDescription {
34
34
  type: TopologyType;
35
- setName?: string;
36
- maxSetVersion?: number;
37
- maxElectionId?: ObjectId;
35
+ setName: string | null;
36
+ maxSetVersion: number | null;
37
+ maxElectionId: ObjectId | null;
38
38
  servers: Map<string, ServerDescription>;
39
39
  stale: boolean;
40
40
  compatible: boolean;
41
41
  compatibilityError?: string;
42
- logicalSessionTimeoutMinutes?: number;
42
+ logicalSessionTimeoutMinutes: number | null;
43
43
  heartbeatFrequencyMS: number;
44
44
  localThresholdMS: number;
45
- commonWireVersion?: number;
45
+ commonWireVersion: number;
46
46
 
47
47
  /**
48
48
  * Create a TopologyDescription
49
49
  */
50
50
  constructor(
51
51
  topologyType: TopologyType,
52
- serverDescriptions?: Map<string, ServerDescription>,
53
- setName?: string,
54
- maxSetVersion?: number,
55
- maxElectionId?: ObjectId,
56
- commonWireVersion?: number,
57
- options?: TopologyDescriptionOptions
52
+ serverDescriptions: Map<string, ServerDescription> | null = null,
53
+ setName: string | null = null,
54
+ maxSetVersion: number | null = null,
55
+ maxElectionId: ObjectId | null = null,
56
+ commonWireVersion: number | null = null,
57
+ options: TopologyDescriptionOptions | null = null
58
58
  ) {
59
59
  options = options ?? {};
60
60
 
@@ -64,22 +64,10 @@ export class TopologyDescription {
64
64
  this.compatible = true;
65
65
  this.heartbeatFrequencyMS = options.heartbeatFrequencyMS ?? 0;
66
66
  this.localThresholdMS = options.localThresholdMS ?? 15;
67
-
68
- if (setName) {
69
- this.setName = setName;
70
- }
71
-
72
- if (maxSetVersion) {
73
- this.maxSetVersion = maxSetVersion;
74
- }
75
-
76
- if (maxElectionId) {
77
- this.maxElectionId = maxElectionId;
78
- }
79
-
80
- if (commonWireVersion) {
81
- this.commonWireVersion = commonWireVersion;
82
- }
67
+ this.setName = setName ?? null;
68
+ this.maxElectionId = maxElectionId ?? null;
69
+ this.maxSetVersion = maxSetVersion ?? null;
70
+ this.commonWireVersion = commonWireVersion ?? 0;
83
71
 
84
72
  // determine server compatibility
85
73
  for (const serverDescription of this.servers.values()) {
@@ -108,12 +96,12 @@ export class TopologyDescription {
108
96
  // value among ServerDescriptions of all data-bearing server types. If any have a null
109
97
  // logicalSessionTimeoutMinutes, then TopologyDescription.logicalSessionTimeoutMinutes MUST be
110
98
  // set to null.
111
- this.logicalSessionTimeoutMinutes = undefined;
99
+ this.logicalSessionTimeoutMinutes = null;
112
100
  for (const [, server] of this.servers) {
113
101
  if (server.isReadable) {
114
102
  if (server.logicalSessionTimeoutMinutes == null) {
115
103
  // If any of the servers have a null logicalSessionsTimeout, then the whole topology does
116
- this.logicalSessionTimeoutMinutes = undefined;
104
+ this.logicalSessionTimeoutMinutes = null;
117
105
  break;
118
106
  }
119
107
 
@@ -200,11 +188,6 @@ export class TopologyDescription {
200
188
  // potentially mutated values
201
189
  let { type: topologyType, setName, maxSetVersion, maxElectionId, commonWireVersion } = this;
202
190
 
203
- if (serverDescription.setName && setName && serverDescription.setName !== setName) {
204
- // TODO(NODE-4159): servers with an incorrect setName should be removed not marked Unknown
205
- serverDescription = new ServerDescription(address, undefined);
206
- }
207
-
208
191
  const serverType = serverDescription.type;
209
192
  const serverDescriptions = new Map(this.servers);
210
193
 
@@ -217,6 +200,19 @@ export class TopologyDescription {
217
200
  }
218
201
  }
219
202
 
203
+ if (
204
+ typeof serverDescription.setName === 'string' &&
205
+ typeof setName === 'string' &&
206
+ serverDescription.setName !== setName
207
+ ) {
208
+ if (topologyType === TopologyType.Single) {
209
+ // "Single" Topology with setName mismatch is direct connection usage, mark unknown do not remove
210
+ serverDescription = new ServerDescription(address);
211
+ } else {
212
+ serverDescriptions.delete(address);
213
+ }
214
+ }
215
+
220
216
  // update the actual server description
221
217
  serverDescriptions.set(address, serverDescription);
222
218
 
@@ -311,7 +307,7 @@ export class TopologyDescription {
311
307
  );
312
308
  }
313
309
 
314
- get error(): MongoError | undefined {
310
+ get error(): MongoServerError | null {
315
311
  const descriptionsWithError = Array.from(this.servers.values()).filter(
316
312
  (sd: ServerDescription) => sd.error
317
313
  );
@@ -319,7 +315,8 @@ export class TopologyDescription {
319
315
  if (descriptionsWithError.length > 0) {
320
316
  return descriptionsWithError[0].error;
321
317
  }
322
- return;
318
+
319
+ return null;
323
320
  }
324
321
 
325
322
  /**
@@ -363,34 +360,13 @@ function topologyTypeForServerType(serverType: ServerType): TopologyType {
363
360
  }
364
361
  }
365
362
 
366
- // TODO: improve these docs when ObjectId is properly typed
367
- function compareObjectId(oid1: Document, oid2: Document): number {
368
- if (oid1 == null) {
369
- return -1;
370
- }
371
-
372
- if (oid2 == null) {
373
- return 1;
374
- }
375
-
376
- if (oid1.id instanceof Buffer && oid2.id instanceof Buffer) {
377
- const oid1Buffer = oid1.id;
378
- const oid2Buffer = oid2.id;
379
- return oid1Buffer.compare(oid2Buffer);
380
- }
381
-
382
- const oid1String = oid1.toString();
383
- const oid2String = oid2.toString();
384
- return oid1String.localeCompare(oid2String);
385
- }
386
-
387
363
  function updateRsFromPrimary(
388
364
  serverDescriptions: Map<string, ServerDescription>,
389
365
  serverDescription: ServerDescription,
390
- setName?: string,
391
- maxSetVersion?: number,
392
- maxElectionId?: ObjectId
393
- ): [TopologyType, string?, number?, ObjectId?] {
366
+ setName: string | null = null,
367
+ maxSetVersion: number | null = null,
368
+ maxElectionId: ObjectId | null = null
369
+ ): [TopologyType, string | null, number | null, ObjectId | null] {
394
370
  setName = setName || serverDescription.setName;
395
371
  if (setName !== serverDescription.setName) {
396
372
  serverDescriptions.delete(serverDescription.address);
@@ -457,7 +433,7 @@ function updateRsFromPrimary(
457
433
  function updateRsWithPrimaryFromMember(
458
434
  serverDescriptions: Map<string, ServerDescription>,
459
435
  serverDescription: ServerDescription,
460
- setName?: string
436
+ setName: string | null = null
461
437
  ): TopologyType {
462
438
  if (setName == null) {
463
439
  // TODO(NODE-3483): should be an appropriate runtime error
@@ -477,10 +453,10 @@ function updateRsWithPrimaryFromMember(
477
453
  function updateRsNoPrimaryFromMember(
478
454
  serverDescriptions: Map<string, ServerDescription>,
479
455
  serverDescription: ServerDescription,
480
- setName?: string
481
- ): [TopologyType, string?] {
456
+ setName: string | null = null
457
+ ): [TopologyType, string | null] {
482
458
  const topologyType = TopologyType.ReplicaSetNoPrimary;
483
- setName = setName || serverDescription.setName;
459
+ setName = setName ?? serverDescription.setName;
484
460
  if (setName !== serverDescription.setName) {
485
461
  serverDescriptions.delete(serverDescription.address);
486
462
  return [topologyType, setName];
package/src/sessions.ts CHANGED
@@ -27,7 +27,6 @@ import { PromiseProvider } from './promise_provider';
27
27
  import { ReadConcernLevel } from './read_concern';
28
28
  import { ReadPreference } from './read_preference';
29
29
  import { _advanceClusterTime, ClusterTime, TopologyType } from './sdam/common';
30
- import type { Topology } from './sdam/topology';
31
30
  import { isTransactionCommand, Transaction, TransactionOptions, TxnState } from './transactions';
32
31
  import {
33
32
  calculateDurationInMs,
@@ -269,9 +268,10 @@ export class ClientSession extends TypedEventEmitter<ClientSessionEvents> {
269
268
  if (serverSession != null) {
270
269
  // release the server session back to the pool
271
270
  this.sessionPool.release(serverSession);
272
- // Make sure a new serverSession never makes it on to the ClientSession
271
+ // Make sure a new serverSession never makes it onto this ClientSession
273
272
  Object.defineProperty(this, kServerSession, {
274
- value: ServerSession.clone(serverSession)
273
+ value: ServerSession.clone(serverSession),
274
+ writable: false
275
275
  });
276
276
  }
277
277
 
@@ -383,7 +383,7 @@ export class ClientSession extends TypedEventEmitter<ClientSessionEvents> {
383
383
  */
384
384
  startTransaction(options?: TransactionOptions): void {
385
385
  if (this[kSnapshotEnabled]) {
386
- throw new MongoCompatibilityError('Transactions are not allowed with snapshot sessions');
386
+ throw new MongoCompatibilityError('Transactions are not supported in snapshot sessions');
387
387
  }
388
388
 
389
389
  if (this.inTransaction()) {
@@ -616,7 +616,7 @@ function attemptTransaction<TSchema>(
616
616
  }
617
617
 
618
618
  if (!isPromiseLike(promise)) {
619
- session.abortTransaction();
619
+ session.abortTransaction().catch(() => null);
620
620
  throw new MongoInvalidArgumentError(
621
621
  'Function provided to `withTransaction` must return a Promise'
622
622
  );
@@ -878,39 +878,18 @@ export class ServerSession {
878
878
  * @internal
879
879
  */
880
880
  export class ServerSessionPool {
881
- topology: Topology;
881
+ client: MongoClient;
882
882
  sessions: ServerSession[];
883
883
 
884
- constructor(topology: Topology) {
885
- if (topology == null) {
886
- throw new MongoRuntimeError('ServerSessionPool requires a topology');
884
+ constructor(client: MongoClient) {
885
+ if (client == null) {
886
+ throw new MongoRuntimeError('ServerSessionPool requires a MongoClient');
887
887
  }
888
888
 
889
- this.topology = topology;
889
+ this.client = client;
890
890
  this.sessions = [];
891
891
  }
892
892
 
893
- /** Ends all sessions in the session pool */
894
- endAllPooledSessions(callback?: Callback<void>): void {
895
- if (this.sessions.length) {
896
- this.topology.endSessions(
897
- this.sessions.map((session: ServerSession) => session.id),
898
- () => {
899
- this.sessions = [];
900
- if (typeof callback === 'function') {
901
- callback();
902
- }
903
- }
904
- );
905
-
906
- return;
907
- }
908
-
909
- if (typeof callback === 'function') {
910
- callback();
911
- }
912
- }
913
-
914
893
  /**
915
894
  * Acquire a Server Session from the pool.
916
895
  * Iterates through each session in the pool, removing any stale sessions
@@ -918,16 +897,29 @@ export class ServerSessionPool {
918
897
  * pool and returned. If no non-stale session is found, a new ServerSession is created.
919
898
  */
920
899
  acquire(): ServerSession {
921
- const sessionTimeoutMinutes = this.topology.logicalSessionTimeoutMinutes || 10;
900
+ const sessionTimeoutMinutes = this.client.topology?.logicalSessionTimeoutMinutes ?? 10;
922
901
 
923
- while (this.sessions.length) {
924
- const session = this.sessions.shift();
925
- if (session && (this.topology.loadBalanced || !session.hasTimedOut(sessionTimeoutMinutes))) {
926
- return session;
902
+ let session: ServerSession | null = null;
903
+
904
+ // Try to obtain from session pool
905
+ while (this.sessions.length > 0) {
906
+ const potentialSession = this.sessions.shift();
907
+ if (
908
+ potentialSession != null &&
909
+ (!!this.client.topology?.loadBalanced ||
910
+ !potentialSession.hasTimedOut(sessionTimeoutMinutes))
911
+ ) {
912
+ session = potentialSession;
913
+ break;
927
914
  }
928
915
  }
929
916
 
930
- return new ServerSession();
917
+ // If nothing valid came from the pool make a new one
918
+ if (session == null) {
919
+ session = new ServerSession();
920
+ }
921
+
922
+ return session;
931
923
  }
932
924
 
933
925
  /**
@@ -938,9 +930,9 @@ export class ServerSessionPool {
938
930
  * @param session - The session to release to the pool
939
931
  */
940
932
  release(session: ServerSession): void {
941
- const sessionTimeoutMinutes = this.topology.logicalSessionTimeoutMinutes;
933
+ const sessionTimeoutMinutes = this.client.topology?.logicalSessionTimeoutMinutes ?? 10;
942
934
 
943
- if (this.topology.loadBalanced && !sessionTimeoutMinutes) {
935
+ if (this.client.topology?.loadBalanced && !sessionTimeoutMinutes) {
944
936
  this.sessions.unshift(session);
945
937
  }
946
938
 
@@ -1010,6 +1002,7 @@ export function applySession(
1010
1002
  if (isRetryableWrite || inTxnOrTxnCommand) {
1011
1003
  serverSession.txnNumber += session[kTxnNumberIncrement];
1012
1004
  session[kTxnNumberIncrement] = 0;
1005
+ // TODO(NODE-2674): Preserve int64 sent from MongoDB
1013
1006
  command.txnNumber = Long.fromNumber(serverSession.txnNumber);
1014
1007
  }
1015
1008