1
0
Fork 0
mirror of https://github.com/rails/rails.git synced 2022-11-09 12:12:34 -05:00
rails--rails/activerecord/lib/active_record.rb

189 lines
5.1 KiB
Ruby
Raw Normal View History

# frozen_string_literal: true
#--
# Copyright (c) 2004-2020 David Heinemeier Hansson
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
# "Software"), to deal in the Software without restriction, including
# without limitation the rights to use, copy, modify, merge, publish,
# distribute, sublicense, and/or sell copies of the Software, and to
# permit persons to whom the Software is furnished to do so, subject to
# the following conditions:
#
# The above copyright notice and this permission notice shall be
# included in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#++
require "active_support"
require "active_support/rails"
require "active_model"
require "arel"
require "yaml"
2009-10-14 22:05:06 -04:00
require "active_record/version"
require "active_model/attribute_set"
Stop autoloading AbstractAdapter prematurely In 7254d23764f7abe8023f3daeb07d99ea1c8e777a, an autoload for `ConnectionAdapters::AbstractAdapter` was added to `active_record.rb`. Later in d6b923adbdfc9a4df20132f741bbfb43db12113c, a manual require for that class was added to `active_record/base.rb` as some constants under `ConnectionAdapters` weren't defined until `AbstractAdapter` was loaded. In 1efd88283ef68d912df215125951a87526768a51, the require was removed and replaced with an autoload in `active_record.rb`, above the previous one. Because the first autoload was for the `ConnectionAdapters` constant and the second one tried to create it, the autoload would fire immediately. Rather than fixing the autoload problem, the require had effectively just been moved from `active_record/base.rb` to `active_record.rb`. Instead of defining autoloads for constants under `ConnectionAdapters` in the `abstract_adapter.rb` file, we can create a separate, autoloaded `connection_adapters.rb` file for this purpose. To avoid a "circular require considered harmful" warning from Ruby, we have to fix the module nesting in `schema_creation.rb`, as a followup to e4108fc619e0f1c28cdec6049d31f2db01d56dfd. `AbstractAdapter` loads many other dependencies, so making it autoload properly has a noticeable impact on the load time of `active_record.rb`. Benchmark: $ cat test.rb require "bundler/setup" before = ObjectSpace.each_object(Module).count start = Process.clock_gettime(Process::CLOCK_MONOTONIC) require "active_record" finish = Process.clock_gettime(Process::CLOCK_MONOTONIC) after = ObjectSpace.each_object(Module).count puts "took #{finish - start} and created #{after - before} modules" Before: $ ruby test.rb took 0.47532399999909103 and created 901 modules After: $ ruby test.rb took 0.3299509999342263 and created 608 modules
2019-09-12 18:02:35 -04:00
require "active_record/errors"
2008-11-24 12:14:24 -05:00
module ActiveRecord
extend ActiveSupport::Autoload
autoload :Base
autoload :Callbacks
autoload :Core
autoload :ConnectionHandling
2013-04-05 02:21:48 -04:00
autoload :CounterCache
autoload :DynamicMatchers
autoload :DelegatedType
autoload :Enum
autoload :InternalMetadata
autoload :Explain
autoload :Inheritance
autoload :Integration
autoload :Migration
autoload :Migrator, "active_record/migration"
autoload :ModelSchema
autoload :NestedAttributes
autoload :NoTouching
autoload :TouchLater
autoload :Persistence
autoload :QueryCache
autoload :Querying
autoload :ReadonlyAttributes
autoload :RecordInvalid, "active_record/validations"
autoload :Reflection
autoload :RuntimeRegistry
autoload :Sanitization
autoload :Schema
autoload :SchemaDumper
autoload :SchemaMigration
autoload :Scoping
autoload :Serialization
2013-04-10 10:40:01 -04:00
autoload :StatementCache
autoload :Store
autoload :SignedId
2015-02-18 17:55:48 -05:00
autoload :Suppressor
autoload :Timestamp
autoload :Transactions
autoload :Translation
autoload :Validations
autoload :SecureToken
2009-12-22 18:27:37 -05:00
eager_autoload do
Stop autoloading AbstractAdapter prematurely In 7254d23764f7abe8023f3daeb07d99ea1c8e777a, an autoload for `ConnectionAdapters::AbstractAdapter` was added to `active_record.rb`. Later in d6b923adbdfc9a4df20132f741bbfb43db12113c, a manual require for that class was added to `active_record/base.rb` as some constants under `ConnectionAdapters` weren't defined until `AbstractAdapter` was loaded. In 1efd88283ef68d912df215125951a87526768a51, the require was removed and replaced with an autoload in `active_record.rb`, above the previous one. Because the first autoload was for the `ConnectionAdapters` constant and the second one tried to create it, the autoload would fire immediately. Rather than fixing the autoload problem, the require had effectively just been moved from `active_record/base.rb` to `active_record.rb`. Instead of defining autoloads for constants under `ConnectionAdapters` in the `abstract_adapter.rb` file, we can create a separate, autoloaded `connection_adapters.rb` file for this purpose. To avoid a "circular require considered harmful" warning from Ruby, we have to fix the module nesting in `schema_creation.rb`, as a followup to e4108fc619e0f1c28cdec6049d31f2db01d56dfd. `AbstractAdapter` loads many other dependencies, so making it autoload properly has a noticeable impact on the load time of `active_record.rb`. Benchmark: $ cat test.rb require "bundler/setup" before = ObjectSpace.each_object(Module).count start = Process.clock_gettime(Process::CLOCK_MONOTONIC) require "active_record" finish = Process.clock_gettime(Process::CLOCK_MONOTONIC) after = ObjectSpace.each_object(Module).count puts "took #{finish - start} and created #{after - before} modules" Before: $ ruby test.rb took 0.47532399999909103 and created 901 modules After: $ ruby test.rb took 0.3299509999342263 and created 608 modules
2019-09-12 18:02:35 -04:00
autoload :ConnectionAdapters
autoload :Aggregations
2009-12-22 18:27:37 -05:00
autoload :Associations
autoload :AttributeAssignment
2013-04-05 02:21:48 -04:00
autoload :AttributeMethods
2009-12-22 18:27:37 -05:00
autoload :AutosaveAssociation
autoload :LegacyYamlAdapter
2009-12-22 18:27:37 -05:00
autoload :Relation
autoload :AssociationRelation
autoload :NullRelation
autoload_under "relation" do
autoload :QueryMethods
autoload :FinderMethods
autoload :Calculations
autoload :PredicateBuilder
autoload :SpawnMethods
2010-02-12 11:53:51 -05:00
autoload :Batches
autoload :Delegation
end
autoload :Result
autoload :TableMetadata
autoload :Type
2009-12-22 18:27:37 -05:00
end
module Coders
autoload :YAMLColumn, "active_record/coders/yaml_column"
autoload :JSON, "active_record/coders/json"
end
2009-07-24 01:25:27 -04:00
module AttributeMethods
extend ActiveSupport::Autoload
2009-12-22 18:27:37 -05:00
eager_autoload do
autoload :BeforeTypeCast
autoload :Dirty
autoload :PrimaryKey
autoload :Query
autoload :Read
autoload :TimeZoneConversion
autoload :Write
autoload :Serialization
2009-12-22 18:27:37 -05:00
end
2009-07-24 01:25:27 -04:00
end
2008-11-24 12:14:24 -05:00
module Locking
extend ActiveSupport::Autoload
2009-12-22 18:27:37 -05:00
eager_autoload do
autoload :Optimistic
autoload :Pessimistic
end
2008-11-24 12:14:24 -05:00
end
module Scoping
extend ActiveSupport::Autoload
eager_autoload do
autoload :Named
autoload :Default
end
end
Adds basic automatic database switching to Rails 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.
2019-01-17 13:33:48 -05:00
module Middleware
extend ActiveSupport::Autoload
autoload :DatabaseSelector, "active_record/middleware/database_selector"
end
module Tasks
extend ActiveSupport::Autoload
autoload :DatabaseTasks
autoload :SQLiteDatabaseTasks, "active_record/tasks/sqlite_database_tasks"
autoload :MySQLDatabaseTasks, "active_record/tasks/mysql_database_tasks"
autoload :PostgreSQLDatabaseTasks,
"active_record/tasks/postgresql_database_tasks"
end
autoload :TestDatabases, "active_record/test_databases"
autoload :TestFixtures, "active_record/fixtures"
def self.eager_load!
super
ActiveRecord::Locking.eager_load!
ActiveRecord::Scoping.eager_load!
ActiveRecord::Associations.eager_load!
ActiveRecord::AttributeMethods.eager_load!
ActiveRecord::ConnectionAdapters.eager_load!
end
end
ActiveSupport.on_load(:active_record) do
Arel::Table.engine = self
2008-11-24 12:14:24 -05:00
end
2008-06-19 10:25:27 -04:00
2012-05-10 19:48:23 -04:00
ActiveSupport.on_load(:i18n) do
I18n.load_path << File.expand_path("active_record/locale/en.yml", __dir__)
2012-05-10 19:48:23 -04:00
end
YAML.load_tags["!ruby/object:ActiveRecord::AttributeSet"] = "ActiveModel::AttributeSet"
YAML.load_tags["!ruby/object:ActiveRecord::Attribute::FromDatabase"] = "ActiveModel::Attribute::FromDatabase"
YAML.load_tags["!ruby/object:ActiveRecord::LazyAttributeHash"] = "ActiveModel::LazyAttributeHash"
YAML.load_tags["!ruby/object:ActiveRecord::ConnectionAdapters::AbstractMysqlAdapter::MysqlString"] = "ActiveRecord::Type::String"