CI を H2 -> MySQL にした時に遅いと思ったので試行錯誤してみた記録

今まで CI での RDBMS がらみのテストには H2 使ってたのですが、本番にもうちょっと近づけたいと思って、MySQL にしてみました。 その辺の調査と対応を備忘録的に残します。

前提条件

  • migration tool に Flyway 使った SpringBoot アプリ
  • 長いこと開発しているので migration ファイルの数は数十くらいあります
    • ある程度の時期で merge したいけど環境数が多いので migration の手間を考えて二の足を踏んでいる現状です

何をしようとしたか

本番は MySQL だが、CI では速度重視で H2 に接続するようにしていました。DDL やエラーハンドリングで RDBMS を意識するケースがちょいちょいあり、H2 用の処理ってアプリの価値に寄与していないのでは?というところから CI でも MySQL で行うようにして本質的でないコードを駆逐したいのがモチベーションでした。

何が起こったか

CI の時間が長くなりました。

これによる課題

CI にかかるコスト増

調査

実際にサンプルを作成して、時間を計測してみます。

1. H2 で実行

サンプル

2. MySQL8.0 に置き換えて実行

差分

サンプルの処理時間が少ないので誤差みたいなものかもしれませんが、確実に遅くなっています。 ローカルで実行するとより「詰まってる」感があります。

3. コード調査

コードを見たところ、@FlywayTest を使っている場合はデフォルトが invokeCleanDB=true になっていて、この設定だとテストメソッドが終わるたびに MySQL の場合は drop table を呼んでいました。で、次のテストメソッド実行前に migrate を呼ぶので時間がかかっていそうな感じでした。 このアプリは Repository のテストに @FlywayTest を付与しており、数十の migration ファイルを実行するので、確かに時間がかかりそうです。

4. 対応

drop table の箇所を truncate にすればテスト毎に最初から migration を行う必要がなくなるので早くなるのでは?と考え、新しく TestExecutionListener を作成してみます。

5. TestExecutionListener 追加して実行

差分 *1

素の状態よりマシになったように思えます。ローカルで実行しても「詰まる」感は薄れたように感じました。

まとめ

@FlywayTest を使ったテストを大量に使っている時に H2 以外にしたテストが遅いな、と思った時はテスト前後に何をしているか確認してみては如何でしょうか。 drop table でなく、truncate にするモードもあれば良いのに、とも思ったのですが、あんまりやりたくないみたいですね

*1:テスト用なので雑なのはご容赦ください