はじめに

猫好きエンジニアの福田です。

あるプロジェクトでAuroraの接続数が足りなくなることがあり、RDS Proxyを導入したのですが、
30分程度のバッチ処理でたまに以下のような接続エラーが発生することがありました。
調査に少し時間がかかったので、何が原因でどうすれば解消したかを備忘として残しておきます。

# エラー抜粋
"error": ActiveRecord::StatementInvalid
"message": Mysql2::Error::ConnectionError: Lost connection to MySQL server during query

エラーが発生した原因

エラーが発生したバッチは以下のような流れで動いていました。

  • バッチ起動
    1. RDS操作
    2. RDSを操作しない処理(外部アクセス等)を30分以上実施
    3. RDS操作 <--- ここでエラーが発生

以上から、RDSを操作しない処理が30分以上続いたのちに、RDSを操作することでエラーが発生することがわかりました。
(30分未満だと発生しない)
さらに調査を進めると、この30分というのはRDS Proxyの IdleClientTimeout が初期値の 1,800秒(30分)であることも判明し、
この値を伸ばすことでエラーが発生しなくなることがわかりました。

https://docs.aws.amazon.com/ja_jp/AmazonRDS/latest/UserGuide/rds-proxy-managing.html#rds-proxy-connection-pooling-tuning.idleclienttimeout

IdleClientTimeout
プロキシがクライアント接続を閉じるまでの間、接続がアイドル状態を継続できる時間を指定できます。デフォルトは 1,800 秒 (30 分) です。

ちなみに、RDS Proxy導入前で発生しなかったのは、Aurora MySQLの wait_timeoutが初期値の28,800秒(8時間)となっていたためです。
整理すると以下が重なってしまいエラーが発生したことになります。

  • RDS Proxyの IdleClientTimeout が初期値の 1,800秒(30分)になっていた
  • エラーが発生したバッチはRDSを操作後、30分を超えてからRDSの操作をしようとした(30分以内にRDS操作をしなかった)

CDKで設定変更

CDKでは以下のようにDatabaseProxyのPropsからidleClientTimeoutが設定できます。
未設定だと、デフォルト値の 1,800秒(30分)が設定されるので、適宜変更してください。
今回はMySQLの wait_timeout の設定値(28,800秒)の同値とした例を以下に記載していますので参考にしてください。

# CDKでのRDS Proxyリソース作成例
const dbProxyName = this.createResourceName('rds-proxy')
this.rdsProxy = new rds.DatabaseProxy(this.scope, dbProxyName, {
  dbProxyName,
  vpc: this.vpc.vpc,
  vpcSubnets: this.vpc.isolatedSubnets(),
  proxyTarget: rds.ProxyTarget.fromCluster(this.aurora.cluster),
  secrets: [this.auroraSecrets],
  maxConnectionsPercent: this.props.rdsProxy.maxConnectionsPercent,
  maxIdleConnectionsPercent: this.props.rdsProxy.maxIdleConnectionsPercent,
  securityGroups: [this.securityGroup],
  debugLogging: false,
  requireTLS: true,
  idleClientTimeout: cdk.Duration.hours(8), // <--- ★IdleClientTimeoutを28,800秒(8時間)に変更
  // ...
})

cdk diff結果

$ cdk diff

~省略~

Resources
[~] AWS::RDS::DBProxy my-project-dev-rds-proxy myprojectdevrdsproxyCBFE0490 
 └─ [~] IdleClientTimeout
     ├─ [-] 1800
     └─ [+] 28800

その他対応案

ご紹介した対応以外にも以下のような対応をとることで解消できそうです。
今回のプロジェクトでは、今まで動いていたバッチを実装に手を入れることなく動かす必要がありましたので、IdleClientTimeout を変更することで解消することとしました。

  • 接続エラーを検知して自動で再接続するようにする
  • バッチを分割するなど実装を見直して長時間待たないようにする

まとめ

今回はRDS Proxy利用時のLost connection to MySQL server during queryエラーの原因と解消方法をまとめました。
基本的にはidleClientTimeoutの初期値のままで問題ないかと思いますが、長時間のバッチでこのエラーが発生して困っているという方の参考になれば幸いです。