@@ -91,10 +91,19 @@ def initialize(view, result, server, options = {})
9191 @context = @options [ :context ] &.with ( connection_global_id : connection_global_id_for_context ) || fresh_context
9292 @explicitly_closed = false
9393 @lock = Mutex . new
94- unless closed?
94+ if server . load_balancer?
95+ # We need the connection in the cursor only in load balanced topology;
96+ # we do not need an additional reference to it otherwise.
97+ @connection = @initial_result . connection
98+ end
99+ if closed?
100+ check_in_connection
101+ else
95102 register
96- ObjectSpace . define_finalizer ( self , self . class . finalize ( kill_spec ( @connection_global_id ) ,
97- cluster ) )
103+ ObjectSpace . define_finalizer (
104+ self ,
105+ self . class . finalize ( kill_spec ( @connection_global_id ) , cluster )
106+ )
98107 end
99108 end
100109
@@ -104,6 +113,9 @@ def initialize(view, result, server, options = {})
104113 # @api private
105114 attr_reader :initial_result
106115
116+ # @api private
117+ attr_reader :connection
118+
107119 # Finalize the cursor for garbage collection. Schedules this cursor to be included
108120 # in a killCursors operation executed by the Cluster's CursorReaper.
109121 #
@@ -315,6 +327,7 @@ def close(opts = {})
315327 @lock . synchronize do
316328 @explicitly_closed = true
317329 end
330+ check_in_connection
318331 end
319332
320333 # Get the parsed collection name.
@@ -395,6 +408,7 @@ def kill_spec(connection_global_id)
395408 connection_global_id : connection_global_id ,
396409 server_address : server . address ,
397410 session : @session ,
411+ connection : @connection
398412 )
399413 end
400414
@@ -464,7 +478,10 @@ def process(result)
464478 # the @cursor_id may be zero (all results fit in the first batch).
465479 # Thus we need to check both @cursor_id and the cursor_id of the result
466480 # prior to calling unregister here.
467- unregister if !closed? && result . cursor_id == 0
481+ if !closed? && result . cursor_id == 0
482+ unregister
483+ check_in_connection
484+ end
468485 @cursor_id = set_cursor_id ( result )
469486
470487 if result . respond_to? ( :post_batch_resume_token )
@@ -496,7 +513,12 @@ def unregister
496513 end
497514
498515 def execute_operation ( op , context : nil )
499- op . execute ( @server , context : context || possibly_refreshed_context )
516+ op_context = context || possibly_refreshed_context
517+ if @connection . nil?
518+ op . execute ( @server , context : op_context )
519+ else
520+ op . execute_with_connection ( @connection , context : op_context )
521+ end
500522 end
501523
502524 # Considers the timeout mode and will either return the cursor's
@@ -545,6 +567,22 @@ def connection_global_id_for_context
545567 @connection_global_id
546568 end
547569 end
570+
571+ # Returns the connection that was used to create the cursor back to the
572+ # corresponding connection pool.
573+ #
574+ # In a load balanced topology cursors must use the same connection for the
575+ # initial and all subsequent operations. Therefore, the connection is not
576+ # checked into the pool after the initial operation is completed, but
577+ # only when the cursor is drained.
578+ def check_in_connection
579+ # Connection nil means the connection has been already checked in.
580+ return if @connection . nil?
581+ return unless @connection . server . load_balancer?
582+
583+ @connection . connection_pool . check_in ( @connection )
584+ @connection = nil
585+ end
548586 end
549587end
550588
0 commit comments