ActiveRecordのestablish_connectionを読む
ActiveRecord における DB との接続確立方法をきちんと理解できてなかったので、pry-byebug を使いながらコードを読み解いてみる。
Railsのversion は 4.2.0 という前提で。
establish_connection
sonots さん解説の通り、Rails を起動すると establish_connection メソッドが呼び出される。
# lib/active_record/connection_handling.rb def establish_connection(spec = nil) spec ||= DEFAULT_ENV.call.to_sym resolver = ConnectionAdapters::ConnectionSpecification::Resolver.new configurations spec = resolver.spec(spec) unless respond_to?(spec.adapter_method) raise AdapterNotFound, "database configuration specifies nonexistent #{spec.config[:adapter]} adapter" end remove_connection connection_handler.establish_connection self, spec end
binding.pry でデバッグ
適当な箇所に binding.pry を挿入して rails server するとコードを追いかけやすくてよい。
self は何?
[12] pry(ActiveRecord::Base)> self => ActiveRecord::Base
なぜ self が ActiveRecord::Base になるかというと、ConnectionHandling モジュールを extendしてるから。 https://github.com/rails/rails/blob/v4.2.0/activerecord/lib/active_record/base.rb#L276
spec とは?
[11] pry(ActiveRecord::Base)> p spec :development
resolver.spec(spec) の結果は?
[1] pry(ActiveRecord::Base):1> resolver.spec(spec) => #<ActiveRecord::ConnectionAdapters::ConnectionSpecification:0x007fa52311ae20 @adapter_method="mysql2_connection", @config={:adapter=>"mysql2", :encoding=>"utf8mb4", :database=>"development", :pool=>5, :username=>"root", :password=>nil, :socket=>"/tmp/mysql.sock"}>
connection_handler とは?
def self.connection_handler ActiveRecord::RuntimeRegistry.connection_handler || default_connection_handler end def self.connection_handler=(handler) ActiveRecord::RuntimeRegistry.connection_handler = handler end self.default_connection_handler = ConnectionAdapters::ConnectionHandler.new
https://github.com/rails/rails/blob/v4.2.0/activerecord/lib/active_record/core.rb#L100-L102
により、ActiveRecord::ConnectionAdapters::ConnectionHandler のインスタンスということになる。
そして...
# lib/active_record/connection_handling.rb def establish_connection(spec = nil) spec ||= DEFAULT_ENV.call.to_sym resolver = ConnectionAdapters::ConnectionSpecification::Resolver.new configurations spec = resolver.spec(spec) unless respond_to?(spec.adapter_method) raise AdapterNotFound, "database configuration specifies nonexistent #{spec.config[:adapter]} adapter" end remove_connection connection_handler.establish_connection self, spec end
の最後の一行は
# lib/active_record/connection_adapters/abstract/connection_pool.rb def establish_connection(owner, spec) @class_to_pool.clear raise RuntimeError, "Anonymous class is not allowed." unless owner.name owner_to_pool[owner.name] = ConnectionAdapters::ConnectionPool.new(spec) end
lib/active_record/connection_adapters/abstract/connection_pool.rb に行き着く。
実は owner_to_pool に instance を代入しているだけで接続していない。
いつ接続するのか?
[Ruby] 例えば、ActiveRecord の connection_pool を止める - sonots:blog
同じく sonots さんの解説の通り、クエリが投げられる時に connection pool から取得 or 新規接続という感じになっている。
↓
↓
↓
↓
という流れで、
def new_connection Base.send(spec.adapter_method, spec.config) end
がコネクションを貼ってる箇所になり、spec.adapter_method を send で呼び出すことになる。
[10] pry(ActiveRecord::Base)> p spec.adapter_method => "mysql2_connection"
まとめ
establish_connectionでは接続確立せず、実際はクエリを投げるタイミングである。
また、spec.adapter_methodには 例えば "mysql2_connection" などが格納されており、これを動的に実行することでconfig/database.yml に書かれた DB に接続する。