diff options
author | Eileen Uchitelle <eileencodes@gmail.com> | 2018-11-21 15:29:46 -0500 |
---|---|---|
committer | Eileen Uchitelle <eileencodes@gmail.com> | 2018-11-30 09:28:04 -0500 |
commit | f39d72d5267baed1000932831cda98503d1e1047 (patch) | |
tree | 30f91b6801492c0e040b76ee330f53b3e4b957e4 /activerecord/lib/active_record/connection_adapters/abstract_adapter.rb | |
parent | 5c6316dbb88075e27169f49a22e59357efd1a967 (diff) | |
download | rails-f39d72d5267baed1000932831cda98503d1e1047.tar.gz rails-f39d72d5267baed1000932831cda98503d1e1047.tar.bz2 rails-f39d72d5267baed1000932831cda98503d1e1047.zip |
Add ability to prevent writes to a database
This PR adds the ability to prevent writes to a database even if the
database user is able to write (ie the database is a primary and not a
replica).
This is useful for a few reasons: 1) when converting your database from
a single db to a primary/replica setup - you can fix all the writes on
reads early on, 2) when we implement automatic database switching or
when an app is manually switching connections this feature can be used
to ensure reads are reading and writes are writing. We want to make sure
we raise if we ever try to write in read mode, regardless of database
type and 3) for local development if you don't want to set up multiple
databases but do want to support rw/ro queries.
This should be used in conjunction with `connected_to` in write mode.
For example:
```
ActiveRecord::Base.connected_to(role: :writing) do
Dog.connection.while_preventing_writes do
Dog.create! # will raise because we're preventing writes
end
end
ActiveRecord::Base.connected_to(role: :reading) do
Dog.connection.while_preventing_writes do
Dog.first # will not raise because we're not writing
end
end
```
Diffstat (limited to 'activerecord/lib/active_record/connection_adapters/abstract_adapter.rb')
-rw-r--r-- | activerecord/lib/active_record/connection_adapters/abstract_adapter.rb | 30 |
1 files changed, 29 insertions, 1 deletions
diff --git a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb index f73369ceef..31a994292e 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb @@ -76,7 +76,7 @@ module ActiveRecord SIMPLE_INT = /\A\d+\z/ - attr_accessor :visitor, :pool + attr_accessor :visitor, :pool, :prevent_writes attr_reader :schema_cache, :owner, :logger, :prepared_statements, :lock alias :in_use? :owner @@ -100,6 +100,13 @@ module ActiveRecord end end + def self.build_read_query_regexp(*parts) # :nodoc: + lambda do |*parts| + parts = parts.map { |part| /\A\s*#{part}/i } + Regexp.union(*parts) + end + end + def initialize(connection, logger = nil, config = {}) # :nodoc: super() @@ -133,6 +140,27 @@ module ActiveRecord @config[:replica] || false end + # Determines whether writes are currently being prevents. + # + # Returns true if the connection is a replica, or if +prevent_writes+ + # is set to true. + def preventing_writes? + replica? || 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 + original = self.prevent_writes + self.prevent_writes = true + yield + ensure + self.prevent_writes = original + end + def migrations_paths # :nodoc: @config[:migrations_paths] || Migrator.migrations_paths end |