mongodb 6.10.0-dev.20241106.sha.dc3fe957 → 6.10.0-dev.20241107.sha.e5582ed7

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 (203) hide show
  1. package/lib/admin.js +3 -2
  2. package/lib/admin.js.map +1 -1
  3. package/lib/beta.d.ts +558 -38
  4. package/lib/bulk/common.js +4 -4
  5. package/lib/bulk/common.js.map +1 -1
  6. package/lib/change_stream.js +111 -51
  7. package/lib/change_stream.js.map +1 -1
  8. package/lib/client-side-encryption/auto_encrypter.js +8 -5
  9. package/lib/client-side-encryption/auto_encrypter.js.map +1 -1
  10. package/lib/client-side-encryption/client_encryption.js +48 -18
  11. package/lib/client-side-encryption/client_encryption.js.map +1 -1
  12. package/lib/client-side-encryption/state_machine.js +43 -29
  13. package/lib/client-side-encryption/state_machine.js.map +1 -1
  14. package/lib/cmap/connection.js +78 -6
  15. package/lib/cmap/connection.js.map +1 -1
  16. package/lib/cmap/connection_pool.js +14 -9
  17. package/lib/cmap/connection_pool.js.map +1 -1
  18. package/lib/cmap/wire_protocol/on_data.js +5 -1
  19. package/lib/cmap/wire_protocol/on_data.js.map +1 -1
  20. package/lib/cmap/wire_protocol/responses.js +30 -0
  21. package/lib/cmap/wire_protocol/responses.js.map +1 -1
  22. package/lib/collection.js +62 -3
  23. package/lib/collection.js.map +1 -1
  24. package/lib/connection_string.js +2 -0
  25. package/lib/connection_string.js.map +1 -1
  26. package/lib/cursor/abstract_cursor.js +218 -38
  27. package/lib/cursor/abstract_cursor.js.map +1 -1
  28. package/lib/cursor/aggregation_cursor.js +29 -7
  29. package/lib/cursor/aggregation_cursor.js.map +1 -1
  30. package/lib/cursor/change_stream_cursor.js +2 -2
  31. package/lib/cursor/change_stream_cursor.js.map +1 -1
  32. package/lib/cursor/client_bulk_write_cursor.js +1 -1
  33. package/lib/cursor/client_bulk_write_cursor.js.map +1 -1
  34. package/lib/cursor/find_cursor.js +18 -8
  35. package/lib/cursor/find_cursor.js.map +1 -1
  36. package/lib/cursor/list_collections_cursor.js +1 -1
  37. package/lib/cursor/list_collections_cursor.js.map +1 -1
  38. package/lib/cursor/list_indexes_cursor.js +1 -1
  39. package/lib/cursor/list_indexes_cursor.js.map +1 -1
  40. package/lib/cursor/run_command_cursor.js +6 -4
  41. package/lib/cursor/run_command_cursor.js.map +1 -1
  42. package/lib/db.js +63 -3
  43. package/lib/db.js.map +1 -1
  44. package/lib/error.js +27 -2
  45. package/lib/error.js.map +1 -1
  46. package/lib/explain.js +57 -1
  47. package/lib/explain.js.map +1 -1
  48. package/lib/gridfs/download.js +31 -3
  49. package/lib/gridfs/download.js.map +1 -1
  50. package/lib/gridfs/index.js +49 -14
  51. package/lib/gridfs/index.js.map +1 -1
  52. package/lib/gridfs/upload.js +80 -22
  53. package/lib/gridfs/upload.js.map +1 -1
  54. package/lib/index.js +9 -5
  55. package/lib/index.js.map +1 -1
  56. package/lib/mongo_client.js +70 -1
  57. package/lib/mongo_client.js.map +1 -1
  58. package/lib/operations/aggregate.js +2 -2
  59. package/lib/operations/aggregate.js.map +1 -1
  60. package/lib/operations/bulk_write.js +7 -2
  61. package/lib/operations/bulk_write.js.map +1 -1
  62. package/lib/operations/client_bulk_write/client_bulk_write.js +3 -3
  63. package/lib/operations/client_bulk_write/client_bulk_write.js.map +1 -1
  64. package/lib/operations/client_bulk_write/executor.js +14 -3
  65. package/lib/operations/client_bulk_write/executor.js.map +1 -1
  66. package/lib/operations/command.js +5 -2
  67. package/lib/operations/command.js.map +1 -1
  68. package/lib/operations/count.js +2 -2
  69. package/lib/operations/count.js.map +1 -1
  70. package/lib/operations/create_collection.js +8 -7
  71. package/lib/operations/create_collection.js.map +1 -1
  72. package/lib/operations/delete.js +6 -6
  73. package/lib/operations/delete.js.map +1 -1
  74. package/lib/operations/distinct.js +2 -2
  75. package/lib/operations/distinct.js.map +1 -1
  76. package/lib/operations/drop.js +8 -8
  77. package/lib/operations/drop.js.map +1 -1
  78. package/lib/operations/estimated_document_count.js +2 -2
  79. package/lib/operations/estimated_document_count.js.map +1 -1
  80. package/lib/operations/execute_operation.js +16 -10
  81. package/lib/operations/execute_operation.js.map +1 -1
  82. package/lib/operations/find.js +6 -3
  83. package/lib/operations/find.js.map +1 -1
  84. package/lib/operations/find_and_modify.js +2 -2
  85. package/lib/operations/find_and_modify.js.map +1 -1
  86. package/lib/operations/get_more.js +2 -1
  87. package/lib/operations/get_more.js.map +1 -1
  88. package/lib/operations/indexes.js +6 -6
  89. package/lib/operations/indexes.js.map +1 -1
  90. package/lib/operations/insert.js +6 -6
  91. package/lib/operations/insert.js.map +1 -1
  92. package/lib/operations/kill_cursors.js +5 -2
  93. package/lib/operations/kill_cursors.js.map +1 -1
  94. package/lib/operations/list_collections.js +2 -2
  95. package/lib/operations/list_collections.js.map +1 -1
  96. package/lib/operations/list_databases.js +2 -2
  97. package/lib/operations/list_databases.js.map +1 -1
  98. package/lib/operations/operation.js.map +1 -1
  99. package/lib/operations/profiling_level.js +2 -2
  100. package/lib/operations/profiling_level.js.map +1 -1
  101. package/lib/operations/remove_user.js +2 -2
  102. package/lib/operations/remove_user.js.map +1 -1
  103. package/lib/operations/rename.js +2 -2
  104. package/lib/operations/rename.js.map +1 -1
  105. package/lib/operations/run_command.js +6 -4
  106. package/lib/operations/run_command.js.map +1 -1
  107. package/lib/operations/search_indexes/create.js +5 -2
  108. package/lib/operations/search_indexes/create.js.map +1 -1
  109. package/lib/operations/search_indexes/drop.js +2 -2
  110. package/lib/operations/search_indexes/drop.js.map +1 -1
  111. package/lib/operations/search_indexes/update.js +2 -2
  112. package/lib/operations/search_indexes/update.js.map +1 -1
  113. package/lib/operations/set_profiling_level.js +2 -2
  114. package/lib/operations/set_profiling_level.js.map +1 -1
  115. package/lib/operations/stats.js +2 -2
  116. package/lib/operations/stats.js.map +1 -1
  117. package/lib/operations/update.js +8 -8
  118. package/lib/operations/update.js.map +1 -1
  119. package/lib/operations/validate_collection.js +2 -2
  120. package/lib/operations/validate_collection.js.map +1 -1
  121. package/lib/sdam/server.js +4 -1
  122. package/lib/sdam/server.js.map +1 -1
  123. package/lib/sdam/server_description.js +2 -0
  124. package/lib/sdam/server_description.js.map +1 -1
  125. package/lib/sdam/topology.js +38 -11
  126. package/lib/sdam/topology.js.map +1 -1
  127. package/lib/sessions.js +145 -74
  128. package/lib/sessions.js.map +1 -1
  129. package/lib/timeout.js +217 -16
  130. package/lib/timeout.js.map +1 -1
  131. package/lib/utils.js +31 -17
  132. package/lib/utils.js.map +1 -1
  133. package/lib/write_concern.js.map +1 -1
  134. package/mongodb.d.ts +558 -38
  135. package/package.json +2 -2
  136. package/src/admin.ts +6 -2
  137. package/src/bulk/common.ts +17 -5
  138. package/src/change_stream.ts +127 -52
  139. package/src/client-side-encryption/auto_encrypter.ts +12 -5
  140. package/src/client-side-encryption/client_encryption.ts +103 -20
  141. package/src/client-side-encryption/state_machine.ts +66 -32
  142. package/src/cmap/connection.ts +105 -8
  143. package/src/cmap/connection_pool.ts +14 -14
  144. package/src/cmap/wire_protocol/on_data.ts +11 -1
  145. package/src/cmap/wire_protocol/responses.ts +35 -1
  146. package/src/collection.ts +81 -9
  147. package/src/connection_string.ts +2 -0
  148. package/src/cursor/abstract_cursor.ts +286 -39
  149. package/src/cursor/aggregation_cursor.ts +54 -8
  150. package/src/cursor/change_stream_cursor.ts +6 -2
  151. package/src/cursor/client_bulk_write_cursor.ts +6 -2
  152. package/src/cursor/find_cursor.ts +40 -9
  153. package/src/cursor/list_collections_cursor.ts +1 -1
  154. package/src/cursor/list_indexes_cursor.ts +1 -1
  155. package/src/cursor/run_command_cursor.ts +50 -5
  156. package/src/db.ts +75 -7
  157. package/src/error.ts +26 -1
  158. package/src/explain.ts +85 -0
  159. package/src/gridfs/download.ts +43 -4
  160. package/src/gridfs/index.ts +64 -16
  161. package/src/gridfs/upload.ts +152 -45
  162. package/src/index.ts +26 -4
  163. package/src/mongo_client.ts +75 -3
  164. package/src/operations/aggregate.ts +10 -2
  165. package/src/operations/bulk_write.ts +9 -2
  166. package/src/operations/client_bulk_write/client_bulk_write.ts +11 -3
  167. package/src/operations/client_bulk_write/executor.ts +15 -3
  168. package/src/operations/command.ts +18 -8
  169. package/src/operations/count.ts +10 -3
  170. package/src/operations/create_collection.ts +14 -7
  171. package/src/operations/delete.ts +15 -6
  172. package/src/operations/distinct.ts +7 -2
  173. package/src/operations/drop.ts +18 -8
  174. package/src/operations/estimated_document_count.ts +7 -2
  175. package/src/operations/execute_operation.ts +22 -13
  176. package/src/operations/find.ts +17 -5
  177. package/src/operations/find_and_modify.ts +7 -2
  178. package/src/operations/get_more.ts +4 -1
  179. package/src/operations/indexes.ts +20 -7
  180. package/src/operations/insert.ts +13 -6
  181. package/src/operations/kill_cursors.ts +10 -2
  182. package/src/operations/list_collections.ts +10 -1
  183. package/src/operations/list_databases.ts +9 -2
  184. package/src/operations/operation.ts +16 -2
  185. package/src/operations/profiling_level.ts +7 -2
  186. package/src/operations/remove_user.ts +7 -2
  187. package/src/operations/rename.ts +7 -2
  188. package/src/operations/run_command.ts +23 -4
  189. package/src/operations/search_indexes/create.ts +10 -2
  190. package/src/operations/search_indexes/drop.ts +7 -2
  191. package/src/operations/search_indexes/update.ts +7 -2
  192. package/src/operations/set_profiling_level.ts +4 -2
  193. package/src/operations/stats.ts +7 -2
  194. package/src/operations/update.ts +16 -8
  195. package/src/operations/validate_collection.ts +7 -2
  196. package/src/sdam/server.ts +14 -4
  197. package/src/sdam/server_description.ts +4 -0
  198. package/src/sdam/topology.ts +43 -18
  199. package/src/sessions.ts +193 -89
  200. package/src/timeout.ts +310 -23
  201. package/src/transactions.ts +1 -1
  202. package/src/utils.ts +42 -28
  203. package/src/write_concern.ts +6 -3
