mirror of https://github.com/rails/rails
feat: support nested connection pinning
This commit is contained in:
parent
7dbe81710e
commit
e44cdcb7a7
|
@ -253,6 +253,7 @@ module ActiveRecord
|
|||
|
||||
@available = ConnectionLeasingQueue.new self
|
||||
@pinned_connection = nil
|
||||
@pinned_connections_depth = 0
|
||||
|
||||
@async_executor = build_async_executor
|
||||
|
||||
|
@ -311,9 +312,9 @@ module ActiveRecord
|
|||
end
|
||||
|
||||
def pin_connection!(lock_thread) # :nodoc:
|
||||
raise "There is already a pinned connection" if @pinned_connection
|
||||
@pinned_connection ||= (connection_lease&.connection || checkout)
|
||||
@pinned_connections_depth += 1
|
||||
|
||||
@pinned_connection = (connection_lease&.connection || checkout)
|
||||
# Any leased connection must be in @connections otherwise
|
||||
# some methods like #connected? won't behave correctly
|
||||
unless @connections.include?(@pinned_connection)
|
||||
|
@ -330,7 +331,10 @@ module ActiveRecord
|
|||
|
||||
clean = true
|
||||
@pinned_connection.lock.synchronize do
|
||||
connection, @pinned_connection = @pinned_connection, nil
|
||||
@pinned_connections_depth -= 1
|
||||
connection = @pinned_connection
|
||||
@pinned_connection = nil if @pinned_connections_depth.zero?
|
||||
|
||||
if connection.transaction_open?
|
||||
connection.rollback_transaction
|
||||
else
|
||||
|
@ -338,8 +342,11 @@ module ActiveRecord
|
|||
clean = false
|
||||
connection.reset!
|
||||
end
|
||||
connection.lock_thread = nil
|
||||
checkin(connection)
|
||||
|
||||
if @pinned_connection.nil?
|
||||
connection.lock_thread = nil
|
||||
checkin(connection)
|
||||
end
|
||||
end
|
||||
|
||||
clean
|
||||
|
|
|
@ -934,6 +934,56 @@ module ActiveRecord
|
|||
assert_equal false, @pool.unpin_connection!
|
||||
end
|
||||
|
||||
def test_pin_connection_nesting
|
||||
assert_instance_of NullTransaction, @pool.lease_connection.current_transaction
|
||||
@pool.pin_connection!(true)
|
||||
assert_instance_of RealTransaction, @pool.lease_connection.current_transaction
|
||||
@pool.pin_connection!(true)
|
||||
assert_instance_of SavepointTransaction, @pool.lease_connection.current_transaction
|
||||
@pool.unpin_connection!
|
||||
assert_instance_of RealTransaction, @pool.lease_connection.current_transaction
|
||||
@pool.unpin_connection!
|
||||
assert_instance_of NullTransaction, @pool.lease_connection.current_transaction
|
||||
|
||||
assert_raises(RuntimeError, match: /There isn't a pinned connection/) do
|
||||
@pool.unpin_connection!
|
||||
end
|
||||
end
|
||||
|
||||
def test_pin_connection_nesting_lock
|
||||
assert_equal ActiveSupport::Concurrency::NullLock, @pool.lease_connection.lock
|
||||
|
||||
@pool.pin_connection!(true)
|
||||
actual_lock = @pool.lease_connection.lock
|
||||
assert_not_equal ActiveSupport::Concurrency::NullLock, actual_lock
|
||||
|
||||
@pool.pin_connection!(false)
|
||||
assert_same actual_lock, @pool.lease_connection.lock
|
||||
|
||||
@pool.unpin_connection!
|
||||
assert_same actual_lock, @pool.lease_connection.lock
|
||||
|
||||
@pool.unpin_connection!
|
||||
assert_equal ActiveSupport::Concurrency::NullLock, @pool.lease_connection.lock
|
||||
end
|
||||
|
||||
def test_pin_connection_nesting_lock_inverse
|
||||
assert_equal ActiveSupport::Concurrency::NullLock, @pool.lease_connection.lock
|
||||
|
||||
@pool.pin_connection!(false)
|
||||
assert_equal ActiveSupport::Concurrency::NullLock, @pool.lease_connection.lock
|
||||
|
||||
@pool.pin_connection!(true)
|
||||
actual_lock = @pool.lease_connection.lock
|
||||
assert_not_equal ActiveSupport::Concurrency::NullLock, actual_lock
|
||||
|
||||
@pool.unpin_connection!
|
||||
assert_same actual_lock, @pool.lease_connection.lock # The lock persist until full unpin
|
||||
|
||||
@pool.unpin_connection!
|
||||
assert_equal ActiveSupport::Concurrency::NullLock, @pool.lease_connection.lock
|
||||
end
|
||||
|
||||
private
|
||||
def active_connections(pool)
|
||||
pool.connections.find_all(&:in_use?)
|
||||
|
|
Loading…
Reference in New Issue