Skip to content

Commit 04bee77

Browse files
RUBY-3824 Make Session#unpin idempotent
The transactions runner triggers Session#unpin twice for the same network error on the bulk_write code path: once from the unpin_maybe handler in Operation::Executable#do_execute, and again from the unpin_maybe handler in BulkWrite#execute_operation wrapping the same OpMsg execution. The second call tried to check the connection back into the pool that had already received it, raising "Trying to check in a connection which is not currently checked out by this pool". This regression had been hidden since the rubocop autocorrect on the runner (commit 8db6431) skipped all transactions tests; it surfaced as soon as PR 3033 re-enabled them on sharded clusters. Guard Session#unpin to return early when there is no pinned state. The single-document path is unaffected (only one unpin_maybe layer there).
1 parent c165d46 commit 04bee77

2 files changed

Lines changed: 29 additions & 0 deletions

File tree

lib/mongo/session.rb

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -936,6 +936,13 @@ def pin_to_connection(connection_global_id, connection: nil)
936936
#
937937
# @api private
938938
def unpin(connection = nil)
939+
# Idempotent: if there is no pinned state to clear, do nothing. Nested
940+
# unpin_maybe handlers (e.g. in BulkWrite#execute_operation wrapping an
941+
# OpMsg execution that already calls unpin_maybe in its own do_execute)
942+
# can call this method twice for the same error; checking the connection
943+
# back into the pool a second time would raise from the pool.
944+
return if @pinned_server.nil? && @pinned_connection.nil? && @pinned_connection_global_id.nil?
945+
939946
@pinned_server = nil
940947
@pinned_connection_global_id = nil
941948
conn = connection || @pinned_connection

spec/mongo/session_spec.rb

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -340,4 +340,26 @@
340340
session.session_id.should be_a(BSON::Document)
341341
end
342342
end
343+
344+
describe '#unpin' do
345+
context 'when called twice with the same connection' do
346+
# Nested unpin_maybe handlers in the bulk_write code path can trigger
347+
# Session#unpin to be invoked twice for the same network error: once
348+
# from Operation::Executable#do_execute and once from
349+
# BulkWrite#execute_operation. The second call must be a no-op rather
350+
# than raising "Trying to check in a connection which is not currently
351+
# checked out" from the pool.
352+
it 'does not raise on the second call' do
353+
server = authorized_client.cluster.next_primary
354+
server.with_connection do |connection|
355+
session.pin_to_server(server)
356+
session.unpin(connection)
357+
358+
expect do
359+
session.unpin(connection)
360+
end.not_to raise_error
361+
end
362+
end
363+
end
364+
end
343365
end

0 commit comments

Comments
 (0)