提交 85319e37 编写于 作者: E Eileen M. Uchitelle 提交者: eileencodes

Merge pull request #36868 from eileencodes/make-prevent-writes-threadsafe

Fix thread safety of prevent_writes
上级 fb716a65
......@@ -1005,28 +1005,33 @@ def self.discard_unowned_pools(pid_map) # :nodoc:
end
end
attr_reader :prevent_writes
def initialize
# These caches are keyed by spec.name (ConnectionSpecification#name).
@owner_to_pool = ConnectionHandler.create_owner_to_pool
@prevent_writes = false
# Backup finalizer: if the forked child never needed a pool, the above
# early discard has not occurred
ObjectSpace.define_finalizer self, ConnectionHandler.unowned_pool_finalizer(@owner_to_pool)
end
def prevent_writes # :nodoc:
Thread.current[:prevent_writes]
end
def prevent_writes=(prevent_writes) # :nodoc:
Thread.current[:prevent_writes] = prevent_writes
end
# Prevent writing to the database regardless of role.
#
# In some cases you may want to prevent writes to the database
# even if you are on a database that can write. `while_preventing_writes`
# will prevent writes to the database for the duration of the block.
def while_preventing_writes(enabled = true)
original, @prevent_writes = @prevent_writes, enabled
original, self.prevent_writes = self.prevent_writes, enabled
yield
ensure
@prevent_writes = original
self.prevent_writes = original
end
def connection_pool_list
......
......@@ -143,12 +143,12 @@ def test_preventing_writes_turns_off_for_primary_write
write = false
resolver.read do
assert ActiveRecord::Base.connected_to?(role: :writing)
assert ActiveRecord::Base.connection_handler.prevent_writes
assert_predicate ActiveRecord::Base.connection, :preventing_writes?
read = true
resolver.write do
assert ActiveRecord::Base.connected_to?(role: :writing)
assert_not ActiveRecord::Base.connection_handler.prevent_writes
assert_not_predicate ActiveRecord::Base.connection, :preventing_writes?
write = true
end
end
......@@ -157,6 +157,43 @@ def test_preventing_writes_turns_off_for_primary_write
assert read
end
def test_preventing_writes_works_in_a_threaded_environment
resolver = ActiveRecord::Middleware::DatabaseSelector::Resolver.new(@session, delay: 5.seconds)
inside_preventing = Concurrent::Event.new
finished_checking = Concurrent::Event.new
@session.update_last_write_timestamp
t1 = Thread.new do
resolver.read do
inside_preventing.wait
assert ActiveRecord::Base.connected_to?(role: :writing)
assert_predicate ActiveRecord::Base.connection, :preventing_writes?
finished_checking.set
end
end
t2 = Thread.new do
resolver.write do
assert ActiveRecord::Base.connected_to?(role: :writing)
assert_not_predicate ActiveRecord::Base.connection, :preventing_writes?
inside_preventing.set
finished_checking.wait
end
end
t3 = Thread.new do
resolver.read do
assert ActiveRecord::Base.connected_to?(role: :writing)
assert_predicate ActiveRecord::Base.connection, :preventing_writes?
end
end
t1.join
t2.join
t3.join
end
def test_read_from_replica_with_no_delay
resolver = ActiveRecord::Middleware::DatabaseSelector::Resolver.new(@session, delay: 0.seconds)
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册