@@ -20,7 +20,7 @@ import { ReadConcern, type ReadConcernLike } from '../read_concern';
2020import { ReadPreference , type ReadPreferenceLike } from '../read_preference' ;
2121import { type AsyncDisposable , configureResourceManagement } from '../resource_management' ;
2222import type { Server } from '../sdam/server' ;
23- import { ClientSession , maybeClearPinnedConnection } from '../sessions' ;
23+ import { type ClientSession , maybeClearPinnedConnection } from '../sessions' ;
2424import { type CSOTTimeoutContext , type Timeout , TimeoutContext } from '../timeout' ;
2525import {
2626 addAbortListener ,
@@ -227,7 +227,7 @@ export abstract class AbstractCursor<
227227 /** @internal */
228228 private cursorId : Long | null ;
229229 /** @internal */
230- private cursorSession : ClientSession ;
230+ private cursorSession : ClientSession | null ;
231231 /** @internal */
232232 private selectedServer ?: Server ;
233233 /** @internal */
@@ -352,11 +352,7 @@ export abstract class AbstractCursor<
352352 this . cursorOptions . maxAwaitTimeMS = options . maxAwaitTimeMS ;
353353 }
354354
355- if ( options . session instanceof ClientSession ) {
356- this . cursorSession = options . session ;
357- } else {
358- this . cursorSession = this . cursorClient . startSession ( { owner : this , explicit : false } ) ;
359- }
355+ this . cursorSession = options . session ?? null ;
360356
361357 this . deserializationOptions = {
362358 ...this . cursorOptions ,
@@ -413,7 +409,7 @@ export abstract class AbstractCursor<
413409 }
414410
415411 /** @internal */
416- get session ( ) : ClientSession {
412+ get session ( ) : ClientSession | null {
417413 return this . cursorSession ;
418414 }
419415
@@ -877,11 +873,12 @@ export abstract class AbstractCursor<
877873 this . trackCursor ( ) ;
878874
879875 // We only want to end this session if we created it, and it hasn't ended yet
880- if ( this . cursorSession . explicit === false ) {
876+ if ( this . cursorSession ? .explicit === false ) {
881877 if ( ! this . cursorSession . hasEnded ) {
882878 this . cursorSession . endSession ( ) . then ( undefined , squashError ) ;
883879 }
884- this . cursorSession = this . cursorClient . startSession ( { owner : this , explicit : false } ) ;
880+
881+ this . cursorSession = null ;
885882 }
886883 }
887884
@@ -907,6 +904,13 @@ export abstract class AbstractCursor<
907904 'Unexpected null selectedServer. A cursor creating command should have set this'
908905 ) ;
909906 }
907+
908+ if ( this . cursorSession == null ) {
909+ throw new MongoRuntimeError (
910+ 'Unexpected null session. A cursor creating command should have set this'
911+ ) ;
912+ }
913+
910914 const getMoreOptions = {
911915 ...this . cursorOptions ,
912916 session : this . cursorSession ,
@@ -941,6 +945,7 @@ export abstract class AbstractCursor<
941945 ) ;
942946 }
943947 try {
948+ this . cursorSession ??= this . cursorClient . startSession ( { owner : this , explicit : false } ) ;
944949 const state = await this . _initialize ( this . cursorSession ) ;
945950 // Set omitMaxTimeMS to the value needed for subsequent getMore calls
946951 this . cursorOptions . omitMaxTimeMS = this . cursorOptions . timeoutMS != null ;
@@ -1032,41 +1037,57 @@ export abstract class AbstractCursor<
10321037 return this . timeoutContext ?. refreshed ( ) ;
10331038 }
10341039 } ;
1035- try {
1036- if (
1037- ! this . isKilled &&
1038- this . cursorId &&
1039- ! this . cursorId . isZero ( ) &&
1040- this . cursorNamespace &&
1041- this . selectedServer &&
1042- ! this . cursorSession . hasEnded
1043- ) {
1044- this . isKilled = true ;
1045- const cursorId = this . cursorId ;
1046- this . cursorId = Long . ZERO ;
1047-
1048- await executeOperation (
1049- this . cursorClient ,
1050- new KillCursorsOperation ( cursorId , this . cursorNamespace , this . selectedServer , {
1051- session : this . cursorSession
1052- } ) ,
1053- timeoutContextForKillCursors ( )
1054- ) ;
1040+
1041+ const withEmitClose = async ( fn : ( ) => Promise < void > ) => {
1042+ try {
1043+ await fn ( ) ;
1044+ } finally {
1045+ this . emitClose ( ) ;
10551046 }
1056- } catch ( error ) {
1057- squashError ( error ) ;
1058- } finally {
1047+ } ;
1048+
1049+ const close = async ( ) => {
1050+ // if no session has been defined on the cursor, the cursor was never initialized
1051+ // or the cursor was re-wound and never re-iterated. In either case, we
1052+ // 1. do not need to end the session (there is no session after all)
1053+ // 2. do not need to kill the cursor server-side
1054+ const session = this . cursorSession ;
1055+ if ( ! session ) return ;
1056+
10591057 try {
1060- if ( this . cursorSession ?. owner === this ) {
1061- await this . cursorSession . endSession ( { error } ) ;
1062- }
1063- if ( ! this . cursorSession ?. inTransaction ( ) ) {
1064- maybeClearPinnedConnection ( this . cursorSession , { error } ) ;
1058+ if (
1059+ ! this . isKilled &&
1060+ this . cursorId &&
1061+ ! this . cursorId . isZero ( ) &&
1062+ this . cursorNamespace &&
1063+ this . selectedServer &&
1064+ ! session . hasEnded
1065+ ) {
1066+ this . isKilled = true ;
1067+ const cursorId = this . cursorId ;
1068+ this . cursorId = Long . ZERO ;
1069+
1070+ await executeOperation (
1071+ this . cursorClient ,
1072+ new KillCursorsOperation ( cursorId , this . cursorNamespace , this . selectedServer , {
1073+ session
1074+ } ) ,
1075+ timeoutContextForKillCursors ( )
1076+ ) ;
10651077 }
1078+ } catch ( error ) {
1079+ squashError ( error ) ;
10661080 } finally {
1067- this . emitClose ( ) ;
1081+ if ( session . owner === this ) {
1082+ await session . endSession ( { error } ) ;
1083+ }
1084+ if ( ! session . inTransaction ( ) ) {
1085+ maybeClearPinnedConnection ( session , { error } ) ;
1086+ }
10681087 }
1069- }
1088+ } ;
1089+
1090+ await withEmitClose ( close ) ;
10701091 }
10711092
10721093 /** @internal */
0 commit comments