@@ -1,9 +1,11 @@
1
1
  import type { Document } from '../bson';
2
2
  import { CursorResponse, ExplainedCursorResponse } from '../cmap/wire_protocol/responses';
3
+ import { type CursorTimeoutMode } from '../cursor/abstract_cursor';
3
4
  import { MongoInvalidArgumentError } from '../error';
4
5
  import { type ExplainOptions } from '../explain';
5
6
  import type { Server } from '../sdam/server';
6
7
  import type { ClientSession } from '../sessions';
8
+ import { type TimeoutContext } from '../timeout';
7
9
  import { maxWireVersion, type MongoDBNamespace } from '../utils';
8
10
  import { WriteConcern } from '../write_concern';
9
11
  import { type CollationOptions, CommandOperation, type CommandOperationOptions } from './command';
@@ -24,7 +26,9 @@ export interface AggregateOptions extends Omit<CommandOperationOptions, 'explain
24
26
  bypassDocumentValidation?: boolean;
25
27
  /** Return the query as cursor, on 2.6 \> it returns as a real cursor on pre 2.6 it returns as an emulated cursor. */
26
28
  cursor?: Document;
27
- /** specifies a cumulative time limit in milliseconds for processing operations on the cursor. MongoDB interrupts the operation at the earliest following interrupt point. */
29
+ /**
30
+ * Specifies a cumulative time limit in milliseconds for processing operations on the cursor. MongoDB interrupts the operation at the earliest following interrupt point.
31
+ */
28
32
  maxTimeMS?: number;
