mongodb 6.8.2 → 6.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 (242) hide show
  1. package/README.md +14 -1
  2. package/lib/beta.d.ts +7905 -0
  3. package/lib/beta.js +21 -0
  4. package/lib/beta.js.map +1 -0
  5. package/lib/bson.js +5 -5
  6. package/lib/bson.js.map +1 -1
  7. package/lib/bulk/common.js +16 -21
  8. package/lib/bulk/common.js.map +1 -1
  9. package/lib/bulk/ordered.js.map +1 -1
  10. package/lib/bulk/unordered.js.map +1 -1
  11. package/lib/change_stream.js +10 -8
  12. package/lib/change_stream.js.map +1 -1
  13. package/lib/client-side-encryption/auto_encrypter.js +14 -3
  14. package/lib/client-side-encryption/auto_encrypter.js.map +1 -1
  15. package/lib/client-side-encryption/client_encryption.js +25 -7
  16. package/lib/client-side-encryption/client_encryption.js.map +1 -1
  17. package/lib/client-side-encryption/crypto_callbacks.js +6 -6
  18. package/lib/client-side-encryption/crypto_callbacks.js.map +1 -1
  19. package/lib/client-side-encryption/mongocryptd_manager.js +9 -5
  20. package/lib/client-side-encryption/mongocryptd_manager.js.map +1 -1
  21. package/lib/client-side-encryption/providers/aws.js +1 -2
  22. package/lib/client-side-encryption/providers/aws.js.map +1 -1
  23. package/lib/client-side-encryption/providers/azure.js +5 -5
  24. package/lib/client-side-encryption/providers/azure.js.map +1 -1
  25. package/lib/client-side-encryption/providers/gcp.js +1 -2
  26. package/lib/client-side-encryption/providers/gcp.js.map +1 -1
  27. package/lib/client-side-encryption/providers/index.js +2 -3
  28. package/lib/client-side-encryption/providers/index.js.map +1 -1
  29. package/lib/client-side-encryption/state_machine.js +9 -4
  30. package/lib/client-side-encryption/state_machine.js.map +1 -1
  31. package/lib/cmap/auth/auth_provider.js.map +1 -1
  32. package/lib/cmap/auth/aws_temporary_credentials.js.map +1 -1
  33. package/lib/cmap/auth/gssapi.js +4 -4
  34. package/lib/cmap/auth/gssapi.js.map +1 -1
  35. package/lib/cmap/auth/mongo_credentials.js.map +1 -1
  36. package/lib/cmap/auth/mongocr.js.map +1 -1
  37. package/lib/cmap/auth/mongodb_aws.js.map +1 -1
  38. package/lib/cmap/auth/mongodb_oidc/automated_callback_workflow.js.map +1 -1
  39. package/lib/cmap/auth/mongodb_oidc/azure_machine_workflow.js.map +1 -1
  40. package/lib/cmap/auth/mongodb_oidc/callback_workflow.js +0 -2
  41. package/lib/cmap/auth/mongodb_oidc/callback_workflow.js.map +1 -1
  42. package/lib/cmap/auth/mongodb_oidc/command_builders.js +2 -3
  43. package/lib/cmap/auth/mongodb_oidc/command_builders.js.map +1 -1
  44. package/lib/cmap/auth/mongodb_oidc/gcp_machine_workflow.js.map +1 -1
  45. package/lib/cmap/auth/mongodb_oidc/human_callback_workflow.js.map +1 -1
  46. package/lib/cmap/auth/mongodb_oidc/machine_workflow.js +0 -2
  47. package/lib/cmap/auth/mongodb_oidc/machine_workflow.js.map +1 -1
  48. package/lib/cmap/auth/mongodb_oidc/token_cache.js.map +1 -1
  49. package/lib/cmap/auth/mongodb_oidc/token_machine_workflow.js.map +1 -1
  50. package/lib/cmap/auth/mongodb_oidc.js.map +1 -1
  51. package/lib/cmap/auth/plain.js.map +1 -1
  52. package/lib/cmap/auth/scram.js.map +1 -1
  53. package/lib/cmap/auth/x509.js.map +1 -1
  54. package/lib/cmap/command_monitoring_events.js.map +1 -1
  55. package/lib/cmap/commands.js +62 -5
  56. package/lib/cmap/commands.js.map +1 -1
  57. package/lib/cmap/connect.js +10 -7
  58. package/lib/cmap/connect.js.map +1 -1
  59. package/lib/cmap/connection.js +3 -5
  60. package/lib/cmap/connection.js.map +1 -1
  61. package/lib/cmap/connection_pool.js +11 -9
  62. package/lib/cmap/connection_pool.js.map +1 -1
  63. package/lib/cmap/connection_pool_events.js +7 -3
  64. package/lib/cmap/connection_pool_events.js.map +1 -1
  65. package/lib/cmap/handshake/client_metadata.js +5 -5
  66. package/lib/cmap/handshake/client_metadata.js.map +1 -1
  67. package/lib/cmap/metrics.js +1 -1
  68. package/lib/cmap/metrics.js.map +1 -1
  69. package/lib/cmap/stream_description.js.map +1 -1
  70. package/lib/cmap/wire_protocol/compression.js +5 -5
  71. package/lib/cmap/wire_protocol/compression.js.map +1 -1
  72. package/lib/cmap/wire_protocol/constants.js +2 -2
  73. package/lib/cmap/wire_protocol/on_data.js +1 -2
  74. package/lib/cmap/wire_protocol/on_data.js.map +1 -1
  75. package/lib/cmap/wire_protocol/on_demand/document.js.map +1 -1
  76. package/lib/cmap/wire_protocol/responses.js +4 -4
  77. package/lib/cmap/wire_protocol/responses.js.map +1 -1
  78. package/lib/cmap/wire_protocol/shared.js +2 -3
  79. package/lib/cmap/wire_protocol/shared.js.map +1 -1
  80. package/lib/collection.js.map +1 -1
  81. package/lib/connection_string.js +12 -6
  82. package/lib/connection_string.js.map +1 -1
  83. package/lib/constants.js +1 -0
  84. package/lib/constants.js.map +1 -1
  85. package/lib/cursor/abstract_cursor.js +21 -6
  86. package/lib/cursor/abstract_cursor.js.map +1 -1
  87. package/lib/cursor/aggregation_cursor.js +1 -1
  88. package/lib/cursor/aggregation_cursor.js.map +1 -1
  89. package/lib/cursor/change_stream_cursor.js.map +1 -1
  90. package/lib/cursor/find_cursor.js +3 -3
  91. package/lib/cursor/find_cursor.js.map +1 -1
  92. package/lib/db.js +1 -1
  93. package/lib/db.js.map +1 -1
  94. package/lib/deps.js +16 -8
  95. package/lib/deps.js.map +1 -1
  96. package/lib/encrypter.js.map +1 -1
  97. package/lib/error.js +19 -14
  98. package/lib/error.js.map +1 -1
  99. package/lib/explain.js.map +1 -1
  100. package/lib/gridfs/download.js +1 -4
  101. package/lib/gridfs/download.js.map +1 -1
  102. package/lib/gridfs/index.js +1 -1
  103. package/lib/gridfs/index.js.map +1 -1
  104. package/lib/gridfs/upload.js +0 -4
  105. package/lib/gridfs/upload.js.map +1 -1
  106. package/lib/index.js +4 -2
  107. package/lib/index.js.map +1 -1
  108. package/lib/mongo_client.js +15 -1
  109. package/lib/mongo_client.js.map +1 -1
  110. package/lib/mongo_client_auth_providers.js.map +1 -1
  111. package/lib/mongo_logger.js +8 -8
  112. package/lib/mongo_logger.js.map +1 -1
  113. package/lib/mongo_types.js +1 -0
  114. package/lib/mongo_types.js.map +1 -1
  115. package/lib/operations/aggregate.js +1 -0
  116. package/lib/operations/aggregate.js.map +1 -1
  117. package/lib/operations/bulk_write.js.map +1 -1
  118. package/lib/operations/client_bulk_write/command_builder.js +198 -0
  119. package/lib/operations/client_bulk_write/command_builder.js.map +1 -0
  120. package/lib/operations/client_bulk_write/common.js +3 -0
  121. package/lib/operations/client_bulk_write/common.js.map +1 -0
  122. package/lib/operations/collections.js.map +1 -1
  123. package/lib/operations/command.js +1 -1
  124. package/lib/operations/command.js.map +1 -1
  125. package/lib/operations/count.js.map +1 -1
  126. package/lib/operations/create_collection.js.map +1 -1
  127. package/lib/operations/delete.js +2 -2
  128. package/lib/operations/delete.js.map +1 -1
  129. package/lib/operations/distinct.js.map +1 -1
  130. package/lib/operations/drop.js.map +1 -1
  131. package/lib/operations/estimated_document_count.js.map +1 -1
  132. package/lib/operations/execute_operation.js +111 -109
  133. package/lib/operations/execute_operation.js.map +1 -1
  134. package/lib/operations/find.js.map +1 -1
  135. package/lib/operations/find_and_modify.js +2 -8
  136. package/lib/operations/find_and_modify.js.map +1 -1
  137. package/lib/operations/get_more.js.map +1 -1
  138. package/lib/operations/indexes.js.map +1 -1
  139. package/lib/operations/insert.js.map +1 -1
  140. package/lib/operations/is_capped.js.map +1 -1
  141. package/lib/operations/kill_cursors.js.map +1 -1
  142. package/lib/operations/list_collections.js.map +1 -1
  143. package/lib/operations/list_databases.js.map +1 -1
  144. package/lib/operations/operation.js +5 -5
  145. package/lib/operations/operation.js.map +1 -1
  146. package/lib/operations/options_operation.js.map +1 -1
  147. package/lib/operations/profiling_level.js.map +1 -1
  148. package/lib/operations/search_indexes/drop.js.map +1 -1
  149. package/lib/operations/set_profiling_level.js.map +1 -1
  150. package/lib/operations/stats.js.map +1 -1
  151. package/lib/operations/update.js +2 -2
  152. package/lib/operations/update.js.map +1 -1
  153. package/lib/operations/validate_collection.js.map +1 -1
  154. package/lib/read_concern.js.map +1 -1
  155. package/lib/read_preference.js +1 -1
  156. package/lib/read_preference.js.map +1 -1
  157. package/lib/resource_management.js +58 -0
  158. package/lib/resource_management.js.map +1 -0
  159. package/lib/sdam/common.js +3 -3
  160. package/lib/sdam/common.js.map +1 -1
  161. package/lib/sdam/monitor.js +1 -5
  162. package/lib/sdam/monitor.js.map +1 -1
  163. package/lib/sdam/server.js +2 -2
  164. package/lib/sdam/server.js.map +1 -1
  165. package/lib/sdam/server_description.js +3 -3
  166. package/lib/sdam/server_description.js.map +1 -1
  167. package/lib/sdam/server_selection.js +5 -5
  168. package/lib/sdam/server_selection.js.map +1 -1
  169. package/lib/sdam/srv_polling.js +2 -3
  170. package/lib/sdam/srv_polling.js.map +1 -1
  171. package/lib/sdam/topology.js +1 -1
  172. package/lib/sdam/topology.js.map +1 -1
  173. package/lib/sdam/topology_description.js.map +1 -1
  174. package/lib/sessions.js +221 -218
  175. package/lib/sessions.js.map +1 -1
  176. package/lib/sort.js +2 -3
  177. package/lib/sort.js.map +1 -1
  178. package/lib/timeout.js +0 -1
  179. package/lib/timeout.js.map +1 -1
  180. package/lib/transactions.js +2 -2
  181. package/lib/transactions.js.map +1 -1
  182. package/lib/utils.js +49 -51
  183. package/lib/utils.js.map +1 -1
  184. package/lib/write_concern.js +2 -2
  185. package/lib/write_concern.js.map +1 -1
  186. package/mongodb.d.ts +150 -142
  187. package/package.json +26 -27
  188. package/src/beta.ts +22 -0
  189. package/src/bson.ts +1 -2
  190. package/src/bulk/common.ts +18 -18
  191. package/src/change_stream.ts +33 -15
  192. package/src/client-side-encryption/auto_encrypter.ts +18 -82
  193. package/src/client-side-encryption/client_encryption.ts +51 -54
  194. package/src/client-side-encryption/mongocryptd_manager.ts +10 -6
  195. package/src/client-side-encryption/state_machine.ts +28 -6
  196. package/src/cmap/auth/gssapi.ts +1 -1
  197. package/src/cmap/auth/mongodb_aws.ts +2 -2
  198. package/src/cmap/auth/mongodb_oidc/callback_workflow.ts +2 -2
  199. package/src/cmap/auth/mongodb_oidc/machine_workflow.ts +2 -2
  200. package/src/cmap/commands.ts +70 -5
  201. package/src/cmap/connect.ts +4 -1
  202. package/src/cmap/connection.ts +2 -2
  203. package/src/cmap/connection_pool.ts +17 -9
  204. package/src/cmap/connection_pool_events.ts +34 -2
  205. package/src/cmap/handshake/client_metadata.ts +1 -1
  206. package/src/cmap/wire_protocol/constants.ts +2 -2
  207. package/src/cmap/wire_protocol/shared.ts +1 -2
  208. package/src/collection.ts +16 -15
  209. package/src/connection_string.ts +10 -3
  210. package/src/constants.ts +1 -0
  211. package/src/cursor/abstract_cursor.ts +38 -13
  212. package/src/cursor/aggregation_cursor.ts +6 -4
  213. package/src/deps.ts +8 -1
  214. package/src/error.ts +33 -14
  215. package/src/gridfs/download.ts +28 -4
  216. package/src/gridfs/upload.ts +1 -6
  217. package/src/index.ts +5 -1
  218. package/src/mongo_client.ts +29 -5
  219. package/src/mongo_logger.ts +5 -3
  220. package/src/mongo_types.ts +69 -68
  221. package/src/operations/aggregate.ts +2 -1
  222. package/src/operations/bulk_write.ts +2 -2
  223. package/src/operations/client_bulk_write/command_builder.ts +283 -0
  224. package/src/operations/client_bulk_write/common.ts +146 -0
  225. package/src/operations/command.ts +1 -1
  226. package/src/operations/execute_operation.ts +137 -131
  227. package/src/operations/find_and_modify.ts +2 -7
  228. package/src/operations/insert.ts +3 -4
  229. package/src/operations/operation.ts +7 -10
  230. package/src/operations/search_indexes/drop.ts +4 -1
  231. package/src/resource_management.ts +74 -0
  232. package/src/sdam/monitor.ts +3 -5
  233. package/src/sdam/server.ts +1 -1
  234. package/src/sdam/server_description.ts +5 -6
  235. package/src/sdam/srv_polling.ts +1 -2
  236. package/src/sessions.ts +291 -277
  237. package/src/sort.ts +1 -1
  238. package/src/timeout.ts +0 -1
  239. package/src/transactions.ts +1 -2
  240. package/src/utils.ts +9 -4
  241. package/src/write_concern.ts +2 -2
  242. package/tsconfig.json +2 -1
