1
0
Fork 0
mirror of https://github.com/rails/rails.git synced 2022-11-09 12:12:34 -05:00

Revert "Disable automatic write protection on replicas"

This reverts commit 951deecc52.

This change prevents applications from testing replicas and would
require explicitly setting `prevent_writes` when connecting to reading
roles in `connected_to`. For now we'll revert this until there's a
longer term fix in place
This commit is contained in:
Leo Correa 2021-06-25 11:35:51 -04:00
parent 6ae78e964d
commit 69bc201929
No known key found for this signature in database
GPG key ID: 73232F06E7D320B7
11 changed files with 34 additions and 38 deletions

View file

@ -16,19 +16,6 @@
*Jorge Manrubia*
* Disable automatic write protection on replicas.
Write protection is no longer automatically enabled for replicas.
You can manually prevent writes in your app with
`while_preventing_writes`. To automatically disable all writes on
your replica, configure the database user you are using to connect
to your replica to prevent writes. How you configure this is
specific to which database adapter you are using, but it usually
involves only granting the database user permission to do `SELECT`
queries.
*Adam Hess*
* The MySQL adapter now cast numbers and booleans bind parameters to to string for safety reasons.
When comparing a string and a number in a query, MySQL convert the string to a number. So for

View file

@ -131,12 +131,15 @@ module ActiveRecord
# Determines whether writes are currently being prevented.
#
# Returns true if the connection is a replica.
#
# If the application is using legacy handling, returns
# true if +connection_handler.prevent_writes+ is set.
#
# If the application is using the new connection handling
# will return true based on +current_preventing_writes+.
def preventing_writes?
return true if replica?
return ActiveRecord::Base.connection_handler.prevent_writes if ActiveRecord.legacy_connection_handling
return false if connection_klass.nil?

View file

@ -184,6 +184,8 @@ module ActiveRecord
raise NotImplementedError, "connected_to_many can only be called on ActiveRecord::Base."
end
prevent_writes = true if role == ActiveRecord.reading_role
connected_to_stack << { role: role, shard: shard, prevent_writes: prevent_writes, klasses: classes }
yield
ensure
@ -202,6 +204,8 @@ module ActiveRecord
raise NotImplementedError, "`connecting_to` is not available with `legacy_connection_handling`."
end
prevent_writes = true if role == ActiveRecord.reading_role
self.connected_to_stack << { role: role, shard: shard, prevent_writes: prevent_writes, klasses: [self] }
end
@ -352,6 +356,8 @@ module ActiveRecord
end
def with_role_and_shard(role, shard, prevent_writes)
prevent_writes = true if role == ActiveRecord.reading_role
if ActiveRecord.legacy_connection_handling
with_handler(role.to_sym) do
connection_handler.while_preventing_writes(prevent_writes) do

View file

@ -58,7 +58,7 @@ module ActiveRecord
end
def read_from_replica(&blk)
ActiveRecord::Base.connected_to(role: ActiveRecord.reading_role) do
ActiveRecord::Base.connected_to(role: ActiveRecord.reading_role, prevent_writes: true) do
instrumenter.instrument("database_selector.active_record.read_from_replica") do
yield
end

View file

@ -1721,7 +1721,7 @@ class BasicsTest < ActiveRecord::TestCase
SecondAbstractClass.connecting_to(role: :reading)
assert SecondAbstractClass.connected_to?(role: :reading)
assert_not SecondAbstractClass.current_preventing_writes
assert SecondAbstractClass.current_preventing_writes
ensure
ActiveRecord::Base.connected_to_stack.pop
end
@ -1777,24 +1777,24 @@ class BasicsTest < ActiveRecord::TestCase
end
end
test "#connected_to_many does not sets prevent_writes if role is reading" do
test "#connected_to_many sets prevent_writes if role is reading" do
ActiveRecord::Base.connected_to_many([SecondAbstractClass], role: :reading) do
assert_not SecondAbstractClass.current_preventing_writes
assert SecondAbstractClass.current_preventing_writes
assert_not ActiveRecord::Base.current_preventing_writes
end
end
test "#connected_to_many with a single argument for classes does not set prevent_writes" do
test "#connected_to_many with a single argument for classes" do
ActiveRecord::Base.connected_to_many(SecondAbstractClass, role: :reading) do
assert_not SecondAbstractClass.current_preventing_writes
assert SecondAbstractClass.current_preventing_writes
assert_not ActiveRecord::Base.current_preventing_writes
end
end
test "#connected_to_many with a multiple classes without brackets does not prevent_writes" do
test "#connected_to_many with a multiple classes without brackets works" do
ActiveRecord::Base.connected_to_many(FirstAbstractClass, SecondAbstractClass, role: :reading) do
assert_not FirstAbstractClass.current_preventing_writes
assert_not SecondAbstractClass.current_preventing_writes
assert FirstAbstractClass.current_preventing_writes
assert SecondAbstractClass.current_preventing_writes
assert_not ActiveRecord::Base.current_preventing_writes
end
end

View file

@ -130,7 +130,7 @@ module ActiveRecord
assert_equal :reading, ActiveRecord::Base.current_role
assert ActiveRecord::Base.connected_to?(role: :reading)
assert_not ActiveRecord::Base.connected_to?(role: :writing)
assert_not_predicate ActiveRecord::Base.connection, :preventing_writes?
assert_predicate ActiveRecord::Base.connection, :preventing_writes?
end
ActiveRecord::Base.connected_to(role: :writing) do

View file

