ActiveRecordのconnectionを読んだ
メモ
ActiveRecordのconnectionは、ConnectionHandling moduleのconnectionメソッドを呼んでいる。
このメソッドの中で、ConnectionHanlderのretrieve_connectionが呼び出され、最終的には、ConnectionHandlerのインスタンス変数(Hash)から、引数で渡された識別子をもとにConnectionPoolのインスタンスを取り出し、このConnectionPool#connectionを実行する。
ConnectionPoolは{スレッド=>接続}のHashで接続をキャッシュしている。(thread_cached_conns)
カレントスレッドに接続がなければ、ConnectionPoolから接続を取り出す(check out)。
checkout(正確にはacquire_connection)には3パターン。
1. ConnectionPoolが持っているConnectionのキュー(スレッドセーフ)から使用可能なものを取り出す。
2. ConnectionPoolのキャパシティ(config/database.ymlのpoolの値)を超えていなければ、新しい接続を作成する。
3. Connectionのキューに使用可能な接続が来るまで待つ。
2.の新しい接続のパターンについて読む。
ConnectionPoolが持つ接続の数と今から接続しようとしている数がConnectionPoolのキャパシティを超えていないかを確認する。(今から接続しようとしている数はインスタンス変数で、最初は0。新しい接続を作成し終わるまで+1される。これにより、同時に複数の接続が作成される際も、ConnectionPoolの接続の最大数を超えないようにしている。)
ここで超えなければ、new_connectionメソッドが呼ばれる。
この中で、ConnectionPoolが持つDBのconfigをもとに、対象のadapterのメソッドが呼ばれる。
ここでは、mysqlを想定する。
mysqlの場合、mysql2_connectionメソッドが呼ばれる。
ここで、ConnectionAdapters::Mysql2Adapterのインスタンスが作成される。
ここまで「接続」と呼んできたのは、このConnectionAdapters::Mysql2Adapterのインスタンス。
Mysql2Adapterのインスタンス変数のpoolをself(ConnectionPoolのインスタンス)にセットして、ConnectionPoolが持つ接続の配列に、Mysql2Adapterのインスタンスを追加する。
Mysql2Adapter#leaseを呼び出し、その「接続」をを使用するスレッドを指定する。
もし、この「接続」がカレントスレッドや他のスレッドに呼び出された際は、エラーとなる。
これにより、複数のスレッドが、1つの「接続」を使うことを防いでいる。
最終的に、ActiveRecord::Base#connectionは、この接続を返す。
同時に、ConnectionPoolはこの接続をキャッシュする。
ActiveRecordのestablish_connectionを読んだ
ActiveRecordのestablish_connectionを読んだのでメモ
やっていることは、複数のConnectionPoolを管理するConnectionHandlerのインスタンス変数(Hash)のvalueにデータベースの接続情報などConnectionPoolの情報を管理するオブジェクトをセットして、ConnectionPoolのインスタンスを取り出せる状態をつくること。
これにより、複数のDBに接続する際に、ConnectionPoolがインスタンス変数を確認して、キーをもとに正しいConnectionPoolを提供できるようになる。
読んだときのメモ
ActiveRecord::Base.establish_connectionは
ActiveRecord::ConecctionHandling.establish_connectionを呼ぶ
この中でActiveRecord::ConnectionAdapters::ConnectionHandlerのestablish_connectionを呼ぶ
ConnectionHandlerは複数のConnectionPoolを管理している。
複数DBを使用する際に、DBごとにConnectionPoolを持ち、それらの複数のConnectionPoolをConnectionHandlerが管理する。
ConnectionHandler#establish_connectionの中で、
databaseのconfigをもとにConnectionAdapters::PoolConfigのインスタンスが生成される。
PoolConfigはインスタンス変数として、各connectionを識別するためのconnection_specification_nameを持ち、デフォルトだと'primary'になる。PoolConfigは他にもdb_configというインスタンス変数を持つ。これは、config/database.ymlで設定したHashなどが入る。
ConnectionPoolをまとめるConnectionHandlerのインスタンスは、owner_to_pool_managerをConcurrent::Mapで持つ。keyがPoolConfigのインスタンスのconnection_specification_name、valueがPoolManagerのインスタンス。
establish_connectionの際に、valueが存在しなければ、新たにPoolManagerのインスタンスが作成される。
PoolManagerは、name_to_pool_configというHashを管理するためのClass。
キーには識別するための名前が入り(デフォルトで'default')、valueにPoolConfigが入る。
establish_connectionの中で、PoolManagerのname_to_pool_configもセットされる。
最後に、PoolConfigインスタンスのpoolメソッドが呼ばれる。
この中でConnectionPoolのインスタンスが生成される。
ConnectionPoolはインスタンス変数として、PoolConfigのインスタンスや、そのインスタンスのdb_configなどを持つ。
ConnectionPoolのinitializerの中で、ConnectionAdapters::ConnectionPool::Reaperのインスタンスが作成され、runメソッドが呼ばれる。
Reaperはpoolsとthreadsをインスタンス変数として持つ。poolsにはConnectionPoolのインスタンスが入る。threadsには、スレッドが入り、設定したfrequency秒間に一度生きているConnectionPoolのインスタンスに対してreapメソッドとflushメソッドを実行する。
ConnectionPoolのインスタンスがなくなれば、そのスレッドを終了させる。
ここでinitializeされたPoolConnectionのインスタンスは、PoolConfigのpoolで参照できる。