mongodb 4.7.0 → 4.8.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 (81) hide show
  1. package/lib/change_stream.js +136 -271
  2. package/lib/change_stream.js.map +1 -1
  3. package/lib/cmap/command_monitoring_events.js +2 -3
  4. package/lib/cmap/command_monitoring_events.js.map +1 -1
  5. package/lib/cmap/connect.js +3 -6
  6. package/lib/cmap/connect.js.map +1 -1
  7. package/lib/cmap/connection.js.map +1 -1
  8. package/lib/cmap/wire_protocol/compression.js +2 -6
  9. package/lib/cmap/wire_protocol/compression.js.map +1 -1
  10. package/lib/collection.js +18 -3
  11. package/lib/collection.js.map +1 -1
  12. package/lib/cursor/abstract_cursor.js +55 -62
  13. package/lib/cursor/abstract_cursor.js.map +1 -1
  14. package/lib/cursor/change_stream_cursor.js +115 -0
  15. package/lib/cursor/change_stream_cursor.js.map +1 -0
  16. package/lib/cursor/list_collections_cursor.js +37 -0
  17. package/lib/cursor/list_collections_cursor.js.map +1 -0
  18. package/lib/cursor/list_indexes_cursor.js +36 -0
  19. package/lib/cursor/list_indexes_cursor.js.map +1 -0
  20. package/lib/db.js +2 -2
  21. package/lib/db.js.map +1 -1
  22. package/lib/deps.js.map +1 -1
  23. package/lib/encrypter.js +5 -12
  24. package/lib/encrypter.js.map +1 -1
  25. package/lib/index.js +9 -7
  26. package/lib/index.js.map +1 -1
  27. package/lib/mongo_client.js +49 -21
  28. package/lib/mongo_client.js.map +1 -1
  29. package/lib/mongo_types.js.map +1 -1
  30. package/lib/operations/common_functions.js.map +1 -1
  31. package/lib/operations/estimated_document_count.js +5 -0
  32. package/lib/operations/estimated_document_count.js.map +1 -1
  33. package/lib/operations/execute_operation.js +17 -8
  34. package/lib/operations/execute_operation.js.map +1 -1
  35. package/lib/operations/get_more.js +1 -1
  36. package/lib/operations/get_more.js.map +1 -1
  37. package/lib/operations/indexes.js +1 -32
  38. package/lib/operations/indexes.js.map +1 -1
  39. package/lib/operations/kill_cursors.js +22 -0
  40. package/lib/operations/kill_cursors.js.map +1 -0
  41. package/lib/operations/list_collections.js +1 -33
  42. package/lib/operations/list_collections.js.map +1 -1
  43. package/lib/operations/operation.js +4 -1
  44. package/lib/operations/operation.js.map +1 -1
  45. package/lib/read_preference.js.map +1 -1
  46. package/lib/sdam/topology.js +25 -64
  47. package/lib/sdam/topology.js.map +1 -1
  48. package/lib/sessions.js +27 -30
  49. package/lib/sessions.js.map +1 -1
  50. package/lib/utils.js +25 -7
  51. package/lib/utils.js.map +1 -1
  52. package/mongodb.d.ts +60 -27
  53. package/package.json +18 -18
  54. package/src/change_stream.ts +147 -373
  55. package/src/cmap/command_monitoring_events.ts +2 -3
  56. package/src/cmap/connect.ts +20 -25
  57. package/src/cmap/connection.ts +1 -2
  58. package/src/cmap/wire_protocol/compression.ts +8 -6
  59. package/src/collection.ts +21 -7
  60. package/src/cursor/abstract_cursor.ts +66 -71
  61. package/src/cursor/change_stream_cursor.ts +194 -0
  62. package/src/cursor/list_collections_cursor.ts +52 -0
  63. package/src/cursor/list_indexes_cursor.ts +41 -0
  64. package/src/db.ts +2 -5
  65. package/src/deps.ts +13 -22
  66. package/src/encrypter.ts +5 -13
  67. package/src/index.ts +9 -5
  68. package/src/mongo_client.ts +68 -27
  69. package/src/mongo_types.ts +29 -3
  70. package/src/operations/common_functions.ts +1 -1
  71. package/src/operations/estimated_document_count.ts +6 -0
  72. package/src/operations/execute_operation.ts +17 -8
  73. package/src/operations/get_more.ts +2 -2
  74. package/src/operations/indexes.ts +0 -37
  75. package/src/operations/kill_cursors.ts +27 -0
  76. package/src/operations/list_collections.ts +0 -43
  77. package/src/operations/operation.ts +5 -1
  78. package/src/read_preference.ts +5 -9
  79. package/src/sdam/topology.ts +35 -101
  80. package/src/sessions.ts +30 -38
  81. package/src/utils.ts +32 -8
