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はこの接続をキャッシュする。