Closed6

TypeORM のコネクション調査

chocochoco

なんらかの原因で DB との接続が切れた場合、再接続が可能になっても DB への接続ができない。
事象としては下記 issue と類似している。
https://github.com/typeorm/typeorm/issues/6350

TypeORM のバージョン・ドライバは、

  • v0.3.x
  • MySQL

を使用している

chocochoco

TypeORM がクエリを実行する際、 Active Record・Repository に限らず MysqlQueryRunner.query() が実行される。 MysqlQueryRunner.query() 内で MysqlQueryRunner.connect() が参照されていて、connect 内でコネクションの取得が行われる。
https://github.com/typeorm/typeorm/blob/master/src/driver/mysql/MysqlQueryRunner.ts#L72-L97

コネクション取得の詳細は MysqlDriver.obtainSlaveConnection()MysqlDriver.obtainMasterConnection() のいずれか。(permalink は obtainSlaveConnection)
https://github.com/typeorm/typeorm/blob/master/src/driver/mysql/MysqlDriver.ts#L908-L919

ここで node-mysql2 の PoolCluster.getConnection() が呼び出される。
https://github.com/sidorares/node-mysql2/blob/master/lib/pool_cluster.js#L165-L178

PoolCluster.getConnection() 内では PoolNamespace.getConnection() が参照されていて、PoolCluster._getConnection() が呼び出される。
https://github.com/sidorares/node-mysql2/blob/master/lib/pool_cluster.js#L258-L276

ここでコネクションを取得しているが、規定回数のエラーが発生(デフォルトは 5 回)すると PoolCluster._increaseErrorCount() 内でコネクションとの接続が終了されてそう。
https://github.com/sidorares/node-mysql2/blob/master/lib/pool_cluster.js#L239-L250

PoolCluster の remove イベントはどこで登録されてるんだ・・・?

chocochoco

ざっと処理を追ってみたが、MysqlDriver.connect()DataSource.initialize() 内でしか呼び出されていない。これにより DataSource を使いまわしていると一度 PoolCluster からコネクションが削除された場合にコネクションの再生成が行われずクエリが実行できなくなる。

chocochoco

mariadb-connector-nodejs にも類似した issue が過去に立っていた。 issue の中でコラボレータは removeNodeErrorCount を Infinity に設定する方が良いのではとコメントしている。removeNodeErrorCount はコネクションを削除するまでに許容するエラーの発生回数で、デフォルトは 5 回となっている。
https://github.com/mariadb-corporation/mariadb-connector-nodejs/issues/138

コードを覗いてみるとデフォルトで Infinity が設定されていた。
https://github.com/mariadb-corporation/mariadb-connector-nodejs/blob/master/lib/config/cluster-options.js#L3-L17

実際に 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 を実行させるとクエリが実行された。

ただエラーの出力量が尋常じゃないので、リトライのタイムアウト設定ができないだろうか?

このスクラップは2023/02/13にクローズされました