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