1. 24 4月, 2019 3 次提交
    • R
      Avoid redundant `attribute_alias?` before `attribute_alias` · 5575bd7b
      Ryuta Kamizono 提交于
      If we want to get alias resolved attribute finally, we can use
      `attribute_alias` directly.
      
      For that purpose, avoiding redundant `attribute_alias?` makes alias
      attribute access 40% faster.
      
      https://gist.github.com/kamipo/e427f080a27b46f50bc508fae3612a0e
      
      Before (2c0729d8):
      
      ```
      Warming up --------------------------------------
                user['id']   102.668k i/100ms
            user['new_id']    80.660k i/100ms
              user['name']    99.368k i/100ms
          user['new_name']    81.626k i/100ms
      Calculating -------------------------------------
                user['id']      1.431M (± 4.0%) i/s -      7.187M in   5.031985s
            user['new_id']      1.042M (± 4.2%) i/s -      5.243M in   5.039858s
              user['name']      1.406M (± 5.6%) i/s -      7.055M in   5.036743s
          user['new_name']      1.074M (± 3.6%) i/s -      5.387M in   5.024152s
      ```
      
      After (this change):
      
      ```
      Warming up --------------------------------------
                user['id']   109.775k i/100ms
            user['new_id']   103.303k i/100ms
              user['name']   105.988k i/100ms
          user['new_name']    99.618k i/100ms
      Calculating -------------------------------------
                user['id']      1.520M (± 6.7%) i/s -      7.574M in   5.011496s
            user['new_id']      1.485M (± 6.2%) i/s -      7.438M in   5.036252s
              user['name']      1.538M (± 5.4%) i/s -      7.737M in   5.049765s
          user['new_name']      1.516M (± 4.6%) i/s -      7.571M in   5.007293s
      ```
      5575bd7b
    • R
      Re-organize `init_internals` · 2c0729d8
      Ryuta Kamizono 提交于
      * Avoid duplicated `@new_record` assignment
      * Extract `define_attribute_methods` into `init_internals`
      2c0729d8
    • R
      Lazy allocate `@_start_transaction_state` · b1458218
      Ryuta Kamizono 提交于
      b1458218
  2. 22 4月, 2019 2 次提交
    • R
      PERF: 20% faster pk attribute access · b6828fc9
      Ryuta Kamizono 提交于
      I've realized that `user.id` is 20% slower than `user.name` in the
      benchmark (https://github.com/rails/rails/pull/35987#issuecomment-483882480).
      
      The reason that performance difference is that `self.class.primary_key`
      method call is a bit slow.
      
      Avoiding that method call will make almost attribute access faster and
      `user.id` will be completely the same performance with `user.name`.
      
      Before (02b5b8cb):
      
      ```
      Warming up --------------------------------------
                   user.id   140.535k i/100ms
                user['id']    96.549k i/100ms
                 user.name   158.110k i/100ms
              user['name']    94.507k i/100ms
             user.changed?    19.003k i/100ms
       user.saved_changes?    25.404k i/100ms
      Calculating -------------------------------------
                   user.id      2.231M (± 0.9%) i/s -     11.243M in   5.040066s
                user['id']      1.310M (± 1.3%) i/s -      6.565M in   5.012607s
                 user.name      2.683M (± 1.2%) i/s -     13.439M in   5.009392s
              user['name']      1.322M (± 0.9%) i/s -      6.615M in   5.003239s
             user.changed?    201.999k (±10.9%) i/s -      1.007M in   5.091195s
       user.saved_changes?    258.214k (±17.1%) i/s -      1.245M in   5.007421s
      ```
      
      After (this change):
      
      ```
      Warming up --------------------------------------
                   user.id   158.364k i/100ms
                user['id']   106.412k i/100ms
                 user.name   158.644k i/100ms
              user['name']   107.518k i/100ms
             user.changed?    19.082k i/100ms
       user.saved_changes?    24.886k i/100ms
      Calculating -------------------------------------
                   user.id      2.768M (± 1.1%) i/s -     13.936M in   5.034957s
                user['id']      1.507M (± 2.1%) i/s -      7.555M in   5.017211s
                 user.name      2.727M (± 1.5%) i/s -     13.643M in   5.004766s
              user['name']      1.521M (± 1.3%) i/s -      7.634M in   5.018321s
             user.changed?    200.865k (±11.1%) i/s -    992.264k in   5.044868s
       user.saved_changes?    269.652k (±10.5%) i/s -      1.344M in   5.077972s
      ```
      b6828fc9
    • R
      Remove never used `database_selector` class accessor · 02b5b8cb
      Ryuta Kamizono 提交于
      It was never used from the beginning.
      02b5b8cb
  3. 21 4月, 2019 1 次提交
    • R
      Avoid method call if `@transaction_state` is not finalized · f9326e56
      Ryuta Kamizono 提交于
      Method call in Ruby is a bit slow.
      
      This makes attribute access 10% faster by avoiding method call
      (`sync_with_transaction_state`).
      
      Before (96cf7e0e):
      
      ```
      Warming up --------------------------------------
                   user.id   131.291k i/100ms
                user['id']    91.786k i/100ms
                 user.name   151.605k i/100ms
              user['name']    92.664k i/100ms
             user.changed?    17.772k i/100ms
       user.saved_changes?    23.909k i/100ms
      Calculating -------------------------------------
                   user.id      1.988M (± 7.0%) i/s -      9.978M in   5.051474s
                user['id']      1.155M (± 5.8%) i/s -      5.783M in   5.022672s
                 user.name      2.450M (± 4.3%) i/s -     12.280M in   5.021234s
              user['name']      1.263M (± 2.1%) i/s -      6.394M in   5.066638s
             user.changed?    175.070k (±13.3%) i/s -    853.056k in   5.011555s
       user.saved_changes?    259.114k (±11.8%) i/s -      1.267M in   5.001260s
      ```
      
      After (this change):
      
      ```
      Warming up --------------------------------------
                   user.id   137.625k i/100ms
                user['id']    96.054k i/100ms
                 user.name   156.379k i/100ms
              user['name']    94.795k i/100ms
             user.changed?    18.172k i/100ms
       user.saved_changes?    24.337k i/100ms
      Calculating -------------------------------------
                   user.id      2.201M (± 0.5%) i/s -     11.010M in   5.002955s
                user['id']      1.320M (± 1.0%) i/s -      6.628M in   5.021293s
                 user.name      2.677M (± 1.6%) i/s -     13.449M in   5.024399s
              user['name']      1.314M (± 1.8%) i/s -      6.636M in   5.051444s
             user.changed?    190.588k (±11.1%) i/s -    944.944k in   5.065848s
       user.saved_changes?    262.782k (±12.1%) i/s -      1.290M in   5.028080s
      ```
      f9326e56
  4. 16 4月, 2019 1 次提交
    • R
      Fix dirty tracking after rollback. · 63ff495b
      Ryuta Kamizono 提交于
      Currently the rollback only restores primary key value, `new_record?`,
      `destroyed?`, and `frozen?`. Since the `save` clears current dirty
      attribute states, retrying save after rollback will causes no change
      saved if partial writes is enabled (by default).
      
      This makes `remember_transaction_record_state` remembers original values
      then restores dirty attribute states after rollback.
      
      Fixes #15018.
      Fixes #30167.
      Fixes #33868.
      Fixes #33443.
      Closes #33444.
      Closes #34504.
      63ff495b
  5. 10 4月, 2019 1 次提交
  6. 01 3月, 2019 1 次提交
  7. 27 2月, 2019 1 次提交
  8. 05 2月, 2019 1 次提交
    • R
      Improve performance of blank? and present? in an ActiveRecord::Base instance · cc2d614e
      Rafael Mendonça França 提交于
      With this benchmark:
      
          require "bundler/setup"
      
          require "active_record"
          require "benchmark/ips"
      
          # This connection will do for database-independent bug reports.
          ActiveRecord::Base.establish_connection(adapter: "sqlite3", database: ":memory:")
      
          ActiveRecord::Schema.define do
            create_table :posts, force: true do |t|
            end
          end
      
          class Post < ActiveRecord::Base
          end
      
          new_post = Post.new
      
          Benchmark.ips do |b|
            b.report("present?") do
              new_post.present?
            end
      
            b.report("blank?") do
              new_post.blank?
            end
          end
      
      Before:
      
          Warming up --------------------------------------
                      present?    52.147k i/100ms
                        blank?    53.077k i/100ms
          Calculating -------------------------------------
                      present?    580.184k (±21.8%) i/s -      2.555M in   5.427085s
                        blank?    601.537k (± 9.2%) i/s -      2.972M in   5.003503s
      
      After:
      
          Warming up --------------------------------------
                      present?   378.235k i/100ms
                        blank?   375.476k i/100ms
          Calculating -------------------------------------
                      present?     17.381M (± 7.5%) i/s -     86.238M in   5.001815s
                        blank?     17.877M (± 6.4%) i/s -     88.988M in   5.004634s
      
      This improvement is mostly because those methods were hitting
      `method_missing` on a lot of levels to be able to return the value.
      
      To avoid all this stack walking we are short-circuiting those methods.
      
      Closes #35059.
      cc2d614e
  9. 02 2月, 2019 1 次提交
    • E
      Add ability to change the names of the default handlers · 8d32346c
      Eileen Uchitelle 提交于
      When I wrote the `connected_to` and `connects_to` API's I wrote them
      with the idea in mind that it didn't really matter what the
      handlers/roles were called as long as those connecting to the roles knew
      which one wrote and which one read.
      
      With the introduction of the middleware Rails begins to assume it's
      `writing` and `reading` and there's no room for other roles. At GitHub
      we've been using this method for a long time so we have a ton of legacy
      code that uses different handler names `default` and `readonly`. We
      could rename all our code but I think this is better for a few reasons:
      
      - Legacy apps that have been using multiple databases for a long time
        can have an eaiser time switching.
      - If we later find this to cause more issues than it's worth we can
        easily deprecate.
      - We won't force old apps to rewrite the resolver middleware just to use
        a different handler.
      
      Adding the writing_role/reading_role required that I move the code that
      creates the first handler for writing to the railtie. If I didn't move
      this the core class would assign the handler before I was able to assign
      a new one in my configuration and I'd end up with 3 handlers instead of
      2.
      8d32346c
  10. 31 1月, 2019 1 次提交
    • E
      Adds basic automatic database switching to Rails · 0abcec41
      Eileen Uchitelle 提交于
      The following PR adds behavior to Rails to allow an application to
      automatically switch it's connection from the primary to the replica.
      
      A request will be sent to the replica if:
      
      * The request is a read request (`GET` or `HEAD`)
      * AND It's been 2 seconds since the last write to the database (because
      we don't want to send a user to a replica if the write hasn't made it
      to the replica yet)
      
      A request will be sent to the primary if:
      
      * It's not a GET/HEAD request (ie is a POST, PATCH, etc)
      * Has been less than 2 seconds since the last write to the database
      
      The implementation that decides when to switch reads (the 2 seconds) is
      "safe" to use in production but not recommended without adequate testing
      with your infrastructure. At GitHub in addition to the a 5 second delay
      we have a curcuit breaker that checks the replication delay
      and will send the query to a replica before the 5 seconds has passed.
      This is specific to our application and therefore not something Rails
      should be doing for you. You'll need to test and implement more robust
      handling of when to switch based on your infrastructure. The auto
      switcher in Rails is meant to be a basic implementation / API that acts
      as a guide for how to implement autoswitching.
      
      The impementation here is meant to be strict enough that you know how to
      implement your own resolver and operations classes but flexible enough
      that we're not telling you how to do it.
      
      The middleware is not included automatically and can be installed in
      your application with the classes you want to use for the resolver and
      operations passed in. If you don't pass any classes into the middleware
      the Rails default Resolver and Session classes will be used.
      
      The Resolver decides what parameters define when to
      switch, Operations sets timestamps for the Resolver to read from. For
      example you may want to use cookies instead of a session so you'd
      implement a Resolver::Cookies class and pass that into the middleware
      via configuration options.
      
      ```
      config.active_record.database_selector = { delay: 2.seconds }
      config.active_record.database_resolver = MyResolver
      config.active_record.database_operations = MyResolver::MyCookies
      ```
      
      Your classes can inherit from the existing classes and reimplment the
      methods (or implement more methods) that you need to do the switching.
      You only need to implement methods that you want to change. For example
      if you wanted to set the session token for the last read from a replica
      you would reimplement the `read_from_replica` method in your resolver
      class and implement a method that updates a new timestamp in your
      operations class.
      0abcec41
  11. 18 1月, 2019 1 次提交
    • R
      Ensure `StatementCache#execute` never raises `RangeError` · c196ca72
      Ryuta Kamizono 提交于
      Since 31ffbf8d, finder methods no longer raise `RangeError`. So
      `StatementCache#execute` is the only place to raise the exception for
      finder queries.
      
      `StatementCache` is used for simple equality queries in the codebase.
      This means that if `StatementCache#execute` raises `RangeError`, the
      result could always be regarded as empty.
      So `StatementCache#execute` just return nil in that range error case,
      and treat that as empty in the caller side, then we can avoid catching
      the exception in much places.
      c196ca72
  12. 02 1月, 2019 1 次提交
  13. 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
  14. 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
  15. 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
  16. 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
  17. 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
  18. 29 9月, 2018 1 次提交
    • E
      Only define attribute methods from schema cache · 00075016
      Eugene Kenny 提交于
      To define the attribute methods for a model, Active Record needs to know
      the schema of the underlying table, which is usually achieved by making
      a request to the database. This is undesirable behaviour while the app
      is booting, for two reasons: it makes the boot process dependent on the
      availability of the database, and it means every new process will make
      one query for each table, which can cause issues for large applications.
      
      However, if the application is using the schema cache dump feature, then
      the schema cache already contains the necessary information, and we can
      define the attribute methods without causing any extra database queries.
      00075016
  19. 12 9月, 2018 4 次提交
  20. 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
  21. 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
  22. 02 8月, 2018 1 次提交
  23. 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
  24. 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
  25. 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
  26. 21 3月, 2018 1 次提交
  27. 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
  28. 25 1月, 2018 2 次提交
  29. 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
  30. 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