Skip to content

Commit

Permalink
Synchronize all access to a shared database connection
Browse files Browse the repository at this point in the history
In system tests, a single database connection is shared among
all the server threads. Since connections have per-instance
mutable state, this leads to race conditions, as in rails#36763.

This patch changes the connection pool to wrap shared
connections so that all access to them is synchronized on
the connection's monitor lock.
  • Loading branch information
97jaz committed Aug 6, 2019
1 parent 7cd4148 commit 8bf907d
Showing 1 changed file with 30 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -352,6 +352,29 @@ def run
end
end

# When a pool's @lock_thread is truthy, a single database
# connection is shared among all server threads. (This is used
# for transactional fixtures in system tests.) This class is
# used to wrap the shared connection so that all access to it
# is synchronized on the connection's monitor lock.
class SharedConnection # :nodoc:
attr_reader :conn

def initialize(conn)
@conn = conn
end

def respond_to_missing?(method_name, *args)
conn.respond_to?(method_name, *args)
end

def method_missing(method_name, *args, &block)
conn.lock.synchronize do
conn.send(method_name, *args, &block)
end
end
end

include MonitorMixin
include QueryCache::ConnectionPoolConfiguration
include ConnectionAdapters::AbstractPool
Expand Down Expand Up @@ -426,7 +449,13 @@ def lock_thread=(lock_thread)
# #connection can be called any number of times; the connection is
# held in a cache keyed by a thread.
def connection
@thread_cached_conns[connection_cache_key(current_thread)] ||= checkout
conn = @thread_cached_conns[connection_cache_key(current_thread)] ||= checkout

if @lock_thread
SharedConnection.new(conn)
else
conn
end
end

# Returns true if there is an open connection being used for the current thread.
Expand Down

0 comments on commit 8bf907d

Please sign in to comment.