バッチ処理実装時の心得(基本編)
概要
筆者がこれまでの開発経験で気づいた、バッチ処理を実装する際の基本的な心得をまとめようと思います。
1️⃣ メモリ・データベース負荷管理
バッチ処理で扱うデータ量が増加するにつれメモリ使用率が増加するようなアルゴリズムになっていることもあるので、OOM(Out of memory)に要注意。
どの程度メモリを消費するか事前に検証が必要。
データベースも同様に負荷の管理をしてあげましょう。
2️⃣ 外部サービス連携でのリトライ設計
外部APIからのデータを取得などは成功する前提で処理を組みますが、失敗することを想定して、失敗した場合の期待動作と、成功に向かうためのリトライ処理を設けておきましょう。
3️⃣ 実行管理
それぞれのバッチがどれくらいの時間で完了するのかの管理も大事です。
いくつかの例を見てみましょう。
定期実行のバッチが複数がある場合の例
バッチAは毎日9:00に実行開始して9:30までに完了予定だったが想定以上に伸びてしまい、本来被ることを考えていなかった別の9:30開始のバッチBと被りメモリ不足になることもあります。
5分間隔で実行するようなバッチがあった場合の例
5分以内で終わらず6分かかるようになると、1分間、同じバッチが二重にバッチが走ることになります。その結果予期せぬ不具合を起こしかねません。
データが増えたり外部要因などで実行時間が延びてしまうことは、よくある話なので実行時間には注意を払っておきましょう。
4️⃣ 可観測性
可観測性をしっかり保ちましょう。
要はしっかりログを出すことが大事ですね。
- 処理の開始・進行状態を把握できるか
- 処理対象の中身を把握できるか
- 処理の完了状態を把握できるか
- 処理にかかった時間を把握できるか
- 何かエラーが起きた場合、エラー通知だったりログ出力しているか
5️⃣ 冪等性
冪等性とは「ある同じ操作を何度行っても、いつも同じ結果が得られる性質」です。
バッチにおける冪等性は「仮に処理が途中で失敗してリトライしても最終的な結果が1回で処理が成功した時と変わらないこと」
バッチは割とコケます、なんらかの外部要因・内部要因で失敗します。
ここで問題になるのが、「このバッチはIDの1~100まで処理が終わってるけどID 101~200は終わってない..」みたいな状態。
このような状況を考慮して、バッチを再実行する際に必ず、
- 処理済みのものは処理しない
- 処理済みも含めてやり直す
のような処理を入れて、何度実行しても結果の正当性が担保できるようにしましょう。
6️⃣ エラーハンドリング大事
バッチでのエラーハンドリングのパターンとしては下記の3つだと思います。
- 1件でもエラーがあれば、処理全体を止める
- エラーがあれば、該当処理をスキップして処理を続ける
- エラーの許容値を設けて、許容値内は続行、許容超過は失敗とする
最後に
バッチ処理を構築する際に考慮すべき点は多岐に渡り難しいですね。
筆者も、もっと経験を積んで応用的なことを書けるように頑張ります。
参照
Discussion