@@ -1,6 +1,5 @@
1
1
  import type { Document } from '../bson';
2
2
  import type { Collection } from '../collection';
3
- import { AbstractCursor } from '../cursor/abstract_cursor';
4
3
  import type { Db } from '../db';
5
4
  import { MongoCompatibilityError, MONGODB_ERROR_CODES, MongoServerError } from '../error';
6
5
  import type { OneOrMore } from '../mongo_types';
@@ -15,7 +14,6 @@ import {
15
14
  OperationParent
16
15
  } from './command';
17
16
  import { indexInformation, IndexInformationOptions } from './common_functions';
18
- import { executeOperation, ExecutionResult } from './execute_operation';
19
17
  import { AbstractOperation, Aspect, defineAspects } from './operation';
20
18
 
21
19
  const VALID_INDEX_OPTIONS = new Set([
@@ -412,41 +410,6 @@ export class ListIndexesOperation extends CommandOperation<Document> {
412
410
  }
413
411
  }
414
412
 
415
- /** @public */
416
- export class ListIndexesCursor extends AbstractCursor {
417
- parent: Collection;
418
- options?: ListIndexesOptions;
419
-
420
- constructor(collection: Collection, options?: ListIndexesOptions) {
421
- super(collection.s.db.s.client, collection.s.namespace, options);
422
- this.parent = collection;
423
- this.options = options;
424
- }
425
-
426
- clone(): ListIndexesCursor {
427
- return new ListIndexesCursor(this.parent, {
428
- ...this.options,
429
- ...this.cursorOptions
430
- });
431
- }
432
-
433
- /** @internal */
434
- _initialize(session: ClientSession | undefined, callback: Callback<ExecutionResult>): void {
435
- const operation = new ListIndexesOperation(this.parent, {
436
- ...this.cursorOptions,
437
- ...this.options,
438
- session
439
- });
440
-
441
- executeOperation(this.parent.s.db.s.client, operation, (err, response) => {
442
- if (err || response == null) return callback(err);
443
-
444
- // TODO: NODE-2882
445
- callback(undefined, { server: operation.server, session, response });
446
- });
447
- }
448
- }
449
-
450
413
  /** @internal */
451
414
  export class IndexExistsOperation extends AbstractOperation<boolean> {
452
415
  override options: IndexInformationOptions;
@@ -0,0 +1,27 @@
1
+ import type { Long } from '../bson';
2
+ import { MongoRuntimeError } from '../error';
3
+ import type { Server } from '../sdam/server';
4
+ import type { ClientSession } from '../sessions';
5
+ import type { Callback, MongoDBNamespace } from '../utils';
6
+ import { AbstractOperation, Aspect, defineAspects, OperationOptions } from './operation';
7
+
8
+ export class KillCursorsOperation extends AbstractOperation {
9
+ cursorId: Long;
10
+ constructor(cursorId: Long, ns: MongoDBNamespace, server: Server, options: OperationOptions) {
11
+ super(options);
12
+ this.ns = ns;
13
+ this.cursorId = cursorId;
14
+ this.server = server;
15
+ }
16
+
17
+ execute(server: Server, session: ClientSession | undefined, callback: Callback<void>): void {
18
+ if (server !== this.server) {
19
+ return callback(
20
+ new MongoRuntimeError('Killcursor must run on the same server operation began on')
21
+ );
22
+ }
23
+ server.killCursors(this.ns, [this.cursorId], { session }, () => callback());
24
+ }
25
+ }
26
+
27
+ defineAspects(KillCursorsOperation, [Aspect.MUST_SELECT_SAME_SERVER]);
@@ -1,11 +1,9 @@
1
1
  import type { Binary, Document } from '../bson';
2
- import { AbstractCursor } from '../cursor/abstract_cursor';
3
2
  import type { Db } from '../db';
4
3
  import type { Server } from '../sdam/server';
5
4
  import type { ClientSession } from '../sessions';
6
5
  import { Callback, maxWireVersion } from '../utils';
7
6
  import { CommandOperation, CommandOperationOptions } from './command';
8
- import { executeOperation, ExecutionResult } from './execute_operation';
9
7
  import { Aspect, defineAspects } from './operation';
10
8
 
11
9
  /** @public */
@@ -86,47 +84,6 @@ export interface CollectionInfo extends Document {
86
84
  idIndex?: Document;
87
85
  }
88
86
 
89
- /** @public */
90
- export class ListCollectionsCursor<
91
- T extends Pick<CollectionInfo, 'name' | 'type'> | CollectionInfo =
92
- | Pick<CollectionInfo, 'name' | 'type'>
93
- | CollectionInfo
94
- > extends AbstractCursor<T> {
95
- parent: Db;
96
- filter: Document;
97
- options?: ListCollectionsOptions;
98
-
99
- constructor(db: Db, filter: Document, options?: ListCollectionsOptions) {
100
- super(db.s.client, db.s.namespace, options);
101
- this.parent = db;
102
- this.filter = filter;
103
- this.options = options;
104
- }
105
-
106
- clone(): ListCollectionsCursor<T> {
107
- return new ListCollectionsCursor(this.parent, this.filter, {
108
- ...this.options,
109
- ...this.cursorOptions
110
- });
111
- }
112
-
113
- /** @internal */
114
- _initialize(session: ClientSession | undefined, callback: Callback<ExecutionResult>): void {
115
- const operation = new ListCollectionsOperation(this.parent, this.filter, {
116
- ...this.cursorOptions,
117
- ...this.options,
118
- session
119
- });
120
-
121
- executeOperation(this.parent.s.client, operation, (err, response) => {
122
- if (err || response == null) return callback(err);
123
-
124
- // TODO: NODE-2882
125
- callback(undefined, { server: operation.server, session, response });
126
- });
127
- }
128
- }
129
-
130
87
  defineAspects(ListCollectionsOperation, [
131
88
  Aspect.READ_OPERATION,
132
89
  Aspect.RETRYABLE,
@@ -11,7 +11,7 @@ export const Aspect = {
11
11
  EXPLAINABLE: Symbol('EXPLAINABLE'),
12
12
  SKIP_COLLATION: Symbol('SKIP_COLLATION'),
13
13
  CURSOR_CREATING: Symbol('CURSOR_CREATING'),
14
- CURSOR_ITERATING: Symbol('CURSOR_ITERATING')
14
+ MUST_SELECT_SAME_SERVER: Symbol('MUST_SELECT_SAME_SERVER')
15
15
  } as const;
16
16
 
17
17
  /** @public */
@@ -94,6 +94,10 @@ export abstract class AbstractOperation<TResult = any> {
94
94
  return this[kSession];
95
95
  }
96
96
 
97
+ clearSession() {
98
+ this[kSession] = undefined;
99
+ }
100
+
97
101
  get canRetryRead(): boolean {
98
102
  return true;
99
103
  }
@@ -163,14 +163,10 @@ export class ReadPreference {
163
163
  } else if (!(readPreference instanceof ReadPreference) && typeof readPreference === 'object') {
164
164
  const mode = readPreference.mode || readPreference.preference;
165
165
  if (mode && typeof mode === 'string') {
166
- return new ReadPreference(
167
- mode as ReadPreferenceMode,
168
- readPreference.tags ?? readPreferenceTags,
169
- {
170
- maxStalenessSeconds: readPreference.maxStalenessSeconds,
171
- hedge: options.hedge
172
- }
173
- );
166
+ return new ReadPreference(mode, readPreference.tags ?? readPreferenceTags, {
167
+ maxStalenessSeconds: readPreference.maxStalenessSeconds,
168
+ hedge: options.hedge
169
+ });
174
170
  }
175
171
  }
176
172
 
@@ -193,7 +189,7 @@ export class ReadPreference {
193
189
  } else if (r && !(r instanceof ReadPreference) && typeof r === 'object') {
194
190
  const mode = r.mode || r.preference;
195
191
  if (mode && typeof mode === 'string') {
196
- options.readPreference = new ReadPreference(mode as ReadPreferenceMode, r.tags, {
192
+ options.readPreference = new ReadPreference(mode, r.tags, {
197
193
  maxStalenessSeconds: r.maxStalenessSeconds
198
194
  });
199
195
  }
@@ -1,5 +1,6 @@
1
1
  import Denque = require('denque');
2
2
  import { 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
  *
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
 
@@ -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
 
package/src/utils.ts CHANGED
@@ -161,14 +161,15 @@ export function parseIndexOptions(indexSpec: IndexSpecification): IndexOptions {
161
161
  };
162
162
  }
163
163
 
164
+ const TO_STRING = (object: unknown) => Object.prototype.toString.call(object);
164
165
  /**
165
166
  * Checks if arg is an Object:
166
167
  * - **NOTE**: the check is based on the `[Symbol.toStringTag]() === 'Object'`
167
168
  * @internal
168
169
  */
169
- // eslint-disable-next-line @typescript-eslint/ban-types
170
+
170
171
  export function isObject(arg: unknown): arg is object {
171
- return '[object Object]' === Object.prototype.toString.call(arg);
172
+ return '[object Object]' === TO_STRING(arg);
172
173
  }
173
174
 
174
175
  /** @internal */
@@ -380,7 +381,7 @@ export interface DeprecateOptionsConfig {
380
381
  /** index of options object in function arguments array */
381
382
  optionsIndex: number;
382
383
  /** optional custom message handler to generate warnings */
383
- msgHandler?(name: string, option: string): string;
384
+ msgHandler?(this: void, name: string, option: string): string;
384
385
  }
385
386
 
386
387
  /**
@@ -1024,6 +1025,9 @@ export function setDifference<T>(setA: Iterable<T>, setB: Iterable<T>): Set<T> {
1024
1025
  return difference;
1025
1026
  }
1026
1027
 
1028
+ const HAS_OWN = (object: unknown, prop: string) =>
1029
+ Object.prototype.hasOwnProperty.call(object, prop);
1030
+
1027
1031
  export function isRecord<T extends readonly string[]>(
1028
1032
  value: unknown,
1029
1033
  requiredKeys: T
@@ -1033,9 +1037,6 @@ export function isRecord(
1033
1037
  value: unknown,
1034
1038
  requiredKeys: string[] | undefined = undefined
1035
1039
  ): value is Record<string, any> {
1036
- const toString = Object.prototype.toString;
1037
- const hasOwnProperty = Object.prototype.hasOwnProperty;
1038
- const isObject = (v: unknown) => toString.call(v) === '[object Object]';
1039
1040
  if (!isObject(value)) {
1040
1041
  return false;
1041
1042
  }
@@ -1047,7 +1048,7 @@ export function isRecord(
1047
1048
  }
1048
1049
 
1049
1050
  // Check to see if some method exists from the Object exists
1050
- if (!hasOwnProperty.call(ctor.prototype, 'isPrototypeOf')) {
1051
+ if (!HAS_OWN(ctor.prototype, 'isPrototypeOf')) {
1051
1052
  return false;
1052
1053
  }
1053
1054
  }
@@ -1261,7 +1262,7 @@ export class HostAddress {
1261
1262
  return `${this.socketPath}`;
1262
1263
  }
1263
1264
 
1264
- static fromString(s: string): HostAddress {
1265
+ static fromString(this: void, s: string): HostAddress {
1265
1266
  return new HostAddress(s);
1266
1267
  }
1267
1268
 
@@ -1406,3 +1407,26 @@ export function commandSupportsReadConcern(command: Document, options?: Document
1406
1407
 
1407
1408
  return false;
1408
1409
  }
1410
+
1411
+ /**
1412
+ * A utility function to get the instance of mongodb-client-encryption, if it exists.
1413
+ *
1414
+ * @throws MongoMissingDependencyError if mongodb-client-encryption isn't installed.
1415
+ * @returns
1416
+ */
1417
+ export function getMongoDBClientEncryption() {
1418
+ let mongodbClientEncryption;
1419
+
1420
+ // NOTE(NODE-4254): This is to get around the circular dependency between
1421
+ // mongodb-client-encryption and the driver in the test scenarios.
1422
+ if (
1423
+ typeof process.env.MONGODB_CLIENT_ENCRYPTION_OVERRIDE === 'string' &&
1424
+ process.env.MONGODB_CLIENT_ENCRYPTION_OVERRIDE.length > 0
1425
+ ) {
1426
+ mongodbClientEncryption = require(process.env.MONGODB_CLIENT_ENCRYPTION_OVERRIDE);
1427
+ } else {
1428
+ mongodbClientEncryption = require('mongodb-client-encryption');
1429
+ }
1430
+
1431
+ return mongodbClientEncryption;
1432
+ }