1
0
Fork 0
mirror of https://github.com/rails/rails.git synced 2022-11-09 12:12:34 -05:00

Merge pull request #25707 from matthewd/double-reap

Don't reap connections that have already been reassigned
This commit is contained in:
Matthew Draper 2016-07-07 07:40:26 +09:30 committed by GitHub
commit aeba05d389
4 changed files with 50 additions and 11 deletions

View file

@ -1,3 +1,10 @@
* Ensure concurrent invocations of the connection reaper cannot allocate the
same connection to two threads.
Fixes #25585.
*Matthew Draper*
* Inspecting an object with an associated array of over 10 elements no longer * Inspecting an object with an associated array of over 10 elements no longer
truncates the array, preventing `inspect` from looping infinitely in some truncates the array, preventing `inspect` from looping infinitely in some
cases. cases.

View file

@ -415,7 +415,10 @@ module ActiveRecord
with_exclusively_acquired_all_connections(raise_on_acquisition_timeout) do with_exclusively_acquired_all_connections(raise_on_acquisition_timeout) do
synchronize do synchronize do
@connections.each do |conn| @connections.each do |conn|
if conn.in_use?
conn.steal!
checkin conn checkin conn
end
conn.disconnect! conn.disconnect!
end end
@connections = [] @connections = []
@ -447,7 +450,10 @@ module ActiveRecord
with_exclusively_acquired_all_connections(raise_on_acquisition_timeout) do with_exclusively_acquired_all_connections(raise_on_acquisition_timeout) do
synchronize do synchronize do
@connections.each do |conn| @connections.each do |conn|
if conn.in_use?
conn.steal!
checkin conn checkin conn
end
conn.disconnect! if conn.requires_reloading? conn.disconnect! if conn.requires_reloading?
end end
@connections.delete_if(&:requires_reloading?) @connections.delete_if(&:requires_reloading?)
@ -556,11 +562,12 @@ module ActiveRecord
stale_connections = synchronize do stale_connections = synchronize do
@connections.select do |conn| @connections.select do |conn|
conn.in_use? && !conn.owner.alive? conn.in_use? && !conn.owner.alive?
end.each do |conn|
conn.steal!
end end
end end
stale_connections.each do |conn| stale_connections.each do |conn|
synchronize do
if conn.active? if conn.active?
conn.reset! conn.reset!
checkin conn checkin conn
@ -569,7 +576,6 @@ module ActiveRecord
end end
end end
end end
end
def num_waiting_in_queue # :nodoc: def num_waiting_in_queue # :nodoc:
@available.num_waiting @available.num_waiting

View file

@ -184,7 +184,30 @@ module ActiveRecord
# this method must only be called while holding connection pool's mutex # this method must only be called while holding connection pool's mutex
def expire def expire
if in_use?
if @owner != Thread.current
raise ActiveRecordError, "Cannot expire connection, " <<
"it is owned by a different thread: #{@owner}. " <<
"Current thread: #{Thread.current}."
end
@owner = nil @owner = nil
else
raise ActiveRecordError, 'Cannot expire connection, it is not currently leased.'
end
end
# this method must only be called while holding connection pool's mutex (and a desire for segfaults)
def steal! # :nodoc:
if in_use?
if @owner != Thread.current
pool.send :remove_connection_from_thread_cache, self, @owner
@owner = Thread.current
end
else
raise ActiveRecordError, 'Cannot steal connection, it is not currently leased.'
end
end end
def unprepared_statement def unprepared_statement

View file

@ -151,7 +151,7 @@ module ActiveRecord
assert_equal 1, active_connections(@pool).size assert_equal 1, active_connections(@pool).size
ensure ensure
@pool.connections.each(&:close) @pool.connections.each { |conn| conn.close if conn.in_use? }
end end
def test_remove_connection def test_remove_connection
@ -432,6 +432,9 @@ module ActiveRecord
Thread.new { @pool.send(group_action_method) }.join Thread.new { @pool.send(group_action_method) }.join
# assert connection has been forcefully taken away from us # assert connection has been forcefully taken away from us
assert_not @pool.active_connection? assert_not @pool.active_connection?
# make a new connection for with_connection to clean up
@pool.connection
end end
end end
end end