@ -135,7 +135,7 @@ module ActiveRecord
assert_not ActiveRecord::Base.connected_to?(role: :writing, shard: :default)
assert_not ActiveRecord::Base.connected_to?(role: :writing, shard: :shard_one)
assert_not ActiveRecord::Base.connected_to?(role: :reading, shard: :shard_one)
assert_not_predicate ActiveRecord::Base.connection, :preventing_writes?
assert_predicate ActiveRecord::Base.connection, :preventing_writes?
end
ActiveRecord::Base.connected_to(role: :writing, shard: :default) do
@ -153,7 +153,7 @@ module ActiveRecord
assert_not ActiveRecord::Base.connected_to?(role: :writing, shard: :shard_one)
assert_not ActiveRecord::Base.connected_to?(role: :writing, shard: :default)
assert_not ActiveRecord::Base.connected_to?(role: :reading, shard: :default)
assert_not_predicate ActiveRecord::Base.connection, :preventing_writes?
assert_predicate ActiveRecord::Base.connection, :preventing_writes?
end
ActiveRecord::Base.connected_to(role: :writing, shard: :shard_one) do

View file

@ -391,23 +391,23 @@ module ActiveRecord
# Switch only primary to reading
PrimaryBase.connected_to(role: :reading) do
assert_not_predicate PrimaryBase.connection, :preventing_writes?
assert_predicate PrimaryBase.connection, :preventing_writes?
assert_not_predicate SecondaryBase.connection, :preventing_writes?
# Switch global to reading
ActiveRecord::Base.connected_to(role: :reading) do
assert_not_predicate PrimaryBase.connection, :preventing_writes?
assert_not_predicate SecondaryBase.connection, :preventing_writes?
assert_predicate PrimaryBase.connection, :preventing_writes?
assert_predicate SecondaryBase.connection, :preventing_writes?
# Switch only secondary to writing
SecondaryBase.connected_to(role: :writing) do
assert_not_predicate PrimaryBase.connection, :preventing_writes?
assert_predicate PrimaryBase.connection, :preventing_writes?
assert_not_predicate SecondaryBase.connection, :preventing_writes?
end
# Ensure restored to global reading
assert_not_predicate PrimaryBase.connection, :preventing_writes?
assert_not_predicate SecondaryBase.connection, :preventing_writes?
assert_predicate PrimaryBase.connection, :preventing_writes?
assert_predicate SecondaryBase.connection, :preventing_writes?
end
# Switch everything to writing
@ -416,7 +416,7 @@ module ActiveRecord
assert_not_predicate SecondaryBase.connection, :preventing_writes?
end
assert_not_predicate PrimaryBase.connection, :preventing_writes?
assert_predicate PrimaryBase.connection, :preventing_writes?
assert_not_predicate SecondaryBase.connection, :preventing_writes?
end
@ -444,7 +444,7 @@ module ActiveRecord
assert_not_predicate ApplicationRecord.connection, :preventing_writes?
ApplicationRecord.connected_to(role: :reading) do
assert_not_predicate ApplicationRecord.connection, :preventing_writes?
assert_predicate ApplicationRecord.connection, :preventing_writes?
end
end
ensure

View file

@ -180,7 +180,7 @@ module ActiveRecord
assert_equal :reading, ActiveRecord::Base.current_role
assert ActiveRecord::Base.connected_to?(role: :reading)
assert_not ActiveRecord::Base.connected_to?(role: :writing)
assert_not_predicate ActiveRecord::Base.connection, :preventing_writes?
assert_predicate ActiveRecord::Base.connection, :preventing_writes?
end
ActiveRecord::Base.connected_to(role: :writing) do

View file

@ -150,7 +150,7 @@ module ActiveRecord
assert_not ActiveRecord::Base.connected_to?(role: :writing, shard: :default)
assert_not ActiveRecord::Base.connected_to?(role: :writing, shard: :shard_one)
assert_not ActiveRecord::Base.connected_to?(role: :reading, shard: :shard_one)
assert_not_predicate ActiveRecord::Base.connection, :preventing_writes?
assert_predicate ActiveRecord::Base.connection, :preventing_writes?
end
ActiveRecord::Base.connected_to(role: :writing, shard: :default) do
@ -172,7 +172,7 @@ module ActiveRecord
assert_not ActiveRecord::Base.connected_to?(role: :writing, shard: :shard_one)
assert_not ActiveRecord::Base.connected_to?(role: :writing, shard: :default)
assert_not ActiveRecord::Base.connected_to?(role: :reading, shard: :default)
assert_not_predicate ActiveRecord::Base.connection, :preventing_writes?
assert_predicate ActiveRecord::Base.connection, :preventing_writes?
end
ActiveRecord::Base.connected_to(role: :writing, shard: :shard_one) do

View file

@ -62,7 +62,7 @@ module ActiveRecord
called = true
assert ActiveRecord::Base.connected_to?(role: :reading)
assert_not_predicate ActiveRecord::Base.connection, :preventing_writes?
assert_predicate ActiveRecord::Base.connection, :preventing_writes?
ActiveRecord::Base.connected_to(role: :writing, prevent_writes: false) do
assert ActiveRecord::Base.connected_to?(role: :writing)
@ -70,7 +70,7 @@ module ActiveRecord
end
assert ActiveRecord::Base.connected_to?(role: :reading)
assert_not_predicate ActiveRecord::Base.connection, :preventing_writes?
assert_predicate ActiveRecord::Base.connection, :preventing_writes?
end
assert called
end