mirror of
https://github.com/rails/rails.git
synced 2022-11-09 12:12:34 -05:00
Add ability to unset preventing writes
Previously if an app attempts to do a write inside a read request it will be impossilbe to switch back to writing to the primary. This PR adds an argument to the `while_preventing_writes` so that we can make sure to turn it off if we're doing a write on a primary. Fixes #36830 Co-authored-by: John Crepezzi <john.crepezzi@gmail.com>
This commit is contained in:
parent
f3c68c59ed
commit
f2de448106
3 changed files with 43 additions and 7 deletions
|
@ -1020,8 +1020,8 @@ module ActiveRecord
|
||||||
# In some cases you may want to prevent writes to the database
|
# 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`
|
# 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.
|
# will prevent writes to the database for the duration of the block.
|
||||||
def while_preventing_writes
|
def while_preventing_writes(enabled = true)
|
||||||
original, @prevent_writes = @prevent_writes, true
|
original, @prevent_writes = @prevent_writes, enabled
|
||||||
yield
|
yield
|
||||||
ensure
|
ensure
|
||||||
@prevent_writes = original
|
@prevent_writes = original
|
||||||
|
|
|
@ -46,7 +46,7 @@ module ActiveRecord
|
||||||
private
|
private
|
||||||
def read_from_primary(&blk)
|
def read_from_primary(&blk)
|
||||||
ActiveRecord::Base.connected_to(role: ActiveRecord::Base.writing_role) do
|
ActiveRecord::Base.connected_to(role: ActiveRecord::Base.writing_role) do
|
||||||
ActiveRecord::Base.connection_handler.while_preventing_writes do
|
ActiveRecord::Base.connection_handler.while_preventing_writes(true) do
|
||||||
instrumenter.instrument("database_selector.active_record.read_from_primary") do
|
instrumenter.instrument("database_selector.active_record.read_from_primary") do
|
||||||
yield
|
yield
|
||||||
end
|
end
|
||||||
|
@ -64,6 +64,7 @@ module ActiveRecord
|
||||||
|
|
||||||
def write_to_primary(&blk)
|
def write_to_primary(&blk)
|
||||||
ActiveRecord::Base.connected_to(role: ActiveRecord::Base.writing_role) do
|
ActiveRecord::Base.connected_to(role: ActiveRecord::Base.writing_role) do
|
||||||
|
ActiveRecord::Base.connection_handler.while_preventing_writes(false) do
|
||||||
instrumenter.instrument("database_selector.active_record.wrote_to_primary") do
|
instrumenter.instrument("database_selector.active_record.wrote_to_primary") do
|
||||||
yield
|
yield
|
||||||
ensure
|
ensure
|
||||||
|
@ -71,6 +72,7 @@ module ActiveRecord
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def read_from_primary?
|
def read_from_primary?
|
||||||
!time_since_last_write_ok?
|
!time_since_last_write_ok?
|
||||||
|
|
|
@ -123,6 +123,40 @@ module ActiveRecord
|
||||||
assert read
|
assert read
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_preventing_writes_turns_off_for_primary_write
|
||||||
|
resolver = ActiveRecord::Middleware::DatabaseSelector::Resolver.new(@session, delay: 5.seconds)
|
||||||
|
|
||||||
|
# Session should start empty
|
||||||
|
assert_nil @session_store[:last_write]
|
||||||
|
|
||||||
|
called = false
|
||||||
|
resolver.write do
|
||||||
|
assert ActiveRecord::Base.connected_to?(role: :writing)
|
||||||
|
called = true
|
||||||
|
end
|
||||||
|
assert called
|
||||||
|
|
||||||
|
# and be populated by the last write time
|
||||||
|
assert @session_store[:last_write]
|
||||||
|
|
||||||
|
read = false
|
||||||
|
write = false
|
||||||
|
resolver.read do
|
||||||
|
assert ActiveRecord::Base.connected_to?(role: :writing)
|
||||||
|
assert ActiveRecord::Base.connection_handler.prevent_writes
|
||||||
|
read = true
|
||||||
|
|
||||||
|
resolver.write do
|
||||||
|
assert ActiveRecord::Base.connected_to?(role: :writing)
|
||||||
|
assert_not ActiveRecord::Base.connection_handler.prevent_writes
|
||||||
|
write = true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
assert write
|
||||||
|
assert read
|
||||||
|
end
|
||||||
|
|
||||||
def test_read_from_replica_with_no_delay
|
def test_read_from_replica_with_no_delay
|
||||||
resolver = ActiveRecord::Middleware::DatabaseSelector::Resolver.new(@session, delay: 0.seconds)
|
resolver = ActiveRecord::Middleware::DatabaseSelector::Resolver.new(@session, delay: 0.seconds)
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue