Skip to content

Commit 94fc536

Browse files
committed
Prune dead thread from connection pool caches on reap
Otherwise they could linger around and leak memory if a user checkout connections from short lived fibers or threads. The undocumented `connection_cache_key` hook point is eliminated because it was essentially add to allow the connection pool to be fiber based rather than thread based, which is now supported out of the box.
1 parent 85742ce commit 94fc536

File tree

2 files changed

+23
-16
lines changed

2 files changed

+23
-16
lines changed

activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -176,13 +176,13 @@ def initialize(pool_config)
176176
# #connection can be called any number of times; the connection is
177177
# held in a cache keyed by a thread.
178178
def connection
179-
@thread_cached_conns[connection_cache_key(ActiveSupport::IsolatedExecutionState.context)] ||= checkout
179+
@thread_cached_conns[ActiveSupport::IsolatedExecutionState.context] ||= checkout
180180
end
181181

182182
def pin_connection!(lock_thread) # :nodoc:
183183
raise "There is already a pinned connection" if @pinned_connection
184184

185-
@pinned_connection = (@thread_cached_conns[connection_cache_key(ActiveSupport::IsolatedExecutionState.context)] || checkout)
185+
@pinned_connection = (@thread_cached_conns[ActiveSupport::IsolatedExecutionState.context] || checkout)
186186
# Any leased connection must be in @connections otherwise
187187
# some methods like #connected? won't behave correctly
188188
unless @connections.include?(@pinned_connection)
@@ -226,7 +226,7 @@ def connection_class # :nodoc:
226226
# #connection or #with_connection methods. Connections obtained through
227227
# #checkout will not be detected by #active_connection?
228228
def active_connection?
229-
@thread_cached_conns[connection_cache_key(ActiveSupport::IsolatedExecutionState.context)]
229+
@thread_cached_conns[ActiveSupport::IsolatedExecutionState.context]
230230
end
231231

232232
# Signal that the thread is finished with the current connection.
@@ -237,7 +237,7 @@ def active_connection?
237237
# #connection or #with_connection methods, connections obtained through
238238
# #checkout will not be automatically released.
239239
def release_connection(owner_thread = ActiveSupport::IsolatedExecutionState.context)
240-
if conn = @thread_cached_conns.delete(connection_cache_key(owner_thread))
240+
if conn = @thread_cached_conns.delete(owner_thread)
241241
checkin conn
242242
end
243243
end
@@ -252,7 +252,7 @@ def release_connection(owner_thread = ActiveSupport::IsolatedExecutionState.cont
252252
# connection will be properly returned to the pool by the code that checked
253253
# it out.
254254
def with_connection
255-
unless conn = @thread_cached_conns[connection_cache_key(ActiveSupport::IsolatedExecutionState.context)]
255+
unless conn = @thread_cached_conns[ActiveSupport::IsolatedExecutionState.context]
256256
conn = connection
257257
fresh_connection = true
258258
end
@@ -471,6 +471,8 @@ def reap
471471
remove conn
472472
end
473473
end
474+
475+
prune_thread_cache
474476
end
475477

476478
# Disconnect all connections that have been idle for at least
@@ -558,15 +560,6 @@ def bulk_make_new_connections(num_new_conns_needed)
558560
end
559561
end
560562

561-
#--
562-
# From the discussion on GitHub:
563-
# https://github.com/rails/rails/pull/14938#commitcomment-6601951
564-
# This hook-in method allows for easier monkey-patching fixes needed by
565-
# JRuby users that use Fibers.
566-
def connection_cache_key(thread)
567-
thread
568-
end
569-
570563
# Take control of all existing connections so a "group" action such as
571564
# reload/disconnect can be performed safely. It is no longer enough to
572565
# wrap it in +synchronize+ because some pool's actions are allowed
@@ -710,10 +703,17 @@ def acquire_connection(checkout_timeout)
710703
#--
711704
# if owner_thread param is omitted, this must be called in synchronize block
712705
def remove_connection_from_thread_cache(conn, owner_thread = conn.owner)
713-
@thread_cached_conns.delete_pair(connection_cache_key(owner_thread), conn)
706+
@thread_cached_conns.delete_pair(owner_thread, conn)
714707
end
715708
alias_method :release, :remove_connection_from_thread_cache
716709

710+
def prune_thread_cache
711+
dead_threads = @thread_cached_conns.keys.reject(&:alive?)
712+
dead_threads.each do |dead_thread|
713+
@thread_cached_conns.delete(dead_thread)
714+
end
715+
end
716+
717717
def new_connection
718718
connection = db_config.new_connection
719719
connection.pool = self

activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -134,8 +134,15 @@ def query_cache_enabled
134134
end
135135

136136
private
137+
def prune_thread_cache
138+
dead_threads = @thread_query_caches.keys.reject(&:alive?)
139+
dead_threads.each do |dead_thread|
140+
@thread_query_caches.delete(dead_thread)
141+
end
142+
end
143+
137144
def query_cache
138-
@thread_query_caches.compute_if_absent(connection_cache_key(ActiveSupport::IsolatedExecutionState.context)) do
145+
@thread_query_caches.compute_if_absent(ActiveSupport::IsolatedExecutionState.context) do
139146
Store.new(@query_cache_max_size)
140147
end
141148
end

0 commit comments

Comments
 (0)