この記事は「ゆめみ その2 Advent Calendar 2019」の6日目の記事です。

ExecutionContextとは
ExecutionContextはSpring BatchでJobに使用するデータを保管するためのクラスです。
JobやStepの実行ステータスなどの様々なデータが保管されており、デフォルトではメタテーブルに、BatchConfigurerなどを使用して設定を変更すればメモリ上で管理することが可能です。
ExecutionContextにはJob用とStep用で2種類あります。
JobExecutionContext
通常Step間でデータを受け渡すことはできませんが、JobExecutionContextを使用するとステップの処理の結果やステータスを別のステップに受け渡したり、jobBuildFactory内で呼び出したりすることができます。

StepExecutionContext
StepExecutionContextは主にチャンクの実行回数やリスタートのための情報を管理するために使用します。
Spring BatchではItemReaderなどのチャンク関連のクラスにはデフォルトで設定されています。
ただしJobExecutionContextとは異なり、他のステップからは参照できない仕組みになっています。

何が問題か
バッチ処理を実装するにあたって、外部から取得したCSVファイルを加工してDBに保存するなんて処理はよくあるものかと思います。
しかしながら何万行とあるCSVデータをそのまま直接他のチャンクやステップに受け渡すとなると話は変わってきます。
メタテーブルのカラムサイズ
設定がデフォルトの場合、ExecutionContextはSpring Batch実行時に自動生成されるメタテーブルにJson形式でインサートされます。
ただこのカラム、保存できる容量が2500バイト程度なのでそれ以上の長さの文字列を保存することはできません。
要するにどんなに頑張っても2500文字までしか保存できないと言うことです。
またSpring BatchではXstreamでJavaのPOJOをシリアライズするため、それで変換できないオブジェクトは保管することはできません。
後者の問題に関しては予めjacksonなどを使用してJson文字列に変換することで回避はできますが、前者に関してはどうしようもありません…
メモリに保存する
じゃあメタテーブルなんか使わなければいいじゃん!
Spring Batchではこちらの記事のようにメタテーブルを使わずにオンメモリでデータを管理するように変更することができます。
これによってどんな長さの文字列だろうがメモリの容量ある限り無限に保存することができます。
話は変わり、とある本番環境
こうして外部から取得したCSVをStepExecutionContextに保存して他のチャンクに受け渡し、DBを更新するバッチ処理を作ったAさん。
彼はそのまま保守を任されることになります。
彼は毎日変わりなく流れ続けるログを眺めつつ、「最近なんかバッチ処理の実行時間が長くなったかな?」なんて感じつつも、特に問題がないためそのまま放置してました。
そしてある日…
Aさんが呑気にログを眺めている裏ではそれなりに大変なことが起きつつありました。
開発時は数千行程度だったCSVも時間が経つにつれて増えていき、最終的には数十万行もの膨大なファイルに変貌していたのです。
それでもメモリの続く限り、サーバは唸りを上げて処理し続けます。
そしてある日HEEPが全てのメモリを食い潰した時、ついに**それ(障害)**が訪れます…

こうならないためには
こうならないためには主に二つの解決方法があります。
- 処理がステップ内で完結するようにし、データを他のステップに渡さなくても済むように設計する。
- バイナリファイルとして一時保存し、必要なStepで読み込むようにする。
どちらも設計レベルでの話になりますが、間違っても直接ExecutionContextに大量のデータを入れたりしないように工夫しましょう。
参考文献
[Java][Spring][Spring Batch] Spring Batchのメタデータテーブルを作らせない/使わせない