@@ -24,7 +24,7 @@ import {
2424} from '../sdam/server_selection' ;
2525import type { Topology } from '../sdam/topology' ;
2626import type { ClientSession } from '../sessions' ;
27- import { Timeout } from '../timeout' ;
27+ import { TimeoutContext } from '../timeout' ;
2828import { squashError , supportsRetryableWrites } from '../utils' ;
2929import { AbstractOperation , Aspect } from './operation' ;
3030
@@ -88,6 +88,12 @@ export async function executeOperation<
8888 ) ;
8989 }
9090
91+ timeoutContext ??= TimeoutContext . create ( {
92+ serverSelectionTimeoutMS : client . s . options . serverSelectionTimeoutMS ,
93+ waitQueueTimeoutMS : client . s . options . waitQueueTimeoutMS ,
94+ timeoutMS : operation . options . timeoutMS
95+ } ) ;
96+
9197 const readPreference = operation . readPreference ?? ReadPreference . primary ;
9298 const inTransaction = ! ! session ?. inTransaction ( ) ;
9399
@@ -200,18 +206,31 @@ async function tryOperation<
200206 selector = readPreference ;
201207 }
202208
203- const timeout = operation . timeoutMS != null ? Timeout . expires ( operation . timeoutMS ) : undefined ;
204- operation . timeout = timeout ;
205-
206209 const server = await topology . selectServer ( selector , {
207210 session,
208211 operationName : operation . commandName ,
209- timeout
212+ timeoutContext
210213 } ) ;
211214
212- const hasReadAspect = operation . hasAspect ( Aspect . READ_OPERATION ) ;
213- const hasWriteAspect = operation . hasAspect ( Aspect . WRITE_OPERATION ) ;
214- const inTransaction = session ?. inTransaction ( ) ?? false ;
215+ if ( session == null ) {
216+ // No session also means it is not retryable, early exit
217+ return await operation . execute ( server , undefined , timeoutContext ) ;
218+ }
219+
220+ if ( ! operation . hasAspect ( Aspect . RETRYABLE ) ) {
221+ // non-retryable operation, early exit
222+ try {
223+ return await operation . execute ( server , session , timeoutContext ) ;
224+ } finally {
225+ if ( session ?. owner != null && session . owner === owner ) {
226+ try {
227+ await session . endSession ( ) ;
228+ } catch ( error ) {
229+ squashError ( error ) ;
230+ }
231+ }
232+ }
233+ }
215234
216235 const willRetryRead = topology . s . options . retryReads && ! inTransaction && operation . canRetryRead ;
217236
@@ -231,42 +250,16 @@ async function tryOperation<
231250 session . incrementTransactionNumber ( ) ;
232251 }
233252
234- // TODO(NODE-6231): implement infinite retry within CSOT timeout here
235- const maxTries = willRetry ? 2 : 1 ;
236- let previousOperationError : MongoError | undefined ;
237- let previousServer : ServerDescription | undefined ;
238-
239- // TODO(NODE-6231): implement infinite retry within CSOT timeout here
240- for ( let tries = 0 ; tries < maxTries ; tries ++ ) {
241- if ( previousOperationError ) {
242- if ( hasWriteAspect && previousOperationError . code === MMAPv1_RETRY_WRITES_ERROR_CODE ) {
243- throw new MongoServerError ( {
244- message : MMAPv1_RETRY_WRITES_ERROR_MESSAGE ,
245- errmsg : MMAPv1_RETRY_WRITES_ERROR_MESSAGE ,
246- originalError : previousOperationError
247- } ) ;
248- }
249-
250- if ( hasWriteAspect && ! isRetryableWriteError ( previousOperationError ) )
251- throw previousOperationError ;
252-
253- if ( hasReadAspect && ! isRetryableReadError ( previousOperationError ) )
254- throw previousOperationError ;
255-
256- if (
257- previousOperationError instanceof MongoNetworkError &&
258- operation . hasAspect ( Aspect . CURSOR_CREATING ) &&
259- session != null &&
260- session . isPinned &&
261- ! session . inTransaction ( )
262- ) {
263- session . unpin ( { force : true , forceClear : true } ) ;
264- }
265-
266- server = await topology . selectServer ( selector , {
253+ try {
254+ return await operation . execute ( server , session , timeoutContext ) ;
255+ } catch ( operationError ) {
256+ if ( willRetry && operationError instanceof MongoError ) {
257+ return await retryOperation ( operation , operationError , {
267258 session,
268- operationName : operation . commandName ,
269- previousServer
259+ topology,
260+ selector,
261+ previousServer : server . description ,
262+ timeoutContext
270263 } ) ;
271264
272265 if ( hasWriteAspect && ! supportsRetryableWrites ( server ) ) {
@@ -276,18 +269,22 @@ async function tryOperation<
276269 }
277270 }
278271
279- try {
280- return await operation . execute ( server , session , timeoutContext ) ;
281- } catch ( operationError ) {
282- if ( ! ( operationError instanceof MongoError ) ) throw operationError ;
272+ /** @internal */
273+ type RetryOptions = {
274+ session : ClientSession ;
275+ topology : Topology ;
276+ selector : ReadPreference | ServerSelector ;
277+ previousServer : ServerDescription ;
278+ timeoutContext : TimeoutContext ;
279+ } ;
283280
284281async function retryOperation <
285282 T extends AbstractOperation < TResult > ,
286283 TResult = ResultTypeFromOperation < T >
287284> (
288285 operation : T ,
289286 originalError : MongoError ,
290- { session, topology, selector, previousServer } : RetryOptions
287+ { session, topology, selector, previousServer, timeoutContext } : RetryOptions
291288) : Promise < TResult > {
292289 const isWriteOperation = operation . hasAspect ( Aspect . WRITE_OPERATION ) ;
293290 const isReadOperation = operation . hasAspect ( Aspect . READ_OPERATION ) ;
@@ -323,9 +320,9 @@ async function retryOperation<
323320 // select a new server, and attempt to retry the operation
324321 const server = await topology . selectServer ( selector , {
325322 session,
326- timeout : operation . timeout ,
327323 operationName : operation . commandName ,
328- previousServer
324+ previousServer,
325+ timeoutContext
329326 } ) ;
330327
331328 if ( isWriteOperation && ! supportsRetryableWrites ( server ) ) {
@@ -335,7 +332,7 @@ async function retryOperation<
335332 }
336333
337334 try {
338- return await operation . execute ( server , session ) ;
335+ return await operation . execute ( server , session , timeoutContext ) ;
339336 } catch ( retryError ) {
340337 if (
341338 retryError instanceof MongoError &&
0 commit comments