@@ -91,10 +91,19 @@ def initialize(view, result, server, options = {})
91
91
@context = @options [ :context ] &.with ( connection_global_id : connection_global_id_for_context ) || fresh_context
92
92
@explicitly_closed = false
93
93
@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
95
102
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
+ )
98
107
end
99
108
end
100
109
@@ -104,6 +113,9 @@ def initialize(view, result, server, options = {})
104
113
# @api private
105
114
attr_reader :initial_result
106
115
116
+ # @api private
117
+ attr_reader :connection
118
+
107
119
# Finalize the cursor for garbage collection. Schedules this cursor to be included
108
120
# in a killCursors operation executed by the Cluster's CursorReaper.
109
121
#
@@ -315,6 +327,7 @@ def close(opts = {})
315
327
@lock . synchronize do
316
328
@explicitly_closed = true
317
329
end
330
+ check_in_connection
318
331
end
319
332
320
333
# Get the parsed collection name.
@@ -395,6 +408,7 @@ def kill_spec(connection_global_id)
395
408
connection_global_id : connection_global_id ,
396
409
server_address : server . address ,
397
410
session : @session ,
411
+ connection : @connection
398
412
)
399
413
end
400
414
@@ -464,7 +478,10 @@ def process(result)
464
478
# the @cursor_id may be zero (all results fit in the first batch).
465
479
# Thus we need to check both @cursor_id and the cursor_id of the result
466
480
# 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
468
485
@cursor_id = set_cursor_id ( result )
469
486
470
487
if result . respond_to? ( :post_batch_resume_token )
@@ -496,7 +513,12 @@ def unregister
496
513
end
497
514
498
515
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
500
522
end
501
523
502
524
# Considers the timeout mode and will either return the cursor's
@@ -545,6 +567,22 @@ def connection_global_id_for_context
545
567
@connection_global_id
546
568
end
547
569
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
548
586
end
549
587
end
550
588
0 commit comments