29
33
  /** The maximum amount of time for the server to wait on new documents to satisfy a tailable cursor query. */
30
34
  maxAwaitTimeMS?: number;
@@ -43,6 +47,8 @@ export interface AggregateOptions extends Omit<CommandOperationOptions, 'explain
43
47
  * or `db.aggregate().explain()`.
44
48
  */
45
49
  explain?: ExplainOptions['explain'];
50
+ /** @internal */
51
+ timeoutMode?: CursorTimeoutMode;
46
52
  }
47
53
 
48
54
  /** @internal */
@@ -105,7 +111,8 @@ export class AggregateOperation extends CommandOperation<CursorResponse> {
105
111
 
106
112
  override async execute(
107
113
  server: Server,
108
- session: ClientSession | undefined
114
+ session: ClientSession | undefined,
115
+ timeoutContext: TimeoutContext
109
116
  ): Promise<CursorResponse> {
110
117
  const options: AggregateOptions = this.options;
111
118
  const serverWireVersion = maxWireVersion(server);
@@ -150,6 +157,7 @@ export class AggregateOperation extends CommandOperation<CursorResponse> {
150
157
  server,
151
158
  session,
152
159
  command,
160
+ timeoutContext,
153
161
  this.explain ? ExplainedCursorResponse : CursorResponse
154
162
  );
155
163
  }
@@ -7,6 +7,7 @@ import type {
7
7
  import type { Collection } from '../collection';
8
8
  import type { Server } from '../sdam/server';
9
9
  import type { ClientSession } from '../sessions';
10
+ import { type TimeoutContext } from '../timeout';
10
11
  import { AbstractOperation, Aspect, defineAspects } from './operation';
11
12
 
12
13
  /** @internal */
@@ -32,11 +33,17 @@ export class BulkWriteOperation extends AbstractOperation<BulkWriteResult> {
32
33
 
33
34
  override async execute(
34
35
  server: Server,
35
- session: ClientSession | undefined
36
+ session: ClientSession | undefined,
37
+ timeoutContext: TimeoutContext
36
38
  ): Promise<BulkWriteResult> {
37
39
  const coll = this.collection;
38
40
  const operations = this.operations;
39
- const options = { ...this.options, ...this.bsonOptions, readPreference: this.readPreference };
41
+ const options = {
42
+ ...this.options,
43
+ ...this.bsonOptions,
44
+ readPreference: this.readPreference,
45
+ timeoutContext
46
+ };
40
47
 
41
48
  // Create the bulk operation
42
49
  const bulk: BulkOperationBase =
@@ -2,6 +2,7 @@ import { MongoClientBulkWriteExecutionError, ServerType } from '../../beta';
2
2
  import { ClientBulkWriteCursorResponse } from '../../cmap/wire_protocol/responses';
3
3
  import type { Server } from '../../sdam/server';
4
4
  import type { ClientSession } from '../../sessions';
5
+ import { type TimeoutContext } from '../../timeout';
5
6
  import { MongoDBNamespace } from '../../utils';
6
7
  import { CommandOperation } from '../command';
7
8
  import { Aspect, defineAspects } from '../operation';
@@ -43,7 +44,8 @@ export class ClientBulkWriteOperation extends CommandOperation<ClientBulkWriteCu
43
44
  */
44
45
  override async execute(
45
46
  server: Server,
46
- session: ClientSession | undefined
47
+ session: ClientSession | undefined,
48
+ timeoutContext: TimeoutContext
47
49
  ): Promise<ClientBulkWriteCursorResponse> {
48
50
  let command;
49
51
 
@@ -52,7 +54,7 @@ export class ClientBulkWriteOperation extends CommandOperation<ClientBulkWriteCu
52
54
  let connection;
53
55
  if (!session.pinnedConnection) {
54
56
  // Checkout a connection to build the command.
55
- connection = await server.pool.checkOut();
57
+ connection = await server.pool.checkOut({ timeoutContext });
56
58
  // Pin the connection to the session so it get used to execute the command and we do not
57
59
  // perform a double check-in/check-out.
58
60
  session.pin(connection);
@@ -93,7 +95,13 @@ export class ClientBulkWriteOperation extends CommandOperation<ClientBulkWriteCu
93
95
  if (!this.canRetryWrite) {
94
96
  this.options.willRetryWrite = false;
95
97
  }
96
- return await super.executeCommand(server, session, command, ClientBulkWriteCursorResponse);
98
+ return await super.executeCommand(
99
+ server,
100
+ session,
101
+ command,
102
+ timeoutContext,
103
+ ClientBulkWriteCursorResponse
104
+ );
97
105
  }
98
106
  }
99
107
 
@@ -1,4 +1,5 @@
1
1
  import { type Document } from '../../bson';
2
+ import { CursorTimeoutContext, CursorTimeoutMode } from '../../cursor/abstract_cursor';
2
3
  import { ClientBulkWriteCursor } from '../../cursor/client_bulk_write_cursor';
3
4
  import {
4
5
  MongoClientBulkWriteError,
@@ -7,6 +8,8 @@ import {
7
8
  MongoServerError
8
9
  } from '../../error';
9
10
  import { type MongoClient } from '../../mongo_client';
11
+ import { TimeoutContext } from '../../timeout';
12
+ import { resolveTimeoutOptions } from '../../utils';
10
13
  import { WriteConcern } from '../../write_concern';
11
14
  import { executeOperation } from '../execute_operation';
12
15
  import { ClientBulkWriteOperation } from './client_bulk_write';
@@ -53,7 +56,7 @@ export class ClientBulkWriteExecutor {
53
56
 
54
57
  // If no write concern was provided, we inherit one from the client.
55
58
  if (!this.options.writeConcern) {
56
- this.options.writeConcern = WriteConcern.fromOptions(this.client.options);
59
+ this.options.writeConcern = WriteConcern.fromOptions(this.client.s.options);
57
60
  }
58
61
 
59
62
  if (this.options.writeConcern?.w === 0) {
@@ -86,17 +89,26 @@ export class ClientBulkWriteExecutor {
86
89
  pkFactory
87
90
  );
88
91
  // Unacknowledged writes need to execute all batches and return { ok: 1}
92
+ const resolvedOptions = resolveTimeoutOptions(this.client, this.options);
93
+ const context = TimeoutContext.create(resolvedOptions);
94
+
89
95
  if (this.options.writeConcern?.w === 0) {
90
96
  while (commandBuilder.hasNextBatch()) {
91
97
  const operation = new ClientBulkWriteOperation(commandBuilder, this.options);
92
- await executeOperation(this.client, operation);
98
+ await executeOperation(this.client, operation, context);
93
99
  }
94
100
  return ClientBulkWriteResultsMerger.unacknowledged();
95
101
  } else {
96
102
  const resultsMerger = new ClientBulkWriteResultsMerger(this.options);
97
103
  // For each command will will create and exhaust a cursor for the results.
98
104
  while (commandBuilder.hasNextBatch()) {
99
- const cursor = new ClientBulkWriteCursor(this.client, commandBuilder, this.options);
105
+ const cursorContext = new CursorTimeoutContext(context, Symbol());
106
+ const options = {
107
+ ...this.options,
108
+ timeoutContext: cursorContext,
109
+ ...(resolvedOptions.timeoutMS != null && { timeoutMode: CursorTimeoutMode.LIFETIME })
110
+ };
111
+ const cursor = new ClientBulkWriteCursor(this.client, commandBuilder, options);
100
112
  try {
101
113
  await resultsMerger.merge(cursor);
102
114
  } catch (error) {
@@ -1,18 +1,19 @@
1
1
  import type { BSONSerializeOptions, Document } from '../bson';
2
2
  import { type MongoDBResponseConstructor } from '../cmap/wire_protocol/responses';
3
3
  import { MongoInvalidArgumentError } from '../error';
4
- import { Explain, type ExplainOptions } from '../explain';
4
+ import {
5
+ decorateWithExplain,
6
+ Explain,
7
+ type ExplainOptions,
8
+ validateExplainTimeoutOptions
9
+ } from '../explain';
5
10
  import { ReadConcern } from '../read_concern';
6
11
  import type { ReadPreference } from '../read_preference';
7
12
  import type { Server } from '../sdam/server';
8
13
  import { MIN_SECONDARY_WRITE_WIRE_VERSION } from '../sdam/server_selection';
9
14
  import type { ClientSession } from '../sessions';
10
- import {
11
- commandSupportsReadConcern,
12
- decorateWithExplain,
13
- maxWireVersion,
14
- MongoDBNamespace
15
- } from '../utils';
15
+ import { type TimeoutContext } from '../timeout';
16
+ import { commandSupportsReadConcern, maxWireVersion, MongoDBNamespace } from '../utils';
16
17
  import { WriteConcern, type WriteConcernOptions } from '../write_concern';
17
18
  import type { ReadConcernLike } from './../read_concern';
18
19
  import { AbstractOperation, Aspect, type OperationOptions } from './operation';
@@ -39,6 +40,9 @@ export interface CommandOperationOptions
39
40
  readConcern?: ReadConcernLike;
40
41
  /** Collation */
41
42
  collation?: CollationOptions;
43
+ /**
44
+ * maxTimeMS is a server-side time limit in milliseconds for processing an operation.
45
+ */
42
46
  maxTimeMS?: number;
43
47
  /**
44
48
  * Comment to apply to the operation.
@@ -65,6 +69,7 @@ export interface OperationParent {
65
69
  writeConcern?: WriteConcern;
66
70
  readPreference?: ReadPreference;
67
71
  bsonOptions?: BSONSerializeOptions;
72
+ timeoutMS?: number;
68
73
  }
69
74
 
70
75
  /** @internal */
@@ -95,6 +100,7 @@ export abstract class CommandOperation<T> extends AbstractOperation<T> {
95
100
 
96
101
  if (this.hasAspect(Aspect.EXPLAINABLE)) {
97
102
  this.explain = Explain.fromOptions(options);
103
+ if (this.explain) validateExplainTimeoutOptions(this.options, this.explain);
98
104
  } else if (options?.explain != null) {
99
105
  throw new MongoInvalidArgumentError(`Option "explain" is not supported on this command`);
100
106
  }
@@ -111,19 +117,22 @@ export abstract class CommandOperation<T> extends AbstractOperation<T> {
111
117
  server: Server,
112
118
  session: ClientSession | undefined,
113
119
  cmd: Document,
120
+ timeoutContext: TimeoutContext,
114
121
  responseType: T | undefined
115
122
  ): Promise<typeof responseType extends undefined ? Document : InstanceType<T>>;
116
123
 
117
124
  public async executeCommand(
118
125
  server: Server,
119
126
  session: ClientSession | undefined,
120
- cmd: Document
127
+ cmd: Document,
128
+ timeoutContext: TimeoutContext
121
129
  ): Promise<Document>;
122
130
 
123
131
  async executeCommand(
124
132
  server: Server,
125
133
  session: ClientSession | undefined,
126
134
  cmd: Document,
135
+ timeoutContext: TimeoutContext,
127
136
  responseType?: MongoDBResponseConstructor
128
137
  ): Promise<Document> {
129
138
  this.server = server;
@@ -131,6 +140,7 @@ export abstract class CommandOperation<T> extends AbstractOperation<T> {
131
140
  const options = {
132
141
  ...this.options,
133
142
  ...this.bsonOptions,
143
+ timeoutContext,
134
144
  readPreference: this.readPreference,
135
145
  session
136
146
  };
@@ -2,6 +2,7 @@ import type { Document } from '../bson';
2
2
  import type { Collection } from '../collection';
3
3
  import type { Server } from '../sdam/server';
4
4
  import type { ClientSession } from '../sessions';
5
+ import { type TimeoutContext } from '../timeout';
5
6
  import type { MongoDBNamespace } from '../utils';
6
7
  import { CommandOperation, type CommandOperationOptions } from './command';
7
8
  import { Aspect, defineAspects } from './operation';
@@ -12,7 +13,9 @@ export interface CountOptions extends CommandOperationOptions {
12
13
  skip?: number;
13
14
  /** The maximum amounts to count before aborting. */
14
15
  limit?: number;
15
- /** Number of milliseconds to wait before aborting the query. */
16
+ /**
17
+ * Number of milliseconds to wait before aborting the query.
18
+ */
16
19
  maxTimeMS?: number;
17
20
  /** An index name hint for the query. */
18
21
  hint?: string | Document;
@@ -36,7 +39,11 @@ export class CountOperation extends CommandOperation<number> {
36
39
  return 'count' as const;
37
40
  }
38
41
 
39
- override async execute(server: Server, session: ClientSession | undefined): Promise<number> {
42
+ override async execute(
43
+ server: Server,
44
+ session: ClientSession | undefined,
45
+ timeoutContext: TimeoutContext
46
+ ): Promise<number> {
40
47
  const options = this.options;
41
48
  const cmd: Document = {
42
49
  count: this.collectionName,
@@ -59,7 +66,7 @@ export class CountOperation extends CommandOperation<number> {
59
66
  cmd.maxTimeMS = options.maxTimeMS;
60
67
  }
61
68
 
62
- const result = await super.executeCommand(server, session, cmd);
69
+ const result = await super.executeCommand(server, session, cmd, timeoutContext);
63
70
  return result ? result.n : 0;
64
71
  }
65
72
  }
@@ -9,6 +9,7 @@ import { MongoCompatibilityError } from '../error';
9
9
  import type { PkFactory } from '../mongo_client';
10
10
  import type { Server } from '../sdam/server';
11
11
  import type { ClientSession } from '../sessions';
12
+ import { type TimeoutContext } from '../timeout';
12
13
  import { CommandOperation, type CommandOperationOptions } from './command';
13
14
  import { CreateIndexesOperation } from './indexes';
14
15
  import { Aspect, defineAspects } from './operation';
@@ -16,6 +17,7 @@ import { Aspect, defineAspects } from './operation';
16
17
  const ILLEGAL_COMMAND_FIELDS = new Set([
17
18
  'w',
18
19
  'wtimeout',
20
+ 'timeoutMS',
19
21
  'j',
20
22
  'fsync',
21
23
  'autoIndexId',
@@ -124,14 +126,18 @@ export class CreateCollectionOperation extends CommandOperation<Collection> {
124
126
  return 'create' as const;
125
127
  }
126
128
 
127
- override async execute(server: Server, session: ClientSession | undefined): Promise<Collection> {
129
+ override async execute(
130
+ server: Server,
131
+ session: ClientSession | undefined,
132
+ timeoutContext: TimeoutContext
133
+ ): Promise<Collection> {
128
134
  const db = this.db;
129
135
  const name = this.name;
130
136
  const options = this.options;
131
137
 
132
138
  const encryptedFields: Document | undefined =
133
139
  options.encryptedFields ??
134
- db.client.options.autoEncryption?.encryptedFieldsMap?.[`${db.databaseName}.${name}`];
140
+ db.client.s.options.autoEncryption?.encryptedFieldsMap?.[`${db.databaseName}.${name}`];
135
141
 
136
142
  if (encryptedFields) {
137
143
  // Creating a QE collection required min server of 7.0.0
@@ -155,7 +161,7 @@ export class CreateCollectionOperation extends CommandOperation<Collection> {
155
161
  unique: true
156
162
  }
157
163
  });
158
- await createOp.executeWithoutEncryptedFieldsCheck(server, session);
164
+ await createOp.executeWithoutEncryptedFieldsCheck(server, session, timeoutContext);
159
165
  }
160
166
 
161
167
  if (!options.encryptedFields) {
@@ -163,7 +169,7 @@ export class CreateCollectionOperation extends CommandOperation<Collection> {
163
169
  }
164
170
  }
165
171
 
166
- const coll = await this.executeWithoutEncryptedFieldsCheck(server, session);
172
+ const coll = await this.executeWithoutEncryptedFieldsCheck(server, session, timeoutContext);
167
173
 
168
174
  if (encryptedFields) {
169
175
  // Create the required index for queryable encryption support.
@@ -173,7 +179,7 @@ export class CreateCollectionOperation extends CommandOperation<Collection> {
173
179
  { __safeContent__: 1 },
174
180
  {}
175
181
  );
176
- await createIndexOp.execute(server, session);
182
+ await createIndexOp.execute(server, session, timeoutContext);
177
183
  }
178
184
 
179
185
  return coll;
@@ -181,7 +187,8 @@ export class CreateCollectionOperation extends CommandOperation<Collection> {
181
187
 
182
188
  private async executeWithoutEncryptedFieldsCheck(
183
189
  server: Server,
184
- session: ClientSession | undefined
190
+ session: ClientSession | undefined,
191
+ timeoutContext: TimeoutContext
185
192
  ): Promise<Collection> {
186
193
  const db = this.db;
187
194
  const name = this.name;
@@ -198,7 +205,7 @@ export class CreateCollectionOperation extends CommandOperation<Collection> {
198
205
  }
199
206
  }
200
207
  // otherwise just execute the command
201
- await super.executeCommand(server, session, cmd);
208
+ await super.executeCommand(server, session, cmd, timeoutContext);
202
209
  return new Collection(db, name, options);
203
210
  }
204
211
  }
@@ -4,6 +4,7 @@ import { MongoCompatibilityError, MongoServerError } from '../error';
4
4
  import { type TODO_NODE_3286 } from '../mongo_types';
5
5
  import type { Server } from '../sdam/server';
6
6
  import type { ClientSession } from '../sessions';
7
+ import { type TimeoutContext } from '../timeout';
7
8
  import { type MongoDBNamespace } from '../utils';
8
9
  import { type WriteConcernOptions } from '../write_concern';
9
10
  import { type CollationOptions, CommandOperation, type CommandOperationOptions } from './command';
@@ -67,7 +68,8 @@ export class DeleteOperation extends CommandOperation<DeleteResult> {
67
68
 
68
69
  override async execute(
69
70
  server: Server,
70
- session: ClientSession | undefined
71
+ session: ClientSession | undefined,
72
+ timeoutContext: TimeoutContext
71
73
  ): Promise<DeleteResult> {
72
74
  const options = this.options ?? {};
73
75
  const ordered = typeof options.ordered === 'boolean' ? options.ordered : true;
@@ -95,7 +97,12 @@ export class DeleteOperation extends CommandOperation<DeleteResult> {
95
97
  }
96
98
  }
97
99
 
98
- const res: TODO_NODE_3286 = await super.executeCommand(server, session, command);
100
+ const res: TODO_NODE_3286 = await super.executeCommand(
101
+ server,
102
+ session,
103
+ command,
104
+ timeoutContext
105
+ );
99
106
  return res;
100
107
  }
101
108
  }
@@ -107,9 +114,10 @@ export class DeleteOneOperation extends DeleteOperation {
107
114
 
108
115
  override async execute(
109
116
  server: Server,
110
- session: ClientSession | undefined
117
+ session: ClientSession | undefined,
118
+ timeoutContext: TimeoutContext
111
119
  ): Promise<DeleteResult> {
112
- const res: TODO_NODE_3286 = await super.execute(server, session);
120
+ const res: TODO_NODE_3286 = await super.execute(server, session, timeoutContext);
113
121
  if (this.explain) return res;
114
122
  if (res.code) throw new MongoServerError(res);
115
123
  if (res.writeErrors) throw new MongoServerError(res.writeErrors[0]);
@@ -127,9 +135,10 @@ export class DeleteManyOperation extends DeleteOperation {
127
135
 
128
136
  override async execute(
129
137
  server: Server,
130
- session: ClientSession | undefined
138
+ session: ClientSession | undefined,
139
+ timeoutContext: TimeoutContext
131
140
  ): Promise<DeleteResult> {
132
- const res: TODO_NODE_3286 = await super.execute(server, session);
141
+ const res: TODO_NODE_3286 = await super.execute(server, session, timeoutContext);
133
142
  if (this.explain) return res;
134
143
  if (res.code) throw new MongoServerError(res);
135
144
  if (res.writeErrors) throw new MongoServerError(res.writeErrors[0]);
@@ -2,6 +2,7 @@ import type { Document } from '../bson';
2
2
  import type { Collection } from '../collection';
3
3
  import type { Server } from '../sdam/server';
4
4
  import type { ClientSession } from '../sessions';
5
+ import { type TimeoutContext } from '../timeout';
5
6
  import { decorateWithCollation, decorateWithReadConcern } from '../utils';
6
7
  import { CommandOperation, type CommandOperationOptions } from './command';
7
8
  import { Aspect, defineAspects } from './operation';
@@ -42,7 +43,11 @@ export class DistinctOperation extends CommandOperation<any[]> {
42
43
  return 'distinct' as const;
43
44
  }
44
45
 
45
- override async execute(server: Server, session: ClientSession | undefined): Promise<any[]> {
46
+ override async execute(
47
+ server: Server,
48
+ session: ClientSession | undefined,
49
+ timeoutContext: TimeoutContext
50
+ ): Promise<any[]> {
46
51
  const coll = this.collection;
47
52
  const key = this.key;
48
53
  const query = this.query;
@@ -72,7 +77,7 @@ export class DistinctOperation extends CommandOperation<any[]> {
72
77
  // Have we specified collation
73
78
  decorateWithCollation(cmd, coll, options);
74
79
 
75
- const result = await super.executeCommand(server, session, cmd);
80
+ const result = await super.executeCommand(server, session, cmd, timeoutContext);
76
81
 
77
82
  return this.explain ? result : result.values;
78
83
  }
@@ -3,6 +3,7 @@ import type { Db } from '../db';
3
3
  import { MONGODB_ERROR_CODES, MongoServerError } from '../error';
4
4
  import type { Server } from '../sdam/server';
5
5
  import type { ClientSession } from '../sessions';
6
+ import { type TimeoutContext } from '../timeout';
6
7
  import { CommandOperation, type CommandOperationOptions } from './command';
7
8
  import { Aspect, defineAspects } from './operation';
8
9
 
@@ -29,12 +30,16 @@ export class DropCollectionOperation extends CommandOperation<boolean> {
29
30
  return 'drop' as const;
30
31
  }
31
32
 
32
- override async execute(server: Server, session: ClientSession | undefined): Promise<boolean> {
33
+ override async execute(
34
+ server: Server,
35
+ session: ClientSession | undefined,
36
+ timeoutContext: TimeoutContext
37
+ ): Promise<boolean> {
33
38
  const db = this.db;
34
39
  const options = this.options;
35
40
  const name = this.name;
36
41
 
37
- const encryptedFieldsMap = db.client.options.autoEncryption?.encryptedFieldsMap;
42
+ const encryptedFieldsMap = db.client.s.options.autoEncryption?.encryptedFieldsMap;
38
43
  let encryptedFields: Document | undefined =
39
44
  options.encryptedFields ?? encryptedFieldsMap?.[`${db.databaseName}.${name}`];
40
45
 
@@ -57,7 +62,7 @@ export class DropCollectionOperation extends CommandOperation<boolean> {
57
62
  // Drop auxilliary collections, ignoring potential NamespaceNotFound errors.
58
63
  const dropOp = new DropCollectionOperation(db, collectionName);
59
64
  try {
60
- await dropOp.executeWithoutEncryptedFieldsCheck(server, session);
65
+ await dropOp.executeWithoutEncryptedFieldsCheck(server, session, timeoutContext);
61
66
  } catch (err) {
62
67
  if (
63
68
  !(err instanceof MongoServerError) ||
@@ -69,14 +74,15 @@ export class DropCollectionOperation extends CommandOperation<boolean> {
69
74
  }
70
75
  }
71
76
 
72
- return await this.executeWithoutEncryptedFieldsCheck(server, session);
77
+ return await this.executeWithoutEncryptedFieldsCheck(server, session, timeoutContext);
73
78
  }
74
79
 
75
80
  private async executeWithoutEncryptedFieldsCheck(
76
81
  server: Server,
77
- session: ClientSession | undefined
82
+ session: ClientSession | undefined,
83
+ timeoutContext: TimeoutContext
78
84
  ): Promise<boolean> {
79
- await super.executeCommand(server, session, { drop: this.name });
85
+ await super.executeCommand(server, session, { drop: this.name }, timeoutContext);
80
86
  return true;
81
87
  }
82
88
  }
@@ -96,8 +102,12 @@ export class DropDatabaseOperation extends CommandOperation<boolean> {
96
102
  return 'dropDatabase' as const;
97
103
  }
98
104
 
99
- override async execute(server: Server, session: ClientSession | undefined): Promise<boolean> {
100
- await super.executeCommand(server, session, { dropDatabase: 1 });
105
+ override async execute(
106
+ server: Server,
107
+ session: ClientSession | undefined,
108
+ timeoutContext: TimeoutContext
109
+ ): Promise<boolean> {
110
+ await super.executeCommand(server, session, { dropDatabase: 1 }, timeoutContext);
101
111
  return true;
102
112
  }
103
113
  }
@@ -2,6 +2,7 @@ import type { Document } from '../bson';
2
2
  import type { Collection } from '../collection';
3
3
  import type { Server } from '../sdam/server';
4
4
  import type { ClientSession } from '../sessions';
5
+ import { type TimeoutContext } from '../timeout';
5
6
  import { CommandOperation, type CommandOperationOptions } from './command';
6
7
  import { Aspect, defineAspects } from './operation';
7
8
 
@@ -30,7 +31,11 @@ export class EstimatedDocumentCountOperation extends CommandOperation<number> {
30
31
  return 'count' as const;
31
32
  }
32
33
 
33
- override async execute(server: Server, session: ClientSession | undefined): Promise<number> {
34
+ override async execute(
35
+ server: Server,
36
+ session: ClientSession | undefined,
37
+ timeoutContext: TimeoutContext
38
+ ): Promise<number> {
34
39
  const cmd: Document = { count: this.collectionName };
35
40
 
36
41
  if (typeof this.options.maxTimeMS === 'number') {
@@ -43,7 +48,7 @@ export class EstimatedDocumentCountOperation extends CommandOperation<number> {
43
48
  cmd.comment = this.options.comment;
44
49
  }
45
50
 
46
- const response = await super.executeCommand(server, session, cmd);
51
+ const response = await super.executeCommand(server, session, cmd, timeoutContext);
47
52
 
48
53
  return response?.n || 0;
49
54
  }
@@ -24,6 +24,7 @@ import {
24
24
  } from '../sdam/server_selection';
25
25
  import type { Topology } from '../sdam/topology';
26
26
  import type { ClientSession } from '../sessions';
27
+ import { TimeoutContext } from '../timeout';
27
28
  import { supportsRetryableWrites } from '../utils';
28
29
  import { AbstractOperation, Aspect } from './operation';
29
30
 
@@ -57,7 +58,7 @@ type ResultTypeFromOperation<TOperation> =
57
58
  export async function executeOperation<
58
59
  T extends AbstractOperation<TResult>,
59
60
  TResult = ResultTypeFromOperation<T>
60
- >(client: MongoClient, operation: T): Promise<TResult> {
61
+ >(client: MongoClient, operation: T, timeoutContext?: TimeoutContext | null): Promise<TResult> {
61
62
  if (!(operation instanceof AbstractOperation)) {
62
63
  // TODO(NODE-3483): Extend MongoRuntimeError
63
64
  throw new MongoRuntimeError('This method requires a valid operation instance');
@@ -80,11 +81,6 @@ export async function executeOperation<
80
81
  } else if (session.client !== client) {
81
82
  throw new MongoInvalidArgumentError('ClientSession must be from the same MongoClient');
82
83
  }
83
- if (session.explicit && session?.timeoutMS != null && operation.options.timeoutMS != null) {
84
- throw new MongoInvalidArgumentError(
85
- 'Do not specify timeoutMS on operation if already specified on an explicit session'
86
- );
87
- }
88
84
 
89
85
  const readPreference = operation.readPreference ?? ReadPreference.primary;
90
86
  const inTransaction = !!session?.inTransaction();
@@ -105,9 +101,17 @@ export async function executeOperation<
105
101
  session.unpin();
106
102
  }
107
103
 
104
+ timeoutContext ??= TimeoutContext.create({
105
+ session,
106
+ serverSelectionTimeoutMS: client.s.options.serverSelectionTimeoutMS,
107
+ waitQueueTimeoutMS: client.s.options.waitQueueTimeoutMS,
108
+ timeoutMS: operation.options.timeoutMS
109
+ });
110
+
108
111
  try {
109
112
  return await tryOperation(operation, {
110
113
  topology,
114
+ timeoutContext,
111
115
  session,
112
116
  readPreference
113
117
  });
@@ -148,6 +152,7 @@ type RetryOptions = {
148
152
  session: ClientSession | undefined;
149
153
  readPreference: ReadPreference;
150
154
  topology: Topology;
155
+ timeoutContext: TimeoutContext;
151
156
  };
152
157
 
153
158
  /**
@@ -171,7 +176,10 @@ type RetryOptions = {
171
176
  async function tryOperation<
172
177
  T extends AbstractOperation<TResult>,
173
178
  TResult = ResultTypeFromOperation<T>
174
- >(operation: T, { topology, session, readPreference }: RetryOptions): Promise<TResult> {
179
+ >(
180
+ operation: T,
181
+ { topology, timeoutContext, session, readPreference }: RetryOptions
182
+ ): Promise<TResult> {
175
183
  let selector: ReadPreference | ServerSelector;
176
184
 
177
185
  if (operation.hasAspect(Aspect.MUST_SELECT_SAME_SERVER)) {
@@ -189,7 +197,8 @@ async function tryOperation<
189
197
 
190
198
  let server = await topology.selectServer(selector, {
191
199
  session,
192
- operationName: operation.commandName
200
+ operationName: operation.commandName,
201
+ timeoutContext
193
202
  });
194
203
 
195
204
  const hasReadAspect = operation.hasAspect(Aspect.READ_OPERATION);
@@ -214,12 +223,10 @@ async function tryOperation<
214
223
  session.incrementTransactionNumber();
215
224
  }
216
225
 
217
- // TODO(NODE-6231): implement infinite retry within CSOT timeout here
218
- const maxTries = willRetry ? 2 : 1;
226
+ const maxTries = willRetry ? (timeoutContext.csotEnabled() ? Infinity : 2) : 1;
219
227
  let previousOperationError: MongoError | undefined;
220
228
  let previousServer: ServerDescription | undefined;
221
229
 
222
- // TODO(NODE-6231): implement infinite retry within CSOT timeout here
223
230
  for (let tries = 0; tries < maxTries; tries++) {
224
231
  if (previousOperationError) {
225
232
  if (hasWriteAspect && previousOperationError.code === MMAPv1_RETRY_WRITES_ERROR_CODE) {
@@ -268,10 +275,9 @@ async function tryOperation<
268
275
  if (tries > 0 && operation.hasAspect(Aspect.COMMAND_BATCHING)) {
269
276
  operation.resetBatch();
270
277
  }
271
- return await operation.execute(server, session);
278
+ return await operation.execute(server, session, timeoutContext);
272
279
  } catch (operationError) {
273
280
  if (!(operationError instanceof MongoError)) throw operationError;
274
-
275
281
  if (
276
282
  previousOperationError != null &&
277
283
  operationError.hasErrorLabel(MongoErrorLabel.NoWritesPerformed)
@@ -280,6 +286,9 @@ async function tryOperation<
280
286
  }
281
287
  previousServer = server.description;
282
288
  previousOperationError = operationError;
289
+
290
+ // Reset timeouts
291
+ timeoutContext.clear();
283
292
  }
284
293
  }
285
294