1. 20 3月, 2020 1 次提交
    • E
      Handle db:rollback and db:rollback:[NAME] for multi-db apps · e33075a0
      eileencodes 提交于
      With a multiple database application `db:rollback` becomes problematic.
      We can't rollback just the primary, that doesn't match the behavior in
      the other tasks. We can't rollback a migration for every database, that
      is unexpected.
      
      To solve this I handled `db:rollback` the same way I handled `:up` and
      `:down`. If `db:rollback` is called for a multi-db application then it
      will raise an error recommending you use `db:rollback:[NAME]` instead.
      Calling `db:rollback:primary` or `db:rollback:animals` will rollback
      the migration for the number of steps specified.
      
      Closes: #38513
      Follow-up to: #34078
      e33075a0
  2. 07 3月, 2020 1 次提交
    • E
      Add erb tests for multi-db · 67b102a5
      eileencodes 提交于
      If I had had these tests previously I would have not created PR #38658
      and then promptly realize I needed to revert it.
      
      We need to load and parse the configurations twice. Once before the
      environment is loaded to create the named tasks and once after the
      environment is loaded to have the real configurations. The configs
      loaded before the env have the ERB stripped out and aren't valid
      configs.
      67b102a5
  3. 28 2月, 2020 2 次提交
  4. 25 2月, 2020 2 次提交
    • E
      Deprecate `spec_name` and use `name` for configurations · 79ce7d9a
      eileencodes 提交于
      I have so. many. regrets. about using `spec_name` for database
      configurations and now I'm finally putting this mistake to an end.
      
      Back when I started multi-db work I assumed that eventually
      `connection_specification_name` (sometimes called `spec_name`) and
      `spec_name` for configurations would one day be the same thing. After
      2 years I no longer believe they will ever be the same thing.
      
      This PR deprecates `spec_name` on database configurations in favor of
      `name`. It's the same behavior, just a better name, or at least a
      less confusing name.
      
      `connection_specification_name` refers to the parent class name (ie
      ActiveRecord::Base, AnimalsBase, etc) that holds the connection for it's
      models. In some places like ConnectionHandler it shortens this to
      `spec_name`, hence the major confusion.
      
      Recently I've been working with some new folks on database stuff and
      connection management and realize how confusing it was to explain that
      `db_config.spec_name` was not `spec_name` and
      `connection_specification_name`. Worse than that one is a symbole while
      the other is a class name. This was made even more complicated by the
      fact that `ActiveRecord::Base` used `primary` as the
      `connection_specification_name` until #38190.
      
      After spending 2 years with connection management I don't believe that
      we can ever use the symbols from the database configs as a way to
      connect the database without the class name being _somewhere_ because
      a db_config does not know who it's owner class is until it's been
      connected and a model has no idea what db_config belongs to it until
      it's connected. The model is the only way to tie a primary/writer config
      to a replica/reader config. This could change in the future but I don't
      see value in adding a class name to the db_configs before connection or
      telling a model what config belongs to it before connection. That would
      probably break a lot of application assumptions. If we do ever end up in
      that world, we can use name, because tbh `spec_name` and
      `connection_specification_name` were always confusing to me.
      79ce7d9a
    • K
  5. 13 2月, 2020 1 次提交
    • E
      Add schema cache tests · c1a215a0
      eileencodes 提交于
      The schema cache tests test the following scenarios:
      
      1) The default case works (single db, primary spec name (dev is default
      to primary in 2-tier config), standard default schema cache filename)
      2) Primary always wins over other entries
      3) A custom schema cache filename works when set in the configuration
      4) A custom schema cache filename works when set in the ENV
      
      Cases that don't work:
      
      1) A non-primary database entry picks up a namespaced schema cache file
      
      This can't work currently because there's no way of knowing which cache
      we actually want. In this railtie we can only load ActiveRecord::Base's
      schema cache. If we grab the first config we risk loading a cache for
      another connection because order is not guaranteed.
      
      2) Multi-db schema caches
      
      The reasons are similar to above. In addition we can't loop through the
      configs, establish a connection, and load the cache because we don't
      know what parent class to establish a connection to. In that case AR
      Base will always get the cache and it would cause the last one to win
      and therefore be loaded on the wrong connection.
      
      The real fix for these issues is to get rid of the railtie entirely, but
      for now we needed to set this back to what the behavior was before
      recent changes but with the ability to pass a custom key.
      Co-authored-by: NKatrina Owen <kytrinyx@github.com>
      c1a215a0
  6. 25 12月, 2019 1 次提交
  7. 02 11月, 2019 1 次提交
    • E
      Appease rubocop · 042b6fec
      eileencodes 提交于
      Rubocop wants double quoted strings, not single. I missed this when I
      merged #37601
      042b6fec
  8. 31 10月, 2019 1 次提交
    • E
      Reestablish connection to previous database after migrating: · c9e44d1a
      Edouard CHIN 提交于
      - The migrate task iterates and establish a connection over each db
        resulting in the last one to be used by subsequent rake tasks.
        We should reestablish a connection to the connection that was
        established before the migrate tasks was run
      - Fix #37578
      c9e44d1a
  9. 28 9月, 2019 1 次提交
  10. 24 9月, 2019 2 次提交
  11. 14 9月, 2019 1 次提交
    • E
      Reduce surface area of ConnectionSpecification · b8fc0150
      eileencodes 提交于
      Eventually we'd like to get rid of this class altogether but for now
      this PR reduces the surface area by removing methods from the class and
      moving classes out into their own files.
      
      * `adapter_method` was moved into database configurations
      * `initialize_dup` was removed because it was only used in tests
      * Resolver is now it's own class under connection adapters
      * ConnectionUrlResolver, only used by the configurations, is in a class
      under DatabaseConfigurations
      Co-authored-by: NJohn Crepezzi <john.crepezzi@gmail.com>
      b8fc0150
  12. 13 9月, 2019 1 次提交
    • E
      Use symbols everywhere for database configurations · ce9b197c
      eileencodes 提交于
      Previously in some places we used symbol keys, and in some places we used
      string keys. That made it pretty confusing to figure out in a particular
      place what type of configuration object you were working with.
      
      Now internally, all configuration hashes are keyed by symbols and
      converted to such on the way in.
      
      A few exceptions:
      
      - `DatabaseConfigurations#to_h` still returns strings for backward compatibility
      - Same for `legacy_hash`
      - `default_hash` previously could return strings, but the associated
        comment mentions it returns symbol-key `Hash` and now it always does
      
      Because this is a change in behavior, a few method renames have happened:
      
      - `DatabaseConfig#config` is now `DatabaseConfig#configuration_hash` and returns a symbol-key `Hash`
      - `ConnectionSpecification#config` is now `ConnectionSpecification#underlying_configuration_hash` and returns the `Hash` of the underlying `DatabaseConfig`
      - `DatabaseConfig#config` was added back, returns `String`-keys for backward compatibility, and is deprecated in favor of the new `configuration_hash`
      Co-authored-by: Neileencodes <eileencodes@gmail.com>
      ce9b197c
  13. 01 8月, 2019 1 次提交
    • E
      Fix db:seed · cd148be0
      eileencodes 提交于
      The `rake db:seed` command was broken for the primary environment if the
      application is using multiple databases. We never implemented `rake
      db:seed` for other databases (coming soon), but that shouldn't break the
      default case.
      
      The reason this was broken was because `abort_if_pending_migrations`
      would loop through the configs for all databases and check for
      migrations but would leave the last established connection. So `db:seed`
      was looking in the wrong database for the table to seed.
      
      This PR doesn't fix the fact that `db:seed` doesn't work for multiple
      databases but does fix the default case.
      
      Fixes #36817
      Co-authored-by: NJohn Crepezzi <john.crepezzi@gmail.com>
      cd148be0
  14. 11 6月, 2019 1 次提交
  15. 05 6月, 2019 1 次提交
  16. 20 4月, 2019 1 次提交
    • E
      Handle up/down for multiple databases · f9244c65
      eileencodes 提交于
      This change adds the ability to run up/down for a database in a multi-db
      environment.
      
      If you have an app with a primary and animals database the following
      tasks will be generated:
      
      ```
      VERSION=123 rake db:migrate:up:primary
      VERSION=123 rake db:migrate:up:primary
      
      VERSION=123 rake db:migrate:down:primary
      VERSION=123 rake db:migrate:up:animals
      ```
      
      I didn't generate descriptions with them since we don't generate a
      description for a single database application.
      
      In addition to this change I've made it so if your application has
      multiple databases Rails will raise if you try to run `up` or `down`
      without a namespace. This is because we don't know which DB you want to
      run `up` or `down` against unless the app tells us, so it's safer to
      just block it and recommend using namespaced versions of up/down
      respectively.
      
      The output for the raise looks like:
      
      ```
      You're using a multiple database application. To use `db:migrate:down`
      you must run the namespaced task with a VERSION. Available tasks are
      db:migrate:down:primary and db:migrate:down:animals.
      ```
      f9244c65
  17. 03 4月, 2019 1 次提交
  18. 02 4月, 2019 1 次提交
  19. 20 2月, 2019 1 次提交
  20. 08 11月, 2018 2 次提交
  21. 10 10月, 2018 1 次提交
  22. 01 9月, 2018 1 次提交
    • E
      Convert configs_for to kwargs, add include_replicas · a572d228
      Eileen Uchitelle 提交于
      Changes the `configs_for` method from using traditional arguments to
      using kwargs. This is so I can add the `include_replicas` kwarg without
      having to always include `env_name` and `spec_name` in the method call.
      
      `include_replicas` defaults to false because everywhere internally in
      Rails we don't want replicas. `configs_for` is for iterating over
      configurations to create / run rake tasks, so we really don't ever need
      replicas in that case.
      a572d228
  23. 30 8月, 2018 1 次提交
    • E
      Refactors Active Record connection management · fdf3f0b9
      Eileen Uchitelle 提交于
      While the three-tier config makes it easier to define databases for
      multiple database applications, it quickly became clear to offer full
      support for multiple databases we need to change the way the connections
      hash was handled.
      
      A three-tier config means that when Rails needed to choose a default
      configuration (in the case a user doesn't ask for a specific
      configuration) it wasn't clear to Rails which the default was. I
      [bandaid fixed this so the rake tasks could work](#32271) but that fix
      wasn't correct because it actually doubled up the configuration hashes.
      
      Instead of attemping to manipulate the hashes @tenderlove and I decided
      that it made more sense if we converted the hashes to objects so we can
      easily ask those object questions. In a three tier config like this:
      
      ```
      development:
        primary:
          database: "my_primary_db"
        animals:
          database; "my_animals_db"
      ```
      
      We end up with an object like this:
      
      ```
        @configurations=[
          #<ActiveRecord::DatabaseConfigurations::HashConfig:0x00007fd1acbded10
            @env_name="development",@spec_name="primary",
            @config={"adapter"=>"sqlite3", "database"=>"db/development.sqlite3"}>,
          #<ActiveRecord::DatabaseConfigurations::HashConfig:0x00007fd1acbdea90
            @env_name="development",@spec_name="animals",
            @config={"adapter"=>"sqlite3", "database"=>"db/development.sqlite3"}>
      ]>
      ```
      
      The configurations setter takes the database configuration set by your
      application and turns them into an
      `ActiveRecord::DatabaseConfigurations` object that has one getter -
      `@configurations` which is an array of all the database objects.
      
      The configurations getter returns this object by default since it acts
      like a hash in most of the cases we need. For example if you need to
      access the default `development` database we can simply request it as we
      did before:
      
      ```
      ActiveRecord::Base.configurations["development"]
      ```
      
      This will return primary development database configuration hash:
      
      ```
      { "database" => "my_primary_db" }
      ```
      
      Internally all of Active Record has been converted to use the new
      objects. I've built this to be backwards compatible but allow for
      accessing the hash if needed for a deprecation period. To get the
      original hash instead of the object you can either add `to_h` on the
      configurations call or pass `legacy: true` to `configurations.
      
      ```
      ActiveRecord::Base.configurations.to_h
      => { "development => { "database" => "my_primary_db" } }
      
      ActiveRecord::Base.configurations(legacy: true)
      => { "development => { "database" => "my_primary_db" } }
      ```
      
      The new configurations object allows us to iterate over the Active
      Record configurations without losing the known environment or
      specification name for that configuration. You can also select all the
      configs for an env or env and spec. With this we can always ask
      any object what environment it belongs to:
      
      ```
      db_configs = ActiveRecord::Base.configurations.configurations_for("development")
      => #<ActiveRecord::DatabaseConfigurations:0x00007fd1acbdf800
        @configurations=[
          #<ActiveRecord::DatabaseConfigurations::HashConfig:0x00007fd1acbded10
            @env_name="development",@spec_name="primary",
            @config={"adapter"=>"sqlite3", "database"=>"db/development.sqlite3"}>,
          #<ActiveRecord::DatabaseConfigurations::HashConfig:0x00007fd1acbdea90
            @env_name="development",@spec_name="animals",
            @config={"adapter"=>"sqlite3", "database"=>"db/development.sqlite3"}>
      ]>
      
      db_config.env_name
      => "development"
      
      db_config.spec_name
      => "primary"
      
      db_config.config
      => { "adapter"=>"sqlite3", "database"=>"db/development.sqlite3" }
      ```
      
      The configurations object is more flexible than the configurations hash
      and will allow us to build on top of the connection management in order
      to add support for primary/replica connections, sharding, and
      constructing queries for associations that live in multiple databases.
      fdf3f0b9
  24. 19 4月, 2018 1 次提交
  25. 17 4月, 2018 1 次提交
    • U
      Fix test to allow IF NOT EXISTS in structure:dump · 74982a1b
      utilum 提交于
      Before:
      
      ```
      $ ruby -v
      ruby 2.5.1p57 (2018-03-29 revision 63029) [x86_64-linux]
      
      $ ruby -w  -Itest -Ilib -I../activesupport/lib -I../actionpack/lib -I../actionview/lib -I../activemodel/lib test/application/rake/multi_dbs_test.rb
      Run options: --seed 28744
      
      F
      
      Failure:
      ApplicationTests::RakeTests::RakeMultiDbsTest#test_db:migrate_and_db:structure:dump_and_db:structure:load_works_on_all_databases [test/application/rake/multi_dbs_test.rb:70]:
      Expected /CREATE TABLE \"books\"/ to match "CREATE TABLE IF NOT EXISTS \"schema_migrations\" (\"version\" varchar NOT NULL PRIMARY KEY);\nCREATE TABLE IF NOT EXISTS \"ar_internal_metadata\" (\"key\" varchar NOT NULL PRIMARY KEY, \"value\" varchar, \"created_at\" datetime NOT NULL, \"updated_at\" datetime NOT NULL);\nCREATE TABLE IF NOT EXISTS \"books\" (\"id\" integer PRIMARY KEY AUTOINCREMENT NOT NULL, \"title\" varchar, \"created_at\" datetime NOT NULL, \"updated_at\" datetime NOT NULL);\nCREATE TABLE sqlite_sequence(name,seq);\nINSERT INTO \"schema_migrations\" (version) VALUES\n('20180416201805');\n\n\n".
      ```
      74982a1b
  26. 10 4月, 2018 1 次提交
    • E
      Add multidb application test · fa5a028e
      eileencodes 提交于
      I realized I wasn't really testing some of the new rake tasks added so I
      built out this new test that uses a multi-db database.yml and allows us
      to run create/drop/migrate/schema:dump/schema:load and those that are
      namespaced like create:animals. This will make our testing more robust
      so we can catch problems quicker and set a good place to add future
      tests as these features evolve.
      fa5a028e