Merge pull request #40169 from eileencodes/ensure-that-default-is-considered-primary-or-first
Default db_config should be primary or first
This commit is contained in:
commit
77667bcead
|
@ -1,3 +1,9 @@
|
|||
* Ensure the default configuration is considered primary or first for an environment
|
||||
|
||||
If a multiple database application provides a configuration named primary, that will be treated as default. In applications that do not have a primary entry, the default database configuration will be the first configuration for an environment.
|
||||
|
||||
*Eileen M. Uchitelle*
|
||||
|
||||
* Allow `where` references association names as joined table name aliases.
|
||||
|
||||
```ruby
|
||||
|
|
|
@ -87,6 +87,20 @@ module ActiveRecord
|
|||
end
|
||||
end
|
||||
|
||||
# A primary configuration is one that is named primary or if there is
|
||||
# no primary, the first configuration for an environment will be treated
|
||||
# as primary. This is used as the "default" configuration and is used
|
||||
# when the application needs to treat one configuration differently. For
|
||||
# example, when Rails dumps the schema, the primary configuration's schema
|
||||
# file will be named `schema.rb` instead of `primary_schema.rb`.
|
||||
def primary?(name) # :nodoc:
|
||||
return true if name == "primary"
|
||||
|
||||
first_config = find_db_config(default_env)
|
||||
first_config && name == first_config.name
|
||||
end
|
||||
|
||||
|
||||
# Returns the DatabaseConfigurations object as a Hash.
|
||||
def to_h
|
||||
configurations.inject({}) do |memo, db_config|
|
||||
|
|
|
@ -407,21 +407,21 @@ module ActiveRecord
|
|||
end
|
||||
end
|
||||
|
||||
def dump_filename(name, format = ActiveRecord::Base.schema_format)
|
||||
filename = if name == "primary"
|
||||
def dump_filename(db_config_name, format = ActiveRecord::Base.schema_format)
|
||||
filename = if ActiveRecord::Base.configurations.primary?(db_config_name)
|
||||
schema_file_type(format)
|
||||
else
|
||||
"#{name}_#{schema_file_type(format)}"
|
||||
"#{db_config_name}_#{schema_file_type(format)}"
|
||||
end
|
||||
|
||||
ENV["SCHEMA"] || File.join(ActiveRecord::Tasks::DatabaseTasks.db_dir, filename)
|
||||
end
|
||||
|
||||
def cache_dump_filename(name, schema_cache_path: nil)
|
||||
filename = if name == "primary"
|
||||
def cache_dump_filename(db_config_name, schema_cache_path: nil)
|
||||
filename = if ActiveRecord::Base.configurations.primary?(db_config_name)
|
||||
"schema_cache.yml"
|
||||
else
|
||||
"#{name}_schema_cache.yml"
|
||||
"#{db_config_name}_schema_cache.yml"
|
||||
end
|
||||
|
||||
schema_cache_path || ENV["SCHEMA_CACHE"] || File.join(ActiveRecord::Tasks::DatabaseTasks.db_dir, filename)
|
||||
|
|
|
@ -21,9 +21,9 @@ so you don't have to store your data all in one place.
|
|||
|
||||
At this time the following features are supported:
|
||||
|
||||
* Multiple primary databases and a replica for each
|
||||
* Multiple writer databases and a replica for each
|
||||
* Automatic connection switching for the model you're working with
|
||||
* Automatic swapping between the primary and replica depending on the HTTP verb
|
||||
* Automatic swapping between the writer and replica depending on the HTTP verb
|
||||
and recent writes
|
||||
* Rails tasks for creating, dropping, migrating, and interacting with the multiple
|
||||
databases
|
||||
|
@ -40,7 +40,7 @@ The following features are not (yet) supported:
|
|||
While Rails tries to do most of the work for you there are still some steps you'll
|
||||
need to do to get your application ready for multiple databases.
|
||||
|
||||
Let's say we have an application with a single primary database and we need to add a
|
||||
Let's say we have an application with a single writer database and we need to add a
|
||||
new database for some new tables we're adding. The name of the new database will be
|
||||
"animals".
|
||||
|
||||
|
@ -53,8 +53,15 @@ production:
|
|||
adapter: mysql
|
||||
```
|
||||
|
||||
Let's add a replica for the primary, a new writer called animals and a replica for that
|
||||
as well. To do this we need to change our `database.yml` from a 2-tier to a 3-tier config.
|
||||
Let's add a replica for the first configuration, and a second database called animals and a
|
||||
replica for that as well. To do this we need to change our `database.yml` from a 2-tier
|
||||
to a 3-tier config.
|
||||
|
||||
If a primary configuration is provided this will be used as the "default" configuration. If
|
||||
there is no configuration named "primary" Rails will use the first configuration for an
|
||||
environment. The default configurations will use the default Rails filenames. For example
|
||||
primary configurations will use `schema.rb` for the schema file whereas all other entries
|
||||
will use `[CONFIGURATION_NAMESPACE]_schema.rb` for the filename.
|
||||
|
||||
```yaml
|
||||
production:
|
||||
|
@ -81,15 +88,17 @@ production:
|
|||
|
||||
When using multiple databases there are a few important settings.
|
||||
|
||||
First, the database name for the primary and replica should be the same because they contain
|
||||
the same data. Second, the username for the primary and replica should be different, and the
|
||||
replica user's permissions should be to read and not write.
|
||||
First, the database name for the `primary` and `primary_replica` should be the same because they contain
|
||||
the same data. This is also the case for `animals` and `animals_replica`.
|
||||
|
||||
Second, the username for the writers and replicas should be different, and the
|
||||
replica user's permissions should be set to only read and not write.
|
||||
|
||||
When using a replica database you need to add a `replica: true` entry to the replica in the
|
||||
`database.yml`. This is because Rails otherwise has no way of knowing which one is a replica
|
||||
and which one is the primary.
|
||||
and which one is the writer.
|
||||
|
||||
Lastly, for new primary databases you need to set the `migrations_paths` to the directory
|
||||
Lastly, for new writer databases you need to set the `migrations_paths` to the directory
|
||||
where you will store migrations for that database. We'll look more at `migrations_paths`
|
||||
later on in this guide.
|
||||
|
||||
|
@ -227,13 +236,13 @@ use a different parent class.
|
|||
Finally, in order to use the read-only replica in your application you'll need to activate
|
||||
the middleware for automatic switching.
|
||||
|
||||
Automatic switching allows the application to switch from the primary to replica or replica
|
||||
to primary based on the HTTP verb and whether there was a recent write.
|
||||
Automatic switching allows the application to switch from the writer to replica or replica
|
||||
to writer based on the HTTP verb and whether there was a recent write.
|
||||
|
||||
If the application is receiving a POST, PUT, DELETE, or PATCH request the application will
|
||||
automatically write to the primary. For the specified time after the write, the application
|
||||
will read from the primary. For a GET or HEAD request the application will read from the
|
||||
replica unless there was a recent write.
|
||||
automatically write to the writer database. For the specified time after the write, the
|
||||
application will read from the primary. For a GET or HEAD request the application will read
|
||||
from the replica unless there was a recent write.
|
||||
|
||||
To activate the automatic connection switching middleware, add or uncomment the following
|
||||
lines in your application config.
|
||||
|
@ -245,7 +254,7 @@ config.active_record.database_resolver_context = ActiveRecord::Middleware::Datab
|
|||
```
|
||||
|
||||
Rails guarantees "read your own write" and will send your GET or HEAD request to the
|
||||
primary if it's within the `delay` window. By default the delay is set to 2 seconds. You
|
||||
writer if it's within the `delay` window. By default the delay is set to 2 seconds. You
|
||||
should change this based on your database infrastructure. Rails doesn't guarantee "read
|
||||
a recent write" for other users within the delay window and will send GET and HEAD requests
|
||||
to the replicas unless they wrote recently.
|
||||
|
@ -274,7 +283,7 @@ config.active_record.database_resolver_context = MyCookieResolver
|
|||
|
||||
## Using manual connection switching
|
||||
|
||||
There are some cases where you may want your application to connect to a primary or a replica
|
||||
There are some cases where you may want your application to connect to a writer or a replica
|
||||
and the automatic connection switching isn't adequate. For example, you may know that for a
|
||||
particular request you always want to send the request to a replica, even when you are in a
|
||||
POST request path.
|
||||
|
|
|
@ -343,15 +343,12 @@ module ApplicationTests
|
|||
db_migrate_and_status database_url_db_name
|
||||
end
|
||||
|
||||
def db_schema_dump(database: nil)
|
||||
def db_schema_dump
|
||||
Dir.chdir(app_path) do
|
||||
args = ["generate", "model", "book", "title:string"]
|
||||
args << "--database=#{database}" if database
|
||||
rails args
|
||||
rails "db:migrate", "db:schema:dump"
|
||||
dump_name = database ? "#{database}_schema.rb" : "schema.rb"
|
||||
schema_dump = File.read("db/#{dump_name}")
|
||||
assert_match(/create_table \"books\"/, schema_dump)
|
||||
assert_match(/create_table \"books\"/, File.read("db/schema.rb"))
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -427,17 +424,17 @@ module ApplicationTests
|
|||
statement_timeout: 1000
|
||||
development:
|
||||
some_entry:
|
||||
<<: *default
|
||||
database: db/development_other.sqlite3
|
||||
migrations_paths: db/some_entry_migrate
|
||||
primary:
|
||||
<<: *default
|
||||
database: db/development.sqlite3
|
||||
another_entry:
|
||||
<<: *default
|
||||
database: db/another_entry_development.sqlite3
|
||||
migrations_paths: db/another_entry_migrate
|
||||
YAML
|
||||
end
|
||||
end
|
||||
|
||||
db_schema_dump(database: "some_entry")
|
||||
db_schema_dump
|
||||
db_schema_cache_dump
|
||||
end
|
||||
|
||||
|
|
|
@ -812,6 +812,21 @@ module ApplicationTests
|
|||
|
||||
db_create_and_drop_namespace("primary", "db/development.sqlite3")
|
||||
end
|
||||
|
||||
test "a thing" do
|
||||
app_file "config/database.yml", <<-YAML
|
||||
development:
|
||||
default:
|
||||
database: db/default.sqlite3
|
||||
adapter: sqlite3
|
||||
animals:
|
||||
database: db/develoment_animals.sqlite3
|
||||
adapter: sqlite3
|
||||
migrations_paths: db/animals_migrate
|
||||
YAML
|
||||
|
||||
db_migrate_and_schema_dump_and_load
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
Loading…
Reference in New Issue