Skip to content

Commit

Permalink
Merge pull request #31174 from BrentWheeldon/bmw-db-load-deadlock-5-1
Browse files Browse the repository at this point in the history
Backport prevent deadlocks with load interlock and DB lock to 5-1-stable
  • Loading branch information
rafaelfranca committed Nov 17, 2017
2 parents ce0787f + eccc533 commit 567f703
Show file tree
Hide file tree
Showing 3 changed files with 74 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
require "active_record/connection_adapters/sql_type_metadata"
require "active_record/connection_adapters/abstract/schema_dumper"
require "active_record/connection_adapters/abstract/schema_creation"
require "active_support/concurrency/load_interlock_aware_monitor"
require "arel/collectors/bind"
require "arel/collectors/sql_string"

Expand Down Expand Up @@ -105,7 +106,7 @@ def initialize(connection, logger = nil, config = {}) # :nodoc:
@schema_cache = SchemaCache.new self
@quoted_column_names, @quoted_table_names = {}, {}
@visitor = arel_visitor
@lock = Monitor.new
@lock = ActiveSupport::Concurrency::LoadInterlockAwareMonitor.new

if self.class.type_cast_config_to_boolean(config.fetch(:prepared_statements) { true })
@prepared_statements = true
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# frozen_string_literal: true

require "monitor"

module ActiveSupport
module Concurrency
# A monitor that will permit dependency loading while blocked waiting for
# the lock.
class LoadInterlockAwareMonitor < Monitor
# Enters an exclusive section, but allows dependency loading while blocked
def mon_enter
mon_try_enter ||
ActiveSupport::Dependencies.interlock.permit_concurrent_loads { super }
end
end
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
# frozen_string_literal: true

require "abstract_unit"
require "concurrent/atomic/count_down_latch"
require "active_support/concurrency/load_interlock_aware_monitor"

module ActiveSupport
module Concurrency
class LoadInterlockAwareMonitorTest < ActiveSupport::TestCase
def setup
@monitor = ActiveSupport::Concurrency::LoadInterlockAwareMonitor.new
end

def test_entering_with_no_blocking
assert @monitor.mon_enter
end

def test_entering_with_blocking
load_interlock_latch = Concurrent::CountDownLatch.new
monitor_latch = Concurrent::CountDownLatch.new

able_to_use_monitor = false
able_to_load = false

thread_with_load_interlock = Thread.new do
ActiveSupport::Dependencies.interlock.running do
load_interlock_latch.count_down
monitor_latch.wait

@monitor.synchronize do
able_to_use_monitor = true
end
end
end

thread_with_monitor_lock = Thread.new do
@monitor.synchronize do
monitor_latch.count_down
load_interlock_latch.wait

ActiveSupport::Dependencies.interlock.loading do
able_to_load = true
end
end
end

thread_with_load_interlock.join
thread_with_monitor_lock.join

assert able_to_use_monitor
assert able_to_load
end
end
end
end

0 comments on commit 567f703

Please sign in to comment.