1. 21 11月, 2018 1 次提交
    • E
      Make connection handler per thread instead of per fiber · 5ce3e022
      Eileen Uchitelle 提交于
      The connection handler was using the RuntimeRegistry which kind of
      implies it's a per thread registry. But it's actually per fiber.
      
      If you have an application that uses fibers and you're using multiple
      databases, when you switch the connection handler to swap connections
      new fibers running on the same thread used to get a different connection
      id. This PR changes the code to actually use a thread so that we get
      the same connection.
      
      Fixes https://github.com/rails/rails/issues/30047
      
      [Eileen M. Uchitelle, Aaron Patterson, & Arthur Neeves]
      5ce3e022
  2. 06 11月, 2018 1 次提交
    • E
      Fix inspect with non-primary key id attribute · 65cd0fda
      Eugene Kenny 提交于
      The `read_attribute` method always returns the primary key when asked to
      read the `id` attribute, even if the primary key isn't named `id`, and
      even if another attribute named `id` exists.
      
      For the `inspect`, `attribute_for_inspect` and `pretty_print` methods,
      this behaviour is undesirable, as they're used to examine the internal
      state of the record. By using `_read_attribute` instead, we'll get the
      real value of the `id` attribute.
      65cd0fda
  3. 19 10月, 2018 1 次提交
    • Y
      Implement AR#inspect using ParamterFilter. · 32b03b46
      Yoshiyuki Kinjo 提交于
      AR instance support `filter_parameters` since #33756.
      Though Regex or Proc is valid as `filter_parameters`,
      they are not supported as AR#inspect.
      
      I also add :mask option and #filter_params to
      `ActiveSupport::ParameterFilter#new` to implement this.
      32b03b46
  4. 17 10月, 2018 1 次提交
    • R
      Consolidate duplicated code that initializing an empty model object · 1198a388
      Ryuta Kamizono 提交于
      `init_with` and `init_from_db` are almost the same code except decode
      `coder`.
      
      And also, named `init_from_db` is a little misreading, a raw values hash
      from the database is already converted to an attributes object by
      `attributes_builder.build_from_database`, so passed `attributes` in that
      method is just an attributes object.
      
      I renamed that method to `init_with_attributes` since the method is
      shared with `init_with` to initialize an empty model object.
      1198a388
  5. 11 10月, 2018 1 次提交
    • E
      Basic API for connection switching · 31021a8c
      Eileen Uchitelle 提交于
      This PR adds the ability to 1) connect to multiple databases in a model,
      and 2) switch between those connections using a block.
      
      To connect a model to a set of databases for writing and reading use
      the following API. This API supercedes `establish_connection`. The
      `writing` and `reading` keys represent handler / role names and
      `animals` and `animals_replica` represents the database key to look up
      the configuration hash from.
      
      ```
      class AnimalsBase < ApplicationRecord
        connects_to database: { writing: :animals, reading: :animals_replica }
      end
      ```
      
      Inside the application - outside the model declaration - we can switch
      connections with a block call to `connected_to`.
      
      If we want to connect to a db that isn't default (ie readonly_slow) we
      can connect like this:
      
      Outside the model we may want to connect to a new database (one that is
      not in the default writing/reading set) - for example a slow replica for
      making slow queries. To do this we have the `connected_to` method that
      takes a `database` hash that matches the signature of `connects_to`. The
      `connected_to` method also takes a block.
      
      ```
      AcitveRecord::Base.connected_to(database: { slow_readonly: :primary_replica_slow }) do
        ModelInPrimary.do_something_thats_slow
      end
      ```
      
      For models that are already loaded and connections that are already
      connected, `connected_to` doesn't need to pass in a `database` because
      you may want to run queries against multiple databases using a specific
      role/handler.
      
      In this case `connected_to` can take a `role` and use that to swap on
      the connection passed. This simplies queries - and matches how we do it
      in GitHub. Once you're connected to the database you don't need to
      re-connect, we assume the connection is in the pool and simply pass the
      handler we'd like to swap on.
      
      ```
      ActiveRecord::Base.connected_to(role: :reading) do
        Dog.read_something_from_dog
        ModelInPrimary.do_something_from_model_in_primary
      end
      ```
      31021a8c
  6. 12 9月, 2018 4 次提交
  7. 07 9月, 2018 1 次提交
    • Z
      Configuration item `config.filter_parameters` could also filter out sensitive... · 180dcd1b
      Zhang Kang 提交于
      Configuration item `config.filter_parameters` could also filter out sensitive value of database column when call `#inspect`
      
      * Why
      Some sensitive data will be exposed in log accidentally by calling `#inspect`, e.g.
      
      ```ruby
      @account = Account.find params[:id]
      payload = { account: @account }
      logger.info "payload will be #{ payload }"
      ```
      
      All the information of `@account` will be exposed in log.
      
      * Solution
      Add a class attribute filter_attributes to specify which values of columns shouldn't be exposed.
      This attribute equals to `Rails.application.config.filter_parameters` by default.
      
      ```ruby
      Rails.application.config.filter_parameters += [:credit_card_number]
      Account.last.insepct # => #<Account id: 123, credit_card_number: [FILTERED] ...>
      ```
      180dcd1b
  8. 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
  9. 02 8月, 2018 1 次提交
  10. 27 6月, 2018 2 次提交
    • A
      define attribute methods in `init_from_db` · a01de4de
      Aaron Patterson 提交于
      Now that `allocate` is removed, we need to define attribute methods in
      all "init" methods.
      a01de4de
    • A
      Call initialize after allocate · 634c53ac
      Aaron Patterson 提交于
      If someone calls allocate on the object, they'd better also call an
      initialization routine too (you can't expect allocate to do any
      initialization work).  Before this commit, AR objects that are
      instantiated from the database would call `define_attribute_methods`
      twice.
      634c53ac
  11. 26 6月, 2018 1 次提交
    • A
      Speed up homogeneous AR lists / reduce allocations · 7d58fa87
      Aaron Patterson 提交于
      This commit speeds up allocating homogeneous lists of AR objects.  We
      can know if the result set contains an STI column before initializing
      every AR object, so this change pulls the "does this result set contain
      an STI column?" test up, then uses a specialized instantiation function.
      This way we only have to check for an STI column once rather than N
      times.
      
      This change also introduces a new initialization function that is meant
      for use when allocating AR objects that come from the database.  Doing
      this allows us to eliminate one hash allocation per AR instance.
      
      Here is a benchmark:
      
      ```ruby
      require 'active_record'
      require 'benchmark/ips'
      
      ActiveRecord::Base.establish_connection adapter: "sqlite3", database: ":memory:"
      
      ActiveRecord::Migration.verbose = false
      
      ActiveRecord::Schema.define do
        create_table :users, force: true do |t|
          t.string :name
          t.timestamps null: false
        end
      end
      
      class User < ActiveRecord::Base; end
      
      2000.times do
        User.create!(name: "Gorby")
      end
      
      Benchmark.ips do |x|
        x.report("find") do
          User.limit(2000).to_a
        end
      end
      ```
      
      Results:
      
      Before:
      
      ```
      [aaron@TC activerecord (master)]$ be ruby -I lib:~/git/allocation_tracer/lib speed.rb
      Warming up --------------------------------------
                      find     5.000  i/100ms
      Calculating -------------------------------------
                      find     56.192  (± 3.6%) i/s -    285.000  in   5.080940s
      ```
      
      After:
      
      ```
      [aaron@TC activerecord (homogeneous-allocation)]$ be ruby -I lib:~/git/allocation_tracer/lib speed.rb
      Warming up --------------------------------------
                      find     7.000  i/100ms
      Calculating -------------------------------------
                      find     72.204  (± 2.8%) i/s -    364.000  in   5.044592s
      ```
      7d58fa87
  12. 22 3月, 2018 3 次提交
    • D
      Move fk_ignore_pattern from config.active_record to SchemaDumper · f6e612b2
      David Stosik 提交于
      This makes more sense, as the foreign key ignore pattern is only used by
      the schema dumper.
      f6e612b2
    • E
      Refactor configs_for and friends · 4e663c1e
      eileencodes 提交于
      Moves the configs_for and DatabaseConfig struct into it's own file. I
      was considering doing this in a future refactoring but our set up forced
      me to move it now. You see there are `mattr_accessor`'s on the Core
      module that have default settings. For example the `schema_format`
      defaults to Ruby. So if I call `configs_for` or any methods in the Core
      module it will reset the `schema_format` to `:ruby`. By moving it to
      it's own class we can keep the logic contained and avoid this
      unfortunate issue.
      
      The second change here does a double loop over the yaml files. Bear with
      me...
      
      Our tests dictate that we need to load an environment before our rake
      tasks because we could have something in an environment that the
      database.yml depends on. There are side-effects to this and I think
      there's a deeper bug that needs to be fixed but that's for another
      issue. The gist of the problem is when I was creating the dynamic rake
      tasks if the yaml that that rake task is calling evaluates code (like
      erb) that calls the environment configs the code will blow up because
      the environment is not loaded yet.
      
      To avoid this issue we added a new method that simply loads the yaml and
      does not evaluate the erb or anything in it. We then use that yaml to
      create the task name. Inside the task name we can then call
      `load_config` and load the real config to actually call the code
      internal to the task. I admit, this is gross, but refactoring can't all
      be pretty all the time and I'm working hard with `@tenderlove` to
      refactor much more of this code to get to a better place re connection
      management and rake tasks.
      4e663c1e
    • E
      Add DatabaseConfig Struct and associated methods · 1756094b
      eileencodes 提交于
      Passing around and parsing hashes is easy if you know that it's a two
      tier config and each key will be named after the environment and each
      value will be the config for that environment key.
      
      This falls apart pretty quickly with three-tier configs. We have no idea
      what the second tier will be named (we know the first is primary but we
      don't know the second), we have no easy way of figuring out
      how deep a hash we have without iterating over it, and we'd have to do
      this a lot throughout the code since it breaks all of Active Record's
      assumptions regarding configurations.
      
      These methods allow us to pass around objects instead. This will allow
      us to more easily parse the configs for the rake tasks. Evenually I'd
      like to replace the Active Record connection management that passes
      around config hashes to use these methods as well but that's much
      farther down the road.
      
      `walk_configs` takes an environment, specification name, and a config
      and turns them into DatabaseConfig struct objects so we can ask the
      configs questions like:
      
      ```
      db_config.spec_name
      => animals
      
      db_config.env_name
      => development
      
      db_config.config
      { :adapter => mysql etc }
      ```
      
      `db_configs` loops through all given configurations and returns an array
      of DatabaseConfig structs for each config in the yaml file.
      
      and lastly `configs_for` takes an environment and either returns the
      spec name and config if a block is given or returns an array of
      DatabaseConfig structs just for the given environment.
      1756094b
  13. 21 3月, 2018 1 次提交
  14. 20 3月, 2018 1 次提交
    • D
      Expose foreign key name ignore pattern in configuration · d3fd4e4e
      David Stosik 提交于
      When dumping the database schema, Rails will dump foreign key names only
      if those names were not generate by Rails. Currently this is determined
      by checking if the foreign key name is `fk_rails_` followed by
      a 10-character hash.
      
      At [Cookpad](https://github.com/cookpad), we use
      [Departure](https://github.com/departurerb/departure) (Percona's
      pt-online-schema-change runner for ActiveRecord migrations) to run migrations.
      Often, `pt-osc` will make a copy of a table in order to run a long migration
      without blocking it. In this copy process, foreign keys are copied too,
      but [their name is prefixed with an underscore to prevent name collision
      ](https://www.percona.com/doc/percona-toolkit/LATEST/pt-online-schema-change.html#cmdoption-pt-online-schema-change-alter-foreign-keys-method).
      
      In the process described above, we often end up with a development
      database that contains foreign keys which name starts with `_fk_rails_`.
      That name does not match the ignore pattern, so next time Rails dumps
      the database schema (eg. when running `rake db:migrate`), our
      `db/schema.rb` file ends up containing those unwanted foreign key names.
      This also produces an unwanted git diff that we'd prefer not to commit.
      
      In this PR, I'd like to suggest a way to expose the foreign key name
      ignore pattern to the Rails configuration, so that individual projects
      can decide on a different pattern of foreign keys that will not get
      their names dumped in `schema.rb`.
      d3fd4e4e
  15. 25 1月, 2018 2 次提交
  16. 14 12月, 2017 1 次提交
    • O
      Log call site for all queries · 3876defd
      Olivier Lacan 提交于
      This new ActiveRecord configuration option allows you to easily
      pinpoint what line of application code is triggering SQL queries in the
      development log by appending below each SQL statement log the line of
      Ruby code that triggered it.
      
      It’s useful with N+1 issues, and to locate stray queries.
      
      By default this new option ignores Rails and Ruby code in order to
      surface only callers from your application Ruby code or your gems.
      
      It is enabled on newly generated Rails 5.2 applications and can be
      enabled on existing Rails applications:
      
      ```ruby
      Rails.application.configure do
        # ...
        config.active_record.verbose_query_logs = true
      end
      ```
      
      The `rails app:upgrade` task will also add it to
      `config/development.rb`.
      
      This feature purposely avoids coupling with
      ActiveSupport::BacktraceCleaner since ActiveRecord can be used without
      ActiveRecord. This decision can be reverted in the future to allow more
      configurable backtraces (the exclusion of gem callers for example).
      3876defd
  17. 20 11月, 2017 1 次提交
    • R
      Prevent extra `spawn` to make `klass.all` faster (#29009) · eeaf9cf6
      Ryuta Kamizono 提交于
      These extra `spawn` are called via `klass.all` and `klass.all` is called
      everywhere in the internal. Avoiding the extra `spawn` makes` klass.all`
      30% faster for STI classes.
      
      https://gist.github.com/kamipo/684d03817a8115848cec8e8b079560b7
      
      ```
      Warming up --------------------------------------
             fast relation     4.410k i/100ms
             slow relation     3.334k i/100ms
      Calculating -------------------------------------
             fast relation     47.373k (± 5.2%) i/s -    238.140k in   5.041836s
             slow relation     35.757k (±15.9%) i/s -    176.702k in   5.104625s
      
      Comparison:
             fast relation:    47373.2 i/s
             slow relation:    35756.7 i/s - 1.32x  slower
      ```
      eeaf9cf6
  18. 09 11月, 2017 2 次提交
  19. 24 10月, 2017 1 次提交
  20. 22 8月, 2017 1 次提交
  21. 12 8月, 2017 1 次提交
  22. 04 8月, 2017 2 次提交
  23. 20 7月, 2017 1 次提交
  24. 19 7月, 2017 1 次提交
  25. 17 7月, 2017 1 次提交
  26. 16 7月, 2017 1 次提交
  27. 02 7月, 2017 1 次提交
  28. 01 7月, 2017 1 次提交
  29. 03 6月, 2017 1 次提交
  30. 15 3月, 2017 1 次提交
  31. 15 1月, 2017 1 次提交