KAKEHASHI Tech Blog

カケハシのEngineer Teamによるブログです。

バッチ処理のリファクタリングも恐くない!Musubi Insightでのテスト事例をご紹介!

こんにちは。 カケハシでMusibi Insightのバックエンドエンジニアをしている高田です。

Musubi Insightとは、薬局の経営改善に役立つさまざまなデータを可視化しているプロダクトであり、そのデータはAirflowAWS Glueを利用した日次の夜間バッチ処理によって生成されています。

バッチ処理に使っている技術や仕組み等は、以下の記事で紹介させていただいています。

そんなバッチ処理を、パフォーマンス改善や新たな機能追加のための準備としてリファクタリング行なうことがあります。

ただ、バッチ処理のリファクタリングはテストがしづらかったり、思いもよらぬ形で後続の処理に影響を与えてしまう可能性があったりと、なかなか神経をすり減らす作業となっていました...

そこで、この度リファクタリング後の検証を効率よく確実に行う処理を構築したので、本記事にて紹介できればと思います!

Musubi InsightのワークフローE2Eテストについて

今回構築した方法は、ワークフローのE2Eテストと組み合わせたものとなっています。

Musubi InsightでのE2Eテストの詳細は以下の記事で紹介させていただいていますが、概要としては夜間バッチと同じ入力データを使用して、本番環境に影響を与えずワークフロー全体をオンデマンドで実行するための仕組みです。 [h]

この仕組みはバッチ処理の開発や修正がひと段落した夕方に実行することが多いので、Musubi Insightチーム内では夜間バッチと対応づけて「夕方バッチ」と呼んでいます。

夕方バッチの導入後は、バッチスクリプトのバグであったり、ワークフローの依存関係の設定ミスなどを夜間の本実行前に検知することができるようになり、チーム内で非常に有効活用されています。

しかし、夕方バッチの課題として出力されているデータの内容までは検証できない、という課題がありました。

そこで、夕方バッチに相乗りする形でデータの出力内容まで検証できるように新たなGlueJob(以降、差分チェックジョブ)の追加を行いました。

差分チェックジョブについて

差分チェックジョブの発想自体は非常に簡単で、夜間バッチの結果(修正前)と夕方バッチの結果(修正後)の差分チェックをしよう、というものです。

やったこととしては以下3点になります。 1. 各集計処理ジョブで差分チェックを行うためのparquetファイルを出力するようにする 2. 差分チェックのためのジョブを作成する 3. 差分チェックのためのジョブをE2Eテストに組み込む

1. 差分チェックを行うためのparquetファイルを出力する

Musubi Insightのバッチ処理は、結果をRDBにloadするためヘッダー情報を落としたCSV形式で出力するものが多いです。

しかし、CSVと比較してparquet形式の方が列ごとの比較などより柔軟に扱えることや、コスト面でもメリットがあるので、CSV形式とは別に検証用のparquetデータを出力するようにしました。

夕方バッチの実行では、各ジョブに対してIS_TESTというジョブ引数を渡しているため、このジョブ引数をもって検証用ファイルの出力先を分岐させています。

2. 差分チェックのためのGlueJobを作成する

検証用のデータが出力できたら次は、実際に差分比較を行うためのジョブの作成です。

こちらのジョブも非常に簡単で、ロジックとしては以下のようになっています。

def compare_data(production_df, test_df):
    prod_minus_test = production_df.subtract(test_df)
    test_minus_prod = test_df.subtract(production_df)

    return prod_minus_test.union(test_minus_prod)

subtractは、2つのデータフレームの差分を抽出するためのメソッドで、最初の2行を図にするとこのような形になります。

prod_minus_test

test_minus_prod


本番データフレームとテストデータフレームが完全に一致した場合、図の色がついた部分はなくなり、そのUNIONをとったとしてもレコード数は0件となります。

そのため、最後のUNIONを行なったデータフレームの件数が1件以上であれば、何らかの差分が発生している、という判定を行います。

3. 差分チェックのためのGlueJobをE2Eテストに組み込む

最後にE2Eテストに差分チェックジョブを組み込みます。

E2Eテストのバッチ処理全体が終了したあと、差分チェックジョブが実行されるようなワークフローに修正を行いました。

検証を行う対象は、差分チェックジョブの中でリスト形式で持っているため、そのリストを修正することで、新たなジョブの追加や不要になったジョブの削除にも対応できます。

差分チェックジョブの効果

しばらくE2Eテスト + 差分チェックジョブを運用してみて、以下のような効果を実感しています。

  1. バッチ処理のエラーが確実に減った

    この取り組みを行う前は、バッチ処理のエラーがそれなりの頻度で起こってしまっていましたが、導入後はほとんどエラーが発生することはなく安定的な運用が行えるようになってきています。

  2. リファクタリング時の検証工数が削減された

    バッチ処理のリファクタリングを行った際は、特にワークフローの上流の方の処理である場合、後続の処理に影響が出ていないかなどかなり慎重に数値検証を行なっていたため、かなりの検証工数がかかってしまっていました。

    しかし、差分チェックジョブを導入したことで上流から下流まで全ての出力結果の検証を自動で行えるようになったので、人力での検証は差分が出てしまった場合のみになり、大幅に検証工数を削減することができました。

課題・改善アイデア

バッチ処理の差分チェックを行うことで、安心してリファクタリングを行えるようになりましたが、いくつか課題や改善アイデアも挙がってきています

  1. 差分が出ていることはわかるが、具体的にどこで差分が出ているかはわからない

    現状差分が出てしまった場合にはAthenaなどを使用して確認を行なっていますが、より効率的に差分をチェックできる方法がないかと模索しています

  2. データ全件比較を行なっているので、さらにデータ量が増えていった場合にコスト面が懸念される

    ありがたいことに、現在Musubi Insightを利用いただいているユーザー様が急拡大しており、さらにデータ量が増大することが見込まれます。それに伴い、入力データをサンプリングしたり、全件比較ではなく統計値比較などによってコストを下げる方法も今後検討していく必要がありそうだと考えています

最後に

本記事では、Musubi Insightで最近取り入れた、バッチ処理のテストについて紹介させていただきました。

バッチ処理はテストが難しく、参考となる情報もあまり出回っていないように感じるので、本記事が何かのお役に立てれば幸いです。