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
@@ -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
- [kCancelled]?: boolean;
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
- [kWaitQueue]: List<ServerSelectionRequest>;
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[kWaitQueue] = new List();
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[kWaitQueue], new MongoTopologyClosedError());
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[kWaitQueue].push(waitQueueMember);
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[kCancelled] = true;
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[kWaitQueue].length > 0) {
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[kCancelled]) {
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[kWaitQueue], new MongoTopologyClosedError());
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[kWaitQueue].length;
939
+ const membersToProcess = topology.waitQueue.length;
944
940
  for (let i = 0; i < membersToProcess; ++i) {
945
- const waitQueueMember = topology[kWaitQueue].shift();
941
+ const waitQueueMember = topology.waitQueue.shift();
946
942
  if (!waitQueueMember) {
947
943
  continue;
948
944
  }
949
945
 
950
- if (waitQueueMember[kCancelled]) {
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[kWaitQueue].push(waitQueueMember);
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[kWaitQueue].length > 0) {
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() {
@@ -1,6 +1,6 @@
1
1
  import { EJSON, type ObjectId } from '../bson';
2
2
  import * as WIRE_CONSTANTS from '../cmap/wire_protocol/constants';
3
- import { type MongoError, MongoRuntimeError } from '../error';
3
+ import { type MongoError, MongoRuntimeError, MongoStalePrimaryError } from '../error';
4
4
  import { compareObjectId, shuffle } from '../utils';
5
5
  import { ServerType, TopologyType } from './common';
6
6
  import { ServerDescription } from './server_description';
@@ -400,7 +400,9 @@ function updateRsFromPrimary(
400
400
  // replace serverDescription with a default ServerDescription of type "Unknown"
401
401
  serverDescriptions.set(
402
402
  serverDescription.address,
403
- new ServerDescription(serverDescription.address)
403
+ new ServerDescription(serverDescription.address, undefined, {
404
+ error: new MongoStalePrimaryError(serverDescription, maxSetVersion, maxElectionId)
405
+ })
404
406
  );
405
407
 
406
408
  return [checkHasPrimary(serverDescriptions), setName, maxSetVersion, maxElectionId];
@@ -416,7 +418,9 @@ function updateRsFromPrimary(
416
418
  // this primary is stale, we must remove it
417
419
  serverDescriptions.set(
418
420
  serverDescription.address,
419
- new ServerDescription(serverDescription.address)
421
+ new ServerDescription(serverDescription.address, undefined, {
422
+ error: new MongoStalePrimaryError(serverDescription, maxSetVersion, maxElectionId)
423
+ })
420
424
  );
421
425
 
422
426
  return [checkHasPrimary(serverDescriptions), setName, maxSetVersion, maxElectionId];
@@ -438,7 +442,12 @@ function updateRsFromPrimary(
438
442
  for (const [address, server] of serverDescriptions) {
439
443
  if (server.type === ServerType.RSPrimary && server.address !== serverDescription.address) {
440
444
  // Reset old primary's type to Unknown.
441
- serverDescriptions.set(address, new ServerDescription(server.address));
445
+ serverDescriptions.set(
446
+ address,
447
+ new ServerDescription(server.address, undefined, {
448
+ error: new MongoStalePrimaryError(serverDescription, maxSetVersion, maxElectionId)
449
+ })
450
+ );
442
451
 
443
452
  // There can only be one primary
444
453
  break;
package/src/sessions.ts CHANGED
@@ -83,17 +83,6 @@ export type ClientSessionEvents = {
83
83
  ended(session: ClientSession): void;
84
84
  };
85
85
 
86
- /** @internal */
87
- const kServerSession = Symbol('serverSession');
88
- /** @internal */
89
- const kSnapshotTime = Symbol('snapshotTime');
90
- /** @internal */
91
- const kSnapshotEnabled = Symbol('snapshotEnabled');
92
- /** @internal */
93
- const kPinnedConnection = Symbol('pinnedConnection');
94
- /** @internal Accumulates total number of increments to add to txnNumber when applying session to command */
95
- const kTxnNumberIncrement = Symbol('txnNumberIncrement');
96
-
97
86
  /** @public */
98
87
  export interface EndSessionOptions {
99
88
  /**
@@ -132,20 +121,22 @@ export class ClientSession
132
121
  owner?: symbol | AbstractCursor;
133
122
  defaultTransactionOptions: TransactionOptions;
134
123
  transaction: Transaction;
135
- /** @internal
124
+ /**
125
+ * @internal
136
126
  * Keeps track of whether or not the current transaction has attempted to be committed. Is
137
- * initially undefined. Gets set to false when startTransaction is called. When commitTransaction is sent to server, if the commitTransaction succeeds, it is then set to undefined, otherwise, set to true */
138
- commitAttempted?: boolean;
139
- /** @internal */
140
- private [kServerSession]: ServerSession | null;
127
+ * initially undefined. Gets set to false when startTransaction is called. When commitTransaction is sent to server, if the commitTransaction succeeds, it is then set to undefined, otherwise, set to true
128
+ */
129
+ private commitAttempted?: boolean;
130
+ public readonly snapshotEnabled: boolean;
131
+
141
132
  /** @internal */
142
- [kSnapshotTime]?: Timestamp;
133
+ private _serverSession: ServerSession | null;
143
134
  /** @internal */
144
- [kSnapshotEnabled] = false;
135
+ public snapshotTime?: Timestamp;
145
136
  /** @internal */
146
- [kPinnedConnection]?: Connection;
137
+ public pinnedConnection?: Connection;
147
138
  /** @internal */
148
- [kTxnNumberIncrement]: number;
139
+ public txnNumberIncrement: number;
149
140
  /**
150
141
  * @experimental
151
142
  * Specifies the time an operation in a given `ClientSession` will run until it throws a timeout error
@@ -183,13 +174,11 @@ export class ClientSession
183
174
 
184
175
  options = options ?? {};
185
176
 
186
- if (options.snapshot === true) {
187
- this[kSnapshotEnabled] = true;
188
- if (options.causalConsistency === true) {
189
- throw new MongoInvalidArgumentError(
190
- 'Properties "causalConsistency" and "snapshot" are mutually exclusive'
191
- );
192
- }
177
+ this.snapshotEnabled = options.snapshot === true;
178
+ if (options.causalConsistency === true && this.snapshotEnabled) {
179
+ throw new MongoInvalidArgumentError(
180
+ 'Properties "causalConsistency" and "snapshot" are mutually exclusive'
181
+ );
193
182
  }
194
183
 
195
184
  this.client = client;
@@ -199,8 +188,8 @@ export class ClientSession
199
188
  this.timeoutMS = options.defaultTimeoutMS ?? client.s.options?.timeoutMS;
200
189
 
201
190
  this.explicit = !!options.explicit;
202
- this[kServerSession] = this.explicit ? this.sessionPool.acquire() : null;
203
- this[kTxnNumberIncrement] = 0;
191
+ this._serverSession = this.explicit ? this.sessionPool.acquire() : null;
192
+ this.txnNumberIncrement = 0;
204
193
 
205
194
  const defaultCausalConsistencyValue = this.explicit && options.snapshot !== true;
206
195
  this.supports = {
@@ -218,11 +207,11 @@ export class ClientSession
218
207
 
219
208
  /** The server id associated with this session */
220
209
  get id(): ServerSessionId | undefined {
221
- return this[kServerSession]?.id;
210
+ return this.serverSession?.id;
222
211
  }
223
212
 
224
213
  get serverSession(): ServerSession {
225
- let serverSession = this[kServerSession];
214
+ let serverSession = this._serverSession;
226
215
  if (serverSession == null) {
227
216
  if (this.explicit) {
228
217
  throw new MongoRuntimeError('Unexpected null serverSession for an explicit session');
@@ -231,32 +220,22 @@ export class ClientSession
231
220
  throw new MongoRuntimeError('Unexpected null serverSession for an ended implicit session');
232
221
  }
233
222
  serverSession = this.sessionPool.acquire();
234
- this[kServerSession] = serverSession;
223
+ this._serverSession = serverSession;
235
224
  }
236
225
  return serverSession;
237
226
  }
238
227
 
239
- /** Whether or not this session is configured for snapshot reads */
240
- get snapshotEnabled(): boolean {
241
- return this[kSnapshotEnabled];
242
- }
243
-
244
228
  get loadBalanced(): boolean {
245
229
  return this.client.topology?.description.type === TopologyType.LoadBalanced;
246
230
  }
247
231
 
248
- /** @internal */
249
- get pinnedConnection(): Connection | undefined {
250
- return this[kPinnedConnection];
251
- }
252
-
253
232
  /** @internal */
254
233
  pin(conn: Connection): void {
255
- if (this[kPinnedConnection]) {
234
+ if (this.pinnedConnection) {
256
235
  throw TypeError('Cannot pin multiple connections to the same session');
257
236
  }
258
237
 
259
- this[kPinnedConnection] = conn;
238
+ this.pinnedConnection = conn;
260
239
  conn.emit(
261
240
  PINNED,
262
241
  this.inTransaction() ? ConnectionPoolMetrics.TXN : ConnectionPoolMetrics.CURSOR
@@ -273,7 +252,7 @@ export class ClientSession
273
252
  }
274
253
 
275
254
  get isPinned(): boolean {
276
- return this.loadBalanced ? !!this[kPinnedConnection] : this.transaction.isPinned;
255
+ return this.loadBalanced ? !!this.pinnedConnection : this.transaction.isPinned;
277
256
  }
278
257
 
279
258
  /**
@@ -295,12 +274,12 @@ export class ClientSession
295
274
  squashError(error);
296
275
  } finally {
297
276
  if (!this.hasEnded) {
298
- const serverSession = this[kServerSession];
277
+ const serverSession = this.serverSession;
299
278
  if (serverSession != null) {
300
279
  // release the server session back to the pool
301
280
  this.sessionPool.release(serverSession);
302
281
  // Store a clone of the server session for reference (debugging)
303
- this[kServerSession] = new ServerSession(serverSession);
282
+ this._serverSession = new ServerSession(serverSession);
304
283
  }
305
284
  // mark the session as ended, and emit a signal
306
285
  this.hasEnded = true;
@@ -391,7 +370,7 @@ export class ClientSession
391
370
  * This is because the serverSession is lazily acquired after a connection is obtained
392
371
  */
393
372
  incrementTransactionNumber(): void {
394
- this[kTxnNumberIncrement] += 1;
373
+ this.txnNumberIncrement += 1;
395
374
  }
396
375
 
397
376
  /** @returns whether this session is currently in a transaction or not */
@@ -410,7 +389,7 @@ export class ClientSession
410
389
  * @param options - Options for the transaction
411
390
  */
412
391
  startTransaction(options?: TransactionOptions): void {
413
- if (this[kSnapshotEnabled]) {
392
+ if (this.snapshotEnabled) {
414
393
  throw new MongoCompatibilityError('Transactions are not supported in snapshot sessions');
415
394
  }
416
395
 
@@ -908,7 +887,7 @@ export function maybeClearPinnedConnection(
908
887
  options?: EndSessionOptions
909
888
  ): void {
910
889
  // unpin a connection if it has been pinned
911
- const conn = session[kPinnedConnection];
890
+ const conn = session.pinnedConnection;
912
891
  const error = options?.error;
913
892
 
914
893
  if (
@@ -929,7 +908,7 @@ export function maybeClearPinnedConnection(
929
908
 
930
909
  if (options?.error == null || options?.force) {
931
910
  loadBalancer.pool.checkIn(conn);
932
- session[kPinnedConnection] = undefined;
911
+ session.pinnedConnection = undefined;
933
912
  conn.emit(
934
913
  UNPINNED,
935
914
  session.transaction.state !== TxnState.NO_TRANSACTION
@@ -1123,8 +1102,8 @@ export function applySession(
1123
1102
  const isRetryableWrite = !!options.willRetryWrite;
1124
1103
 
1125
1104
  if (isRetryableWrite || inTxnOrTxnCommand) {
1126
- serverSession.txnNumber += session[kTxnNumberIncrement];
1127
- session[kTxnNumberIncrement] = 0;
1105
+ serverSession.txnNumber += session.txnNumberIncrement;
1106
+ session.txnNumberIncrement = 0;
1128
1107
  // TODO(NODE-2674): Preserve int64 sent from MongoDB
1129
1108
  command.txnNumber = Long.fromNumber(serverSession.txnNumber);
1130
1109
  }
@@ -1141,10 +1120,10 @@ export function applySession(
1141
1120
  ) {
1142
1121
  command.readConcern = command.readConcern || {};
1143
1122
  Object.assign(command.readConcern, { afterClusterTime: session.operationTime });
1144
- } else if (session[kSnapshotEnabled]) {
1123
+ } else if (session.snapshotEnabled) {
1145
1124
  command.readConcern = command.readConcern || { level: ReadConcernLevel.snapshot };
1146
- if (session[kSnapshotTime] != null) {
1147
- Object.assign(command.readConcern, { atClusterTime: session[kSnapshotTime] });
1125
+ if (session.snapshotTime != null) {
1126
+ Object.assign(command.readConcern, { atClusterTime: session.snapshotTime });
1148
1127
  }
1149
1128
  }
1150
1129
 
@@ -1187,12 +1166,12 @@ export function updateSessionFromResponse(session: ClientSession, document: Mong
1187
1166
  session.transaction._recoveryToken = document.recoveryToken;
1188
1167
  }
1189
1168
 
1190
- if (session?.[kSnapshotEnabled] && session[kSnapshotTime] == null) {
1169
+ if (session?.snapshotEnabled && session.snapshotTime == null) {
1191
1170
  // find and aggregate commands return atClusterTime on the cursor
1192
1171
  // distinct includes it in the response body
1193
1172
  const atClusterTime = document.atClusterTime;
1194
1173
  if (atClusterTime) {
1195
- session[kSnapshotTime] = atClusterTime;
1174
+ session.snapshotTime = atClusterTime;
1196
1175
  }
1197
1176
  }
1198
1177
  }