kagamihogeの日記

kagamihogeの日記です。

Spring Batchのstep間データ共有

Spring Batchでstep間でデータを共有する方法について。機能的に豊富では無いが、そこはSpring Batchとして積極的に提供する機能では無い、という設計判断なのだと思う。

以下ではそのやり方について述べる。

JobのExecutionContext

JobのExecutionContextに小規模なデータを保存し、step間でこれを共有する。

この方法の特徴は、メタデータのテーブル、デフォルトではBATCH_JOB_EXECUTION_CONTEXTのカラムに保存される。単なるカラム(例:OracleだとCLOB)にJSONシリアライズした文字列が保存される。POJOを保存可能。

このため、小規模データや中間データの参照情報に向いている。逆に、あまり巨大な中間データを保存するのには向いていない。もし中間データをメタデータにstep実行履歴として永続化したとして、「中間」の性質上後で使うことはほぼ無い*1。後で使う例としては、jobの途中のstepからリスタートしたいケースがあるが、これも中間テーブル等で対応できる。

デメリットが目をつむれる程度の小規模データか、後述する中間データの参照情報を保存、というのがこれの基本的な使い方になる、と思われる。

インメモリ

単にプログラム内の変数でstep間でデータを共有する。

@Compopent

@Component
public class IntermediateData  {
  Map<String, SampleData> sharedData;

中間データを持つ適当な@Componentを用意し、複数のstep間で@Autowiredして共有する。

ただの@Compopentなので手軽に使え、メモリの許す限りに大きいデータを持つことも可能。

ただし、インメモリでシングルトンな点に注意が必要。jobが失敗すれば中間データは失われるため、設計上step処理途中からリスタートが必要な場合には使えない。また、シングルトンなのでもしマルチスレッド処理があり得るならその考慮が必要。加えて、jobを複数同時に起動する場合、シングルトンインスタンスに同時にアクセスする問題がある。これは後述の@JobScopeで解決可能。

@Compopent + @JobScope

@Component
@JobScope
public class IntermediateData  {
  Map<String, SampleData> sharedData;

あるbeanのスコープをjob実行単位にしたい場合は@JobScopeを使用する。

中間データを外部保存

適当な永続化機構に中間データを保存する。

伝統的には中間ファイルや中間テーブルがある。他にも、組み込みDBやRedisなどKVS、RabbitMQなどのキューなど、環境に応じて様々な選択肢がある。

もし処理データ対象や範囲指定がある場合、ExecutionContextや@Compopentなどを使用する。中間テーブル名や中間ファイルのパス、処理対象データのインデックス範囲、などをそこで持つ。

中間データを使わない

中間データはなるべく無い方が望ましい。特に、失敗時のリスタートの設計考慮事項、例えば中間データが残らないようにする等、が増えてしまう。バグの元だし、オペレーションが複雑化して事故の元にもなりやすい。

個人的には、Spring Batchのstepに気をとられて分割し過ぎない方が良いように思う。たとえば、RDBだと複数stepのループ処理をSQLだけで済ませられないか、を検討してみるとか。

参照

*1:履歴を残したいならそれ用のテーブル等を用意すると思う