@@ -24,16 +24,15 @@ import {
24
24
  } from '../sdam/server_selection';
25
25
  import type { Topology } from '../sdam/topology';
26
26
  import type { ClientSession } from '../sessions';
27
- import { squashError, supportsRetryableWrites } from '../utils';
27
+ import { supportsRetryableWrites } from '../utils';
28
28
  import { AbstractOperation, Aspect } from './operation';
29
29
 
30
30
  const MMAPv1_RETRY_WRITES_ERROR_CODE = MONGODB_ERROR_CODES.IllegalOperation;
31
31
  const MMAPv1_RETRY_WRITES_ERROR_MESSAGE =
32
32
  'This MongoDB deployment does not support retryable writes. Please add retryWrites=false to your connection string.';
33
33
 
34
- type ResultTypeFromOperation<TOperation> = TOperation extends AbstractOperation<infer K>
35
- ? K
36
- : never;
34
+ type ResultTypeFromOperation<TOperation> =
35
+ TOperation extends AbstractOperation<infer K> ? K : never;
37
36
 
38
37
  /**
39
38
  * Executes the given operation with provided arguments.
@@ -45,10 +44,9 @@ type ResultTypeFromOperation<TOperation> = TOperation extends AbstractOperation<
45
44
  * not provided.
46
45
  *
47
46
  * The expectation is that this function:
48
- * - Connects the MongoClient if it has not already been connected
47
+ * - Connects the MongoClient if it has not already been connected, see {@link autoConnect}
49
48
  * - Creates a session if none is provided and cleans up the session it creates
50
- * - Selects a server based on readPreference or various factors
51
- * - Retries an operation if it fails for certain errors, see {@link retryOperation}
49
+ * - Tries an operation and retries under certain conditions, see {@link tryOperation}
52
50
  *
53
51
  * @typeParam T - The operation's type
54
52
  * @typeParam TResult - The type of the operation's result, calculated from T
@@ -65,23 +63,7 @@ export async function executeOperation<
65
63
  throw new MongoRuntimeError('This method requires a valid operation instance');
66
64
  }
67
65
 
68
- if (client.topology == null) {
69
- // Auto connect on operation
70
- if (client.s.hasBeenClosed) {
71
- throw new MongoNotConnectedError('Client must be connected before running operations');
72
- }
73
- client.s.options[Symbol.for('@@mdb.skipPingOnConnect')] = true;
74
- try {
75
- await client.connect();
76
- } finally {
77
- delete client.s.options[Symbol.for('@@mdb.skipPingOnConnect')];
78
- }
79
- }
80
-
81
- const { topology } = client;
82
- if (topology == null) {
83
- throw new MongoRuntimeError('client.connect did not create a topology but also did not throw');
84
- }
66
+ const topology = await autoConnect(client);
85
67
 
86
68
  // The driver sessions spec mandates that we implicitly create sessions for operations
87
69
  // that are not explicitly provided with a session.
@@ -108,7 +90,6 @@ export async function executeOperation<
108
90
  const inTransaction = !!session?.inTransaction();
109
91
 
110
92
  const hasReadAspect = operation.hasAspect(Aspect.READ_OPERATION);
111
- const hasWriteAspect = operation.hasAspect(Aspect.WRITE_OPERATION);
112
93
 
113
94
  if (
114
95
  inTransaction &&
@@ -124,6 +105,73 @@ export async function executeOperation<
124
105
  session.unpin();
125
106
  }
126
107
 
108
+ try {
109
+ return await tryOperation(operation, {
110
+ topology,
111
+ session,
112
+ readPreference
113
+ });
114
+ } finally {
115
+ if (session?.owner != null && session.owner === owner) {
116
+ await session.endSession();
117
+ }
118
+ }
119
+ }
120
+
121
+ /**
122
+ * Connects a client if it has not yet been connected
123
+ * @internal
124
+ */
125
+ async function autoConnect(client: MongoClient): Promise<Topology> {
126
+ if (client.topology == null) {
127
+ if (client.s.hasBeenClosed) {
128
+ throw new MongoNotConnectedError('Client must be connected before running operations');
129
+ }
130
+ client.s.options[Symbol.for('@@mdb.skipPingOnConnect')] = true;
131
+ try {
132
+ await client.connect();
133
+ if (client.topology == null) {
134
+ throw new MongoRuntimeError(
135
+ 'client.connect did not create a topology but also did not throw'
136
+ );
137
+ }
138
+ return client.topology;
139
+ } finally {
140
+ delete client.s.options[Symbol.for('@@mdb.skipPingOnConnect')];
141
+ }
142
+ }
143
+ return client.topology;
144
+ }
145
+
146
+ /** @internal */
147
+ type RetryOptions = {
148
+ session: ClientSession | undefined;
149
+ readPreference: ReadPreference;
150
+ topology: Topology;
151
+ };
152
+
153
+ /**
154
+ * Executes an operation and retries as appropriate
155
+ * @internal
156
+ *
157
+ * @remarks
158
+ * Implements behaviour described in [Retryable Reads](https://github.com/mongodb/specifications/blob/master/source/retryable-reads/retryable-reads.md) and [Retryable
159
+ * Writes](https://github.com/mongodb/specifications/blob/master/source/retryable-writes/retryable-writes.md) specification
160
+ *
161
+ * This function:
162
+ * - performs initial server selection
163
+ * - attempts to execute an operation
164
+ * - retries the operation if it meets the criteria for a retryable read or a retryable write
165
+ *
166
+ * @typeParam T - The operation's type
167
+ * @typeParam TResult - The type of the operation's result, calculated from T
168
+ *
169
+ * @param operation - The operation to execute
170
+ * */
171
+ async function tryOperation<
172
+ T extends AbstractOperation<TResult>,
173
+ TResult = ResultTypeFromOperation<T>
174
+ >(operation: T, { topology, session, readPreference }: RetryOptions): Promise<TResult> {
127
175
  let selector: ReadPreference | ServerSelector;
128
176
 
129
177
  if (operation.hasAspect(Aspect.MUST_SELECT_SAME_SERVER)) {
@@ -139,30 +187,14 @@ export async function executeOperation<
139
187
  selector = readPreference;
140
188
  }
141
189
 
142
- const server = await topology.selectServer(selector, {
190
+ let server = await topology.selectServer(selector, {
143
191
  session,
144
192
  operationName: operation.commandName
145
193
  });
146
194
 
147
- if (session == null) {
148
- // No session also means it is not retryable, early exit
149
- return await operation.execute(server, undefined);
150
- }
151
-
152
- if (!operation.hasAspect(Aspect.RETRYABLE)) {
153
- // non-retryable operation, early exit
154
- try {
155
- return await operation.execute(server, session);
156
- } finally {
157
- if (session?.owner != null && session.owner === owner) {
158
- try {
159
- await session.endSession();
160
- } catch (error) {
161
- squashError(error);
162
- }
163
- }
164
- }
165
- }
195
+ const hasReadAspect = operation.hasAspect(Aspect.READ_OPERATION);
196
+ const hasWriteAspect = operation.hasAspect(Aspect.WRITE_OPERATION);
197
+ const inTransaction = session?.inTransaction() ?? false;
166
198
 
167
199
  const willRetryRead = topology.s.options.retryReads && !inTransaction && operation.canRetryRead;
168
200
 
@@ -172,105 +204,79 @@ export async function executeOperation<
172
204
  supportsRetryableWrites(server) &&
173
205
  operation.canRetryWrite;
174
206
 
175
- const willRetry = (hasReadAspect && willRetryRead) || (hasWriteAspect && willRetryWrite);
207
+ const willRetry =
208
+ operation.hasAspect(Aspect.RETRYABLE) &&
209
+ session != null &&
210
+ ((hasReadAspect && willRetryRead) || (hasWriteAspect && willRetryWrite));
176
211
 
177
- if (hasWriteAspect && willRetryWrite) {
212
+ if (hasWriteAspect && willRetryWrite && session != null) {
178
213
  operation.options.willRetryWrite = true;
179
214
  session.incrementTransactionNumber();
180
215
  }
181
216
 
182
- try {
183
- return await operation.execute(server, session);
184
- } catch (operationError) {
185
- if (willRetry && operationError instanceof MongoError) {
186
- return await retryOperation(operation, operationError, {
187
- session,
188
- topology,
189
- selector,
190
- previousServer: server.description
191
- });
192
- }
193
- throw operationError;
194
- } finally {
195
- if (session?.owner != null && session.owner === owner) {
196
- try {
197
- await session.endSession();
198
- } catch (error) {
199
- squashError(error);
200
- }
201
- }
202
- }
203
- }
217
+ // TODO(NODE-6231): implement infinite retry within CSOT timeout here
218
+ const maxTries = willRetry ? 2 : 1;
219
+ let previousOperationError: MongoError | undefined;
220
+ let previousServer: ServerDescription | undefined;
204
221
 
205
- /** @internal */
206
- type RetryOptions = {
207
- session: ClientSession;
208
- topology: Topology;
209
- selector: ReadPreference | ServerSelector;
210
- previousServer: ServerDescription;
211
- };
222
+ // TODO(NODE-6231): implement infinite retry within CSOT timeout here
223
+ for (let tries = 0; tries < maxTries; tries++) {
224
+ if (previousOperationError) {
225
+ if (hasWriteAspect && previousOperationError.code === MMAPv1_RETRY_WRITES_ERROR_CODE) {
226
+ throw new MongoServerError({
227
+ message: MMAPv1_RETRY_WRITES_ERROR_MESSAGE,
228
+ errmsg: MMAPv1_RETRY_WRITES_ERROR_MESSAGE,
229
+ originalError: previousOperationError
230
+ });
231
+ }
212
232
 
213
- async function retryOperation<
214
- T extends AbstractOperation<TResult>,
215
- TResult = ResultTypeFromOperation<T>
216
- >(
217
- operation: T,
218
- originalError: MongoError,
219
- { session, topology, selector, previousServer }: RetryOptions
220
- ): Promise<TResult> {
221
- const isWriteOperation = operation.hasAspect(Aspect.WRITE_OPERATION);
222
- const isReadOperation = operation.hasAspect(Aspect.READ_OPERATION);
233
+ if (hasWriteAspect && !isRetryableWriteError(previousOperationError))
234
+ throw previousOperationError;
223
235
 
224
- if (isWriteOperation && originalError.code === MMAPv1_RETRY_WRITES_ERROR_CODE) {
225
- throw new MongoServerError({
226
- message: MMAPv1_RETRY_WRITES_ERROR_MESSAGE,
227
- errmsg: MMAPv1_RETRY_WRITES_ERROR_MESSAGE,
228
- originalError
229
- });
230
- }
236
+ if (hasReadAspect && !isRetryableReadError(previousOperationError))
237
+ throw previousOperationError;
231
238
 
232
- if (isWriteOperation && !isRetryableWriteError(originalError)) {
233
- throw originalError;
234
- }
235
-
236
- if (isReadOperation && !isRetryableReadError(originalError)) {
237
- throw originalError;
238
- }
239
+ if (
240
+ previousOperationError instanceof MongoNetworkError &&
241
+ operation.hasAspect(Aspect.CURSOR_CREATING) &&
242
+ session != null &&
243
+ session.isPinned &&
244
+ !session.inTransaction()
245
+ ) {
246
+ session.unpin({ force: true, forceClear: true });
247
+ }
239
248
 
240
- if (
241
- originalError instanceof MongoNetworkError &&
242
- session.isPinned &&
243
- !session.inTransaction() &&
244
- operation.hasAspect(Aspect.CURSOR_CREATING)
245
- ) {
246
- // If we have a cursor and the initial command fails with a network error,
247
- // we can retry it on another connection. So we need to check it back in, clear the
248
- // pool for the service id, and retry again.
249
- session.unpin({ force: true, forceClear: true });
250
- }
249
+ server = await topology.selectServer(selector, {
250
+ session,
251
+ operationName: operation.commandName,
252
+ previousServer
253
+ });
251
254
 
252
- // select a new server, and attempt to retry the operation
253
- const server = await topology.selectServer(selector, {
254
- session,
255
- operationName: operation.commandName,
256
- previousServer
257
- });
255
+ if (hasWriteAspect && !supportsRetryableWrites(server)) {
256
+ throw new MongoUnexpectedServerResponseError(
257
+ 'Selected server does not support retryable writes'
258
+ );
259
+ }
260
+ }
258
261
 
259
- if (isWriteOperation && !supportsRetryableWrites(server)) {
260
- throw new MongoUnexpectedServerResponseError(
261
- 'Selected server does not support retryable writes'
262
- );
263
- }
262
+ try {
263
+ return await operation.execute(server, session);
264
+ } catch (operationError) {
265
+ if (!(operationError instanceof MongoError)) throw operationError;
264
266
 
265
- try {
266
- return await operation.execute(server, session);
267
- } catch (retryError) {
268
- if (
269
- retryError instanceof MongoError &&
270
- retryError.hasErrorLabel(MongoErrorLabel.NoWritesPerformed)
271
- ) {
272
- throw originalError;
267
+ if (
268
+ previousOperationError != null &&
269
+ operationError.hasErrorLabel(MongoErrorLabel.NoWritesPerformed)
270
+ ) {
271
+ throw previousOperationError;
272
+ }
273
+ previousServer = server.description;
274
+ previousOperationError = operationError;
273
275
  }
274
- throw retryError;
275
276
  }
277
+
278
+ throw (
279
+ previousOperationError ??
280
+ new MongoRuntimeError('Tried to propagate retryability error, but no error was found.')
281
+ );
276
282
  }
@@ -192,12 +192,7 @@ export class FindAndModifyOperation extends CommandOperation<Document> {
192
192
  ...this.cmdBase
193
193
  };
194
194
 
195
- // Have we specified collation
196
- try {
197
- decorateWithCollation(cmd, coll, options);
198
- } catch (err) {
199
- return err;
200
- }
195
+ decorateWithCollation(cmd, coll, options);
201
196
 
202
197
  if (options.hint) {
203
198
  // TODO: once this method becomes a CommandOperation we will have the server
@@ -214,7 +209,7 @@ export class FindAndModifyOperation extends CommandOperation<Document> {
214
209
 
215
210
  // Execute the command
216
211
  const result = await super.executeCommand(server, session, cmd);
217
- return options.includeResultMetadata ? result : result.value ?? null;
212
+ return options.includeResultMetadata ? result : (result.value ?? null);
218
213
  }
219
214
  }
220
215
 
@@ -5,8 +5,7 @@ import { MongoInvalidArgumentError, MongoServerError } from '../error';
5
5
  import type { InferIdType } from '../mongo_types';
6
6
  import type { Server } from '../sdam/server';
7
7
  import type { ClientSession } from '../sessions';
8
- import type { MongoDBNamespace } from '../utils';
9
- import { maybeAddIdToDocuments } from '../utils';
8
+ import { maybeAddIdToDocuments, type MongoDBNamespace } from '../utils';
10
9
  import { WriteConcern } from '../write_concern';
11
10
  import { BulkWriteOperation } from './bulk_write';
12
11
  import { CommandOperation, type CommandOperationOptions } from './command';
@@ -104,9 +103,9 @@ export interface InsertManyResult<TSchema = Document> {
104
103
  export class InsertManyOperation extends AbstractOperation<InsertManyResult> {
105
104
  override options: BulkWriteOptions;
106
105
  collection: Collection;
107
- docs: Document[];
106
+ docs: ReadonlyArray<Document>;
108
107
 
109
- constructor(collection: Collection, docs: Document[], options: BulkWriteOptions) {
108
+ constructor(collection: Collection, docs: ReadonlyArray<Document>, options: BulkWriteOptions) {
110
109
  super(options);
111
110
 
112
111
  if (!Array.isArray(docs)) {
@@ -17,11 +17,6 @@ export const Aspect = {
17
17
  /** @public */
18
18
  export type Hint = string | Document;
19
19
 
20
- // eslint-disable-next-line @typescript-eslint/ban-types
21
- export interface OperationConstructor extends Function {
22
- aspects?: Set<symbol>;
23
- }
24
-
25
20
  /** @public */
26
21
  export interface OperationOptions extends BSONSerializeOptions {
27
22
  /** Specify ClientSession for this command */
@@ -63,10 +58,12 @@ export abstract class AbstractOperation<TResult = any> {
63
58
 
64
59
  [kSession]: ClientSession | undefined;
65
60
 
61
+ static aspects?: Set<symbol>;
62
+
66
63
  constructor(options: OperationOptions = {}) {
67
64
  this.readPreference = this.hasAspect(Aspect.WRITE_OPERATION)
68
65
  ? ReadPreference.primary
69
- : ReadPreference.fromOptions(options) ?? ReadPreference.primary;
66
+ : (ReadPreference.fromOptions(options) ?? ReadPreference.primary);
70
67
 
71
68
  // Pull the BSON serialize options from the already-resolved options
72
69
  this.bsonOptions = resolveBSONOptions(options);
@@ -85,7 +82,7 @@ export abstract class AbstractOperation<TResult = any> {
85
82
  abstract execute(server: Server, session: ClientSession | undefined): Promise<TResult>;
86
83
 
87
84
  hasAspect(aspect: symbol): boolean {
88
- const ctor = this.constructor as OperationConstructor;
85
+ const ctor = this.constructor as { aspects?: Set<symbol> };
89
86
  if (ctor.aspects == null) {
90
87
  return false;
91
88
  }
@@ -102,16 +99,16 @@ export abstract class AbstractOperation<TResult = any> {
102
99
  }
103
100
 
104
101
  get canRetryRead(): boolean {
105
- return true;
102
+ return this.hasAspect(Aspect.RETRYABLE) && this.hasAspect(Aspect.READ_OPERATION);
106
103
  }
107
104
 
108
105
  get canRetryWrite(): boolean {
109
- return true;
106
+ return this.hasAspect(Aspect.RETRYABLE) && this.hasAspect(Aspect.WRITE_OPERATION);
110
107
  }
111
108
  }
112
109
 
113
110
  export function defineAspects(
114
- operation: OperationConstructor,
111
+ operation: { aspects?: Set<symbol> },
115
112
  aspects: symbol | symbol[] | Set<symbol>
116
113
  ): Set<symbol> {
117
114
  if (!Array.isArray(aspects) && !(aspects instanceof Set)) {
@@ -8,7 +8,10 @@ import { AbstractOperation } from '../operation';
8
8
 
9
9
  /** @internal */
10
10
  export class DropSearchIndexOperation extends AbstractOperation<void> {
11
- constructor(private readonly collection: Collection, private readonly name: string) {
11
+ constructor(
12
+ private readonly collection: Collection,
13
+ private readonly name: string
14
+ ) {
12
15
  super();
13
16
  }
14
17
 
@@ -0,0 +1,74 @@
1
+ /**
2
+ * @public
3
+ */
4
+ export interface AsyncDisposable {
5
+ /**
6
+ * @beta
7
+ * @experimental
8
+ */
9
+ [Symbol.asyncDispose](): Promise<void>;
10
+
11
+ /**
12
+ * @internal
13
+ *
14
+ * A method that wraps disposal semantics for a given resource in the class.
15
+ */
16
+ asyncDispose(): Promise<void>;
17
+ }
18
+
19
+ /** @internal */
20
+ export function configureResourceManagement(target: AsyncDisposable) {
21
+ Symbol.asyncDispose &&
22
+ Object.defineProperty(target, Symbol.asyncDispose, {
23
+ value: async function asyncDispose(this: AsyncDisposable) {
24
+ await this.asyncDispose();
25
+ },
26
+ enumerable: false,
27
+ configurable: true,
28
+ writable: true
29
+ });
30
+ }
31
+
32
+ /**
33
+ * @beta
34
+ * @experimental
35
+ *
36
+ * Attaches `Symbol.asyncDispose` methods to the MongoClient, Cursors, sessions and change streams
37
+ * if Symbol.asyncDispose is defined.
38
+ *
39
+ * It's usually not necessary to call this method - the driver attempts to attach these methods
40
+ * itself when its loaded. However, sometimes the driver may be loaded before `Symbol.asyncDispose`
41
+ * is defined, in which case it is necessary to call this method directly. This can happen if the
42
+ * application is polyfilling `Symbol.asyncDispose`.
43
+ *
44
+ * Example:
45
+ *
46
+ * ```typescript
47
+ * import { configureExplicitResourceManagement, MongoClient } from 'mongodb/lib/beta';
48
+ *
49
+ * Symbol.asyncDispose ??= Symbol('dispose');
50
+ * load();
51
+ *
52
+ * await using client = new MongoClient(...);
53
+ * ```
54
+ */
55
+ export function configureExplicitResourceManagement() {
56
+ // We must import lazily here, because there's a circular dependency between the resource management
57
+ // file and each resources' file. We could move `configureResourceManagement` to a separate
58
+ // function, but keeping all resource-management related code together seemed preferable and I chose
59
+ // lazy requiring of resources instead.
60
+
61
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
62
+ const { MongoClient } = require('./mongo_client');
63
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
64
+ const { ClientSession } = require('./sessions');
65
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
66
+ const { AbstractCursor } = require('./cursor/abstract_cursor');
67
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
68
+ const { ChangeStream } = require('./change_stream');
69
+
70
+ configureResourceManagement(MongoClient.prototype);
71
+ configureResourceManagement(ClientSession.prototype);
72
+ configureResourceManagement(AbstractCursor.prototype);
73
+ configureResourceManagement(ChangeStream.prototype);
74
+ }
@@ -308,7 +308,7 @@ function checkServer(monitor: Monitor, callback: Callback<Document | null>) {
308
308
  // duration
309
309
  const duration =
310
310
  isAwaitable && monitor.rttPinger
311
- ? monitor.rttPinger.latestRtt ?? calculateDurationInMs(start)
311
+ ? (monitor.rttPinger.latestRtt ?? calculateDurationInMs(start))
312
312
  : calculateDurationInMs(start);
313
313
 
314
314
  monitor.addRttSample(duration);
@@ -378,7 +378,7 @@ function checkServer(monitor: Monitor, callback: Callback<Document | null>) {
378
378
  awaited = false;
379
379
  connection
380
380
  .command(ns('admin.$cmd'), cmd, options)
381
- // eslint-disable-next-line github/no-then
381
+
382
382
  .then(onHeartbeatSucceeded, onHeartbeatFailed);
383
383
 
384
384
  return;
@@ -397,7 +397,6 @@ function checkServer(monitor: Monitor, callback: Callback<Document | null>) {
397
397
  connection.destroy();
398
398
  throw error;
399
399
  }
400
- // eslint-disable-next-line github/no-then
401
400
  })().then(
402
401
  connection => {
403
402
  if (isInCloseState(monitor)) {
@@ -547,7 +546,6 @@ export class RTTPinger {
547
546
 
548
547
  const connection = this.connection;
549
548
  if (connection == null) {
550
- // eslint-disable-next-line github/no-then
551
549
  connect(this.monitor.connectOptions).then(
552
550
  connection => {
553
551
  this.measureAndReschedule(start, connection);
@@ -561,7 +559,7 @@ export class RTTPinger {
561
559
 
562
560
  const commandName =
563
561
  connection.serverApi?.version || connection.helloOk ? 'hello' : LEGACY_HELLO_COMMAND;
564
- // eslint-disable-next-line github/no-then
562
+
565
563
  connection.command(ns('admin.$cmd'), { [commandName]: 1 }, undefined).then(
566
564
  () => this.measureAndReschedule(start),
567
565
  () => {
@@ -453,7 +453,7 @@ export class Server extends TypedEventEmitter<ServerEvents> {
453
453
  } else {
454
454
  if (
455
455
  (isRetryableWritesEnabled(this.topology) || isTransactionCommand(cmd)) &&
456
- needsRetryableWriteLabel(error, maxWireVersion(this)) &&
456
+ needsRetryableWriteLabel(error, maxWireVersion(this), this.description.type) &&
457
457
  !inActiveTransaction(session, cmd)
458
458
  ) {
459
459
  error.addErrorLabel(MongoErrorLabel.RetryableWriteError);
@@ -1,8 +1,7 @@
1
1
  import { type Document, Long, type ObjectId } from '../bson';
2
2
  import { type MongoError, MongoRuntimeError } from '../error';
3
3
  import { arrayStrictEqual, compareObjectId, errorStrictEqual, HostAddress, now } from '../utils';
4
- import type { ClusterTime } from './common';
5
- import { ServerType } from './common';
4
+ import { type ClusterTime, ServerType } from './common';
6
5
 
7
6
  const WRITABLE_SERVER_TYPES = new Set<ServerType>([
8
7
  ServerType.RSPrimary,
@@ -262,15 +261,15 @@ export function compareTopologyVersion(
262
261
  typeof currentTv.counter === 'bigint'
263
262
  ? Long.fromBigInt(currentTv.counter)
264
263
  : Long.isLong(currentTv.counter)
265
- ? currentTv.counter
266
- : Long.fromNumber(currentTv.counter);
264
+ ? currentTv.counter
265
+ : Long.fromNumber(currentTv.counter);
267
266
 
268
267
  const newCounter =
269
268
  typeof newTv.counter === 'bigint'
270
269
  ? Long.fromBigInt(newTv.counter)
271
270
  : Long.isLong(newTv.counter)
272
- ? newTv.counter
273
- : Long.fromNumber(newTv.counter);
271
+ ? newTv.counter
272
+ : Long.fromNumber(newTv.counter);
274
273
 
275
274
  return currentCounter.compare(newCounter);
276
275
  }
@@ -95,7 +95,6 @@ export class SrvPoller extends TypedEventEmitter<SrvPollerEvents> {
95
95
  }
96
96
 
97
97
  this._timeout = setTimeout(() => {
98
- // eslint-disable-next-line github/no-then
99
98
  this._poll().then(undefined, squashError);
100
99
  }, this.intervalMS);
101
100
  }
@@ -117,7 +116,7 @@ export class SrvPoller extends TypedEventEmitter<SrvPollerEvents> {
117
116
 
118
117
  try {
119
118
  srvRecords = await dns.promises.resolveSrv(this.srvAddress);
120
- } catch (dnsError) {
119
+ } catch {
121
120
  this.failure();
122
121
  return;
123
122
  }