U-NEXTさんからNxBatchRecorder.javaをコントリビュート頂きました

NxBatchRecorder

U-NEXTさん から、NxBatchRecorder.java というクラスと、それを使った Example クラスも一緒にコントリビュート頂きました。

LastaFlute の Example プロジェクトである lastaflute-example-maihama
 => lastaflute-example-maihama | Github

の maihama-orleans アプリにてコミットされています。
 => NxBatchRecorder contributed by U-NEXT, Thanks | Github

ありがとうございます!
U-NEXTではバッチ処理(Job)でガンガン使われているものです。

NxBatchRecorder recorder = new NxBatchRecorder(); // ☆これ
runtime.showEndTitleRoll(data -> {
    data.register("recorder", recorder);
});
recorder.planBatch(memberBhv.selectCount(cb -> { // ☆これ
    arrangeJobTargetCB(cb);
}));

memberBhv.selectCursor(cb -> {
    cb.specify().columnMemberAccount();
    arrangeJobTargetCB(cb);
    cb.addOrderBy_PK_Asc();
}, member -> {
    recorder.asProcessed(); // ☆これ
    try {
        processMember(member);
        recorder.asSuccess(); // ☆これ
    } catch (JobBusinessSkipException continued) {
        recorder.asBusinessSkip(continued.getMessage()); // ☆これ
    } catch (RuntimeException continued) { // NullPointerもSQL例外もあり得る
        recorder.asError(..., continued); // ☆これ
    }
});

まあ、jfluteも作成に絡んでいますけど、オープンにして良いという判断を U-NEXT さんがしてくれたということですね。

バッチのやりたいお約束

「バッチを実装するときのお約束」と共に NxBatchRecorder を紹介。

1. バッチは終了報告したい
2. 終了報告は統一したい
3. 同じエラーはストップしたい

1. バッチは終了報告したい

バッチを実装するときのお約束ですが...

「正常に終了したかどうか?」はもちろんのこと「どんな処理をして終了したか?」を知りたいものです。

具体的には...

o 一回のバッチ実行で何件処理したか?
o そのうち何件は成功したか?
o そのうち何件は失敗したか?
o 失敗の原因はどんなものだったか?

というような情報。

jfluteは色々な現場を見ていますが、意外とそういった情報がどこにも出力されていない現場も多いです。やはり何が起きたのかわからず、普段の運用で情報不足になって困りがちです。

NxBatchRecorderでは、以下のような情報を実行中に溜め込んでログに出力します。(バッチ実装の中で貯め込むメソッドを呼びます)

count:{planned=12, processed=12, success=8, businessSkip=3, error=1}

o 計画件数: 12件
o 処理件数: 12件
o 成功件数: 8件
o 業務的にスキップした件数: 3件
o エラーでスキップした件数: 1件

2. 終了報告は統一したい

また、仮にそういう情報を出していたとしても、人によってフォーマットがバラバラとか、情報の有り無しがバラバラとかだとおいしさ半減です。

出ていないよりかは100倍マシですが、単純に見づらいだけでなく、後でパースして一括的に閲覧できるようにするとか、そういうことがしづらくなるので、とにかく決まった形で出すというのが大切です。

なので、NxBatchRecorderのようなクラスを用意してみんなで使うというのがポイントです。

NxBatchRecorderのクラスをちょっと拡張すれば、自分たちの好きなようなフォーマットで出せるので、そこは現場で合わせると良いでしょう。(JSONで出力したいという話もありました。toString()でオーバーライドして好きなJSONライブラリで好きなように)

3. 同じエラーはストップしたい

地味ながら大切なことです。

基本的にバッチは、一つのデータ (レコード) で失敗しても継続して処理を続行します。業務的な例外はもちろんのこと、エラー例外でも継続できる可能性があるのであれば続行したいところです。

例えば、何かしらその一瞬だけ「DB接続エラー」が発生したとか、とあるレアケースのデータにおいてNullPointerExceptionが発生したとか...それでも次のデータで継続できるのであれば続行したいものです。

と・は・い・え

DB接続エラーが10回連続して発生してたら...
NullPointerが10回連続して発生してたら...

恐らくその後もずっと落ち続けるでしょう。

すると、10万を超えるかもしれない大量のエラーログが出力されるかもしれません。その場合、バッチ処理自体を中断したいものです。つまり、「エラーを続行しつつも続行不能と判断したらバッチ全体を落とす」というような制御が必要になります。

NxBatchRecorderには、その制御が入っています。

地味ながらJobのお供に

ということで、Jobを作るときは、NxBatchRecorder(のようなもの)は必ず欲しいですね。ない現場であればほぼ似たようなものを作ると思います。今であれば、NxBatchRecorderも持ってきて拡張すればOKです。

NxBatchRecorder自体は、LastaJobには依存していないので、別のフレームワークでも利用できます。一部、DBFlute の OptionalThing を使っていますが、サクッと java.util.Optional に変更してしまえば良いです。

Exampleとしてのコントリビュート

今回は何かのライブラリにコミットというのではなく、Exampleとして公開して頂きました。

LastaJob へのプルリクでも良かったかもですが...

どんなことを記録するのか?
どんなフォーマットで出すのか?
どんな基準でログを保持するのか?
何回連続でエラーと判断するのか?

やはり厳密には現場ごとに変わる可能性が十分あるだろうと。

ゆえに、LastaJob に組み込むためには、オプションなどを用意して色々と現場フィットができるようにしたフレームワーク状態に仕立てる必要がありますが、その準備が公開の足かせになるので、気にせず気軽に Example で公開ということで。

Apache Licenseなので、使いたい人はクラス一個コピーして好きなように拡張して使ってくださいとのことです。逆にその方が汎用性が高いかもですね。

これはこれですごく嬉しいことですし、そういう気軽なコントリビュートっていう方法もあるというのを示す良い一例だと思います。体裁を整えてフレームワークみたいな感じにしないとコントリビュートできないってわけじゃないと。企業からこういう気軽なコントリビュートが増えると嬉しいですね(^^。

クラス単位でのコントリビュート、Exampleとしてのコントリビュート、歓迎しています。

バッチ設計のコツ

NxBatchRecorderを使った JobクラスのExampleである SmallDbJob というクラスもコントリビュート頂いています。
 => SmallDbJob.java | Github

単に NxBatchRecorder の使い方がわかるというだけでなく、コメントというか「メモ」として...

o JOB設計の基本べき集
o JOB設計の選択肢
o バッチの排他制御の実装について

など、かなり色々とまとめられています。バッチを設計・実装する際に参考になると思うので、ぜひ読んでみてください。

これを題材にした勉強会もしていきたいですね(^^。まだまだ発展途上のメモですから、フィードバックいっぱい頂いて、もっともっと汎用的な判断フレームワークにしていきたいですね。


※公開に向けてレビューしてくれたU-NEXTふなきくんありがとう!

f:id:jflute:20190403115957j:plain