Skip to content

Commit 69bc201

Browse files
committed
Revert "Disable automatic write protection on replicas"
This reverts commit 951deec. 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
1 parent 6ae78e9 commit 69bc201

11 files changed

+34
-38
lines changed

activerecord/CHANGELOG.md

Lines changed: 0 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -16,19 +16,6 @@
1616

1717
*Jorge Manrubia*
1818

19-
* Disable automatic write protection on replicas.
20-
21-
Write protection is no longer automatically enabled for replicas.
22-
You can manually prevent writes in your app with
23-
`while_preventing_writes`. To automatically disable all writes on
24-
your replica, configure the database user you are using to connect
25-
to your replica to prevent writes. How you configure this is
26-
specific to which database adapter you are using, but it usually
27-
involves only granting the database user permission to do `SELECT`
28-
queries.
29-
30-
*Adam Hess*
31-
3219
* The MySQL adapter now cast numbers and booleans bind parameters to to string for safety reasons.
3320

3421
When comparing a string and a number in a query, MySQL convert the string to a number. So for

activerecord/lib/active_record/connection_adapters/abstract_adapter.rb

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,12 +131,15 @@ def use_metadata_table?
131131

132132
# Determines whether writes are currently being prevented.
133133
#
134+
# Returns true if the connection is a replica.
135+
#
134136
# If the application is using legacy handling, returns
135137
# true if +connection_handler.prevent_writes+ is set.
136138
#
137139
# If the application is using the new connection handling
138140
# will return true based on +current_preventing_writes+.
139141
def preventing_writes?
142+
return true if replica?
140143
return ActiveRecord::Base.connection_handler.prevent_writes if ActiveRecord.legacy_connection_handling
141144
return false if connection_klass.nil?
142145

activerecord/lib/active_record/connection_handling.rb

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,8 @@ def connected_to_many(*classes, role:, shard: nil, prevent_writes: false)
184184
raise NotImplementedError, "connected_to_many can only be called on ActiveRecord::Base."
185185
end
186186

187+
prevent_writes = true if role == ActiveRecord.reading_role
188+
187189
connected_to_stack << { role: role, shard: shard, prevent_writes: prevent_writes, klasses: classes }
188190
yield
189191
ensure
@@ -202,6 +204,8 @@ def connecting_to(role: default_role, shard: default_shard, prevent_writes: fals
202204
raise NotImplementedError, "`connecting_to` is not available with `legacy_connection_handling`."
203205
end
204206

207+
prevent_writes = true if role == ActiveRecord.reading_role
208+
205209
self.connected_to_stack << { role: role, shard: shard, prevent_writes: prevent_writes, klasses: [self] }
206210
end
207211

@@ -352,6 +356,8 @@ def with_handler(handler_key, &blk)
352356
end
353357

354358
def with_role_and_shard(role, shard, prevent_writes)
359+
prevent_writes = true if role == ActiveRecord.reading_role
360+
355361
if ActiveRecord.legacy_connection_handling
356362
with_handler(role.to_sym) do
357363
connection_handler.while_preventing_writes(prevent_writes) do

activerecord/lib/active_record/middleware/database_selector/resolver.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ def read_from_primary(&blk)
5858
end
5959

6060
def read_from_replica(&blk)
61-
ActiveRecord::Base.connected_to(role: ActiveRecord.reading_role) do
61+
ActiveRecord::Base.connected_to(role: ActiveRecord.reading_role, prevent_writes: true) do
6262
instrumenter.instrument("database_selector.active_record.read_from_replica") do
6363
yield
6464
end

activerecord/test/cases/base_test.rb

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1721,7 +1721,7 @@ def test_protected_environments_are_stored_as_an_array_of_string
17211721
SecondAbstractClass.connecting_to(role: :reading)
17221722

17231723
assert SecondAbstractClass.connected_to?(role: :reading)
1724-
assert_not SecondAbstractClass.current_preventing_writes
1724+
assert SecondAbstractClass.current_preventing_writes
17251725
ensure
17261726
ActiveRecord::Base.connected_to_stack.pop
17271727
end
@@ -1777,24 +1777,24 @@ def test_protected_environments_are_stored_as_an_array_of_string
17771777
end
17781778
end
17791779

1780-
test "#connected_to_many does not sets prevent_writes if role is reading" do
1780+
test "#connected_to_many sets prevent_writes if role is reading" do
17811781
ActiveRecord::Base.connected_to_many([SecondAbstractClass], role: :reading) do
1782-
assert_not SecondAbstractClass.current_preventing_writes
1782+
assert SecondAbstractClass.current_preventing_writes
17831783
assert_not ActiveRecord::Base.current_preventing_writes
17841784
end
17851785
end
17861786

1787-
test "#connected_to_many with a single argument for classes does not set prevent_writes" do
1787+
test "#connected_to_many with a single argument for classes" do
17881788
ActiveRecord::Base.connected_to_many(SecondAbstractClass, role: :reading) do
1789-
assert_not SecondAbstractClass.current_preventing_writes
1789+
assert SecondAbstractClass.current_preventing_writes
17901790
assert_not ActiveRecord::Base.current_preventing_writes
17911791
end
17921792
end
17931793

1794-
test "#connected_to_many with a multiple classes without brackets does not prevent_writes" do
1794+
test "#connected_to_many with a multiple classes without brackets works" do
17951795
ActiveRecord::Base.connected_to_many(FirstAbstractClass, SecondAbstractClass, role: :reading) do
1796-
assert_not FirstAbstractClass.current_preventing_writes
1797-
assert_not SecondAbstractClass.current_preventing_writes
1796+
assert FirstAbstractClass.current_preventing_writes
1797+
assert SecondAbstractClass.current_preventing_writes
17981798
assert_not ActiveRecord::Base.current_preventing_writes
17991799
end
18001800
end

activerecord/test/cases/connection_adapters/connection_handlers_multi_db_test.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,7 @@ def test_switching_connections_via_handler
130130
assert_equal :reading, ActiveRecord::Base.current_role
131131
assert ActiveRecord::Base.connected_to?(role: :reading)
132132
assert_not ActiveRecord::Base.connected_to?(role: :writing)
133-
assert_not_predicate ActiveRecord::Base.connection, :preventing_writes?
133+
assert_predicate ActiveRecord::Base.connection, :preventing_writes?
134134
end
135135

136136
ActiveRecord::Base.connected_to(role: :writing) do

activerecord/test/cases/connection_adapters/connection_handlers_sharding_db_test.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,7 @@ def test_switching_connections_via_handler
135135
assert_not ActiveRecord::Base.connected_to?(role: :writing, shard: :default)
136136
assert_not ActiveRecord::Base.connected_to?(role: :writing, shard: :shard_one)
137137
assert_not ActiveRecord::Base.connected_to?(role: :reading, shard: :shard_one)
138-
assert_not_predicate ActiveRecord::Base.connection, :preventing_writes?
138+
assert_predicate ActiveRecord::Base.connection, :preventing_writes?
139139
end
140140

141141
ActiveRecord::Base.connected_to(role: :writing, shard: :default) do
@@ -153,7 +153,7 @@ def test_switching_connections_via_handler
153153
assert_not ActiveRecord::Base.connected_to?(role: :writing, shard: :shard_one)
154154
assert_not ActiveRecord::Base.connected_to?(role: :writing, shard: :default)
155155
assert_not ActiveRecord::Base.connected_to?(role: :reading, shard: :default)
156-
assert_not_predicate ActiveRecord::Base.connection, :preventing_writes?
156+
assert_predicate ActiveRecord::Base.connection, :preventing_writes?
157157
end
158158

159159
ActiveRecord::Base.connected_to(role: :writing, shard: :shard_one) do

activerecord/test/cases/connection_adapters/connection_swapping_nested_test.rb

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -391,23 +391,23 @@ def test_prevent_writes_can_be_changed_granularly
391391

392392
# Switch only primary to reading
393393
PrimaryBase.connected_to(role: :reading) do
394-
assert_not_predicate PrimaryBase.connection, :preventing_writes?
394+
assert_predicate PrimaryBase.connection, :preventing_writes?
395395
assert_not_predicate SecondaryBase.connection, :preventing_writes?
396396

397397
# Switch global to reading
398398
ActiveRecord::Base.connected_to(role: :reading) do
399-
assert_not_predicate PrimaryBase.connection, :preventing_writes?
400-
assert_not_predicate SecondaryBase.connection, :preventing_writes?
399+
assert_predicate PrimaryBase.connection, :preventing_writes?
400+
assert_predicate SecondaryBase.connection, :preventing_writes?
401401

402402
# Switch only secondary to writing
403403
SecondaryBase.connected_to(role: :writing) do
404-
assert_not_predicate PrimaryBase.connection, :preventing_writes?
404+
assert_predicate PrimaryBase.connection, :preventing_writes?
405405
assert_not_predicate SecondaryBase.connection, :preventing_writes?
406406
end
407407

408408
# Ensure restored to global reading
409-
assert_not_predicate PrimaryBase.connection, :preventing_writes?
410-
assert_not_predicate SecondaryBase.connection, :preventing_writes?
409+
assert_predicate PrimaryBase.connection, :preventing_writes?
410+
assert_predicate SecondaryBase.connection, :preventing_writes?
411411
end
412412

413413
# Switch everything to writing
@@ -416,7 +416,7 @@ def test_prevent_writes_can_be_changed_granularly
416416
assert_not_predicate SecondaryBase.connection, :preventing_writes?
417417
end
418418

419-
assert_not_predicate PrimaryBase.connection, :preventing_writes?
419+
assert_predicate PrimaryBase.connection, :preventing_writes?
420420
assert_not_predicate SecondaryBase.connection, :preventing_writes?
421421
end
422422

@@ -444,7 +444,7 @@ def test_application_record_prevent_writes_can_be_changed
444444
assert_not_predicate ApplicationRecord.connection, :preventing_writes?
445445

446446
ApplicationRecord.connected_to(role: :reading) do
447-
assert_not_predicate ApplicationRecord.connection, :preventing_writes?
447+
assert_predicate ApplicationRecord.connection, :preventing_writes?
448448
end
449449
end
450450
ensure

activerecord/test/cases/connection_adapters/legacy_connection_handlers_multi_db_test.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -180,7 +180,7 @@ def test_switching_connections_via_handler
180180
assert_equal :reading, ActiveRecord::Base.current_role
181181
assert ActiveRecord::Base.connected_to?(role: :reading)
182182
assert_not ActiveRecord::Base.connected_to?(role: :writing)
183-
assert_not_predicate ActiveRecord::Base.connection, :preventing_writes?
183+
assert_predicate ActiveRecord::Base.connection, :preventing_writes?
184184
end
185185

186186
ActiveRecord::Base.connected_to(role: :writing) do

activerecord/test/cases/connection_adapters/legacy_connection_handlers_sharding_db_test.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,7 @@ def test_switching_connections_via_handler
150150
assert_not ActiveRecord::Base.connected_to?(role: :writing, shard: :default)
151151
assert_not ActiveRecord::Base.connected_to?(role: :writing, shard: :shard_one)
152152
assert_not ActiveRecord::Base.connected_to?(role: :reading, shard: :shard_one)
153-
assert_not_predicate ActiveRecord::Base.connection, :preventing_writes?
153+
assert_predicate ActiveRecord::Base.connection, :preventing_writes?
154154
end
155155

156156
ActiveRecord::Base.connected_to(role: :writing, shard: :default) do
@@ -172,7 +172,7 @@ def test_switching_connections_via_handler
172172
assert_not ActiveRecord::Base.connected_to?(role: :writing, shard: :shard_one)
173173
assert_not ActiveRecord::Base.connected_to?(role: :writing, shard: :default)
174174
assert_not ActiveRecord::Base.connected_to?(role: :reading, shard: :default)
175-
assert_not_predicate ActiveRecord::Base.connection, :preventing_writes?
175+
assert_predicate ActiveRecord::Base.connection, :preventing_writes?
176176
end
177177

178178
ActiveRecord::Base.connected_to(role: :writing, shard: :shard_one) do

0 commit comments

Comments
 (0)