dip Engineer Blog

Engineer Blog
ディップ株式会社のエンジニアによる技術ブログです。
弊社はバイトル・はたらこねっとなど様々なサービスを運営しています。

Oracleを利用したテストを高速化した話

皆さんこんにちは、ディップ株式会社でテックリードをしている @bikun_bikun です。

さて、今回は弊社のCI事情について少しお話しようと思います。

開発環境について

弊社ではローカル開発環境にDockerの採用が広がり、環境のポータビリティ性があがってきています。
また、近年作成しているプロダクトについてはユニットテストの積極採用を行っており、手軽にテストが行えるような環境になってきました。

テストが手軽に行えるようになったことで、CI/CDの中で自動テストの導入も進んできているように思います。

ユニットテストで話題になりがち?なデータベースについて

ユニットテストで話題に取り上げられることが多いデータベース部分のテストです。
mockを使うのか、DBに接続するのかといったたぐいのお話です。
私の所属しているチームでは、DB接続部分は実際にDBに接続し、エラー系で再現が難しいものはmockで対応しようという方針を取っています。

弊社では複数のDBを利用しているのですが、その中で厄介なのがタイトルにもなっているOracleです。
次セクションでそのお話をしていきたいと思います。

DockerでOracleを立てる

冒頭で取り上げた通り、Dockerを利用しているので、DBもそのままDockerで作っていく方向で舵を切ってきました。
OracleについてはDockerの公式イメージが存在しないため、自前で用意する必要があります。
※Dockerイメージの作成については今回の趣旨から外れるため割愛します。

頑張ってイメージを作成するとそのイメージサイズの大きさにびっくりすると思います。(確認したところ4GBほどの容量となっていました。)
私の所属しているチームはCI/CDをAWS CodeBuildで賄っているため、このイメージを毎回Docker Hubからpullしてくると結構なコストになってしまうと考え、ECRで管理していました。
ローカルなら一度pullしてしまえばそうそうイメージ自体の変更は入らないため、そこまで立ち上げに時間がかかるという事はありません。
しかし、CI/CDの中ではそうも行きません、CodeBuildとECRの組み合わせでもテストの実行時間で10〜12分ほどかかってしまっている状態でした。

CIの高速化をしたい

前のセクションでお話した通り、OracleのDockerイメージが特大サイズになっており、テストの時間が10~12分かかっている状態では良い開発体験とは言いづらい状態です。
githubにpushしたあとテストが終わるまで待つわけですが、ちょっとした休憩時間になってしまいます。
また、pushした後に、アレやコレを忘れてたということが起こり再pushみたいなことになると目も当てられません。
そこでCIの高速化に着手しました。

現在の状態の把握

現在のCIを超ざっくり図解すると下記になります。

asis

  1. CIの中でDockerの立ち上げを行う
    • Oracleが立ち上がったらDDLを流し込み初期データの登録を行う
  2. テストの実施
  3. 後片付け(Dockerの停止など)

1のDockerの立ち上げでECRからOracleのDockerイメージをpullするのですが、前述の通りここに時間がかかっている状態です。
DDLの流し込みも一緒に行っていたので、pullに時間がかかるのか、DDLの流し込みに時間がかかるのか?を調査したところ、DDLの流し込みは一瞬で終わっており、大半がpullの時間となっていました。

高速化の作戦

pullに時間がかかっているため、pullの時間をどうにかしないといけないということがわかりました。
この問題の解消方法としてテスト用のDBインスタンスを立てておき、そこにアクセスすることでpullの時間をカットするという方針を立てました。
この時問題になるのが、テストが並列で動いたり、同じDBを見ている他のテストが動いたときにデータのバッティング等が発生し、テストが正常に動かなくなってしまうことです。

テストごとにDBインスタンスを分けられれば上記の様な問題は起きないのですが、流石に毎回インスタンスを作成していてはECR pullのほうが早いみたいなことが起きてしまいます。
そこで、予め1つテスト用のインスタンスを作成しておき、インスタンス内でDBを分ける方向にしました。
インフラチームに相談したところ、Oracleの仕様でDBの作成は予めディスク領域を確保して置かなければならないということがわかり、テストごとのDBのcreateは現実的ではないとわかりました。
どのような解決方法があるか、引き続き相談したところユーザごとに分ける方法はどうかという話になりました。

Oracleではユーザごとにテーブルを管理しているため、ユーザが分かれると同一テーブルでも別テーブル扱いになるということで、この方式にすることにしました。

どのようにユーザを分けたか

いくつかの方法があると思いますが、弊社ではユーザ名をプロダクトの識別子(アプリ名等)+gitのcommit hashにする方法を選択しました。
docker pullをするタイミングでcommit hashを取得し、Oracleのユーザを作成、テーブル作成のDDLを流し込みます。

テストを行い、finallyの処理でOracle ユーザの削除を行うようにしています。 ※下記図参照

tobe

高速化結果

変更前は10~12分ほどかかっていたテストが3分ほどまで縮まりました。 Oracleのpullとserviceの立ち上げに時間を食っていたので当然といえば当然かと思います。

pushしたあと、「アレ忘れてた、これ入れ忘れた」みたいなことが起こっても「まぁ待てるなぁ」程度の時間で済む様になりました。

また、副次効果になりますが、CodeBuildでOracleを動かすのにCPU, Memoryともにスペックアップしていましたが、Oracleが外に出たことによって、スペックを戻すこともできるようになりました。

まとめ

なんでもDocker化という流れに逆らうような変更でしたが、それぞれ適材適所を見つけてそこにフィットさせていくことが大切なのではないかと思います。
また、今後もこのような小さな改善含め、開発体験をあげられるような施策を取り入れていきたいなと思っています。

最後までお読みいただきありがとうございました。

以上