TypeORM のコネクション調査
なんらかの原因で DB との接続が切れた場合、再接続が可能になっても DB への接続ができない。
事象としては下記 issue と類似している。
TypeORM のバージョン・ドライバは、
- v0.3.x
- MySQL
を使用している
TypeORM v0.3 では DataSource を元に DB と通信を行っている。
DataSource.initialize() を実行すると、設定されたドライバを使って DB との接続を作成する。
MysqlDriver は MysqlConnectionOptions.connectorPackage プロパティが指定されなかった場合、 mysql2 の読み込みを行う。
MysqlDriver.connect() が実行されると mysql2 を経由してコネクションプールの作成が行われる。
今回はレプリケーションの設定をしているので createPoolCluster() が実行される。
TypeORM がクエリを実行する際、 Active Record・Repository に限らず MysqlQueryRunner.query() が実行される。 MysqlQueryRunner.query() 内で MysqlQueryRunner.connect() が参照されていて、connect 内でコネクションの取得が行われる。
コネクション取得の詳細は MysqlDriver.obtainSlaveConnection()、MysqlDriver.obtainMasterConnection() のいずれか。(permalink は obtainSlaveConnection)
ここで node-mysql2 の PoolCluster.getConnection() が呼び出される。
PoolCluster.getConnection() 内では PoolNamespace.getConnection() が参照されていて、PoolCluster._getConnection() が呼び出される。
ここでコネクションを取得しているが、規定回数のエラーが発生(デフォルトは 5 回)すると PoolCluster._increaseErrorCount() 内でコネクションとの接続が終了されてそう。
PoolCluster の remove イベントはどこで登録されてるんだ・・・?
ざっと処理を追ってみたが、MysqlDriver.connect() が DataSource.initialize() 内でしか呼び出されていない。これにより DataSource を使いまわしていると一度 PoolCluster からコネクションが削除された場合にコネクションの再生成が行われずクエリが実行できなくなる。
mariadb-connector-nodejs にも類似した issue が過去に立っていた。 issue の中でコラボレータは removeNodeErrorCount を Infinity に設定する方が良いのではとコメントしている。removeNodeErrorCount はコネクションを削除するまでに許容するエラーの発生回数で、デフォルトは 5 回となっている。
コードを覗いてみるとデフォルトで Infinity が設定されていた。
実際に DataSource に同様なオプションを設定すると下記のようになる。
new DataSource({
  type: 'mysql',
  replication: {
    removeNodeErrorCount: Infinity,
    master: {
      // master connection...
    },
    slaves: [
      // slave connections...
    ],
  },
});
上記をローカルで試してみた。アプリケーションを起動した状態で一度 mysql.server stop を実行させる。その後、任意の方法でクエリを実行させると [Error] PoolCluster : Error: connect ECONNREFUSED 127.0.0.1:3306 のエラーが出力され続けた状態になる。この状態で mysql.server start を実行させるとクエリが実行された。
ただエラーの出力量が尋常じゃないので、リトライのタイムアウト設定ができないだろうか?
