バッチ処理と冪等性 / 20191218_merpay_techtalk

Bf32ae94d9e03c03c963f364f2b47698?s=47 kaznishi
December 18, 2019

バッチ処理と冪等性 / 20191218_merpay_techtalk

Bf32ae94d9e03c03c963f364f2b47698?s=128

kaznishi

December 18, 2019
Tweet

Transcript

  1. 2019.12.18 Wed. 1
 Tech Talk vol.2 Backend Engineer ~マイクロサービスの冪等性 ~

    バッチ処理と冪等性 kaznishi
  2. 自己紹介
 • kaznishi
 • Twitter: @kaznishi1246
 • Backend Engineer
 •

    Code Payment Team
 • 2018年11月入社
 
 15% 35% 50% 2
  3. 自己紹介
 • 担当しているマイクロサービス
 ◦ 加盟店精算 ◦ 補助金接続 ◦ 加盟店レポート •

    いずれもバッチ処理がメイン!
 
 15% 35% 50% 3
  4. バッチ処理の紹介
 4

  5. 加盟店精算の様々なバッチ処理
 5 会計 精算 決済 銀行 残高 会計データ取り込み処理 (毎日) ※会計サービスにリクエストを送って取得するのではなく、

    
  会計サービスがストレージ(GCS)にファイルとして 
  吐き出したものを取り込み 

  6. 加盟店精算の様々なバッチ処理
 6 会計 精算 決済 銀行 残高 会計データ中間集計処理 (毎日)

  7. 加盟店精算の様々なバッチ処理
 7 会計 精算 決済 銀行 残高 精算金額集計(締め)処理(月2回)

  8. 加盟店精算の様々なバッチ処理
 8 会計 精算 決済 銀行 残高 入金指示処理(月2回)

  9. バッチ処理と冪等性
 今回のテーマ 9

  10. 冪等性
 • ある操作を1回実行しても、複数回実行しても、同じ結果になる
 • 冪等性がある処理の例
 ◦ 「ある変数の値を x に更新する」 ◦

    何回実行しようが結果は x 
 10 process x
 x
 x

  11. 冪等性
 • ある操作を1回実行しても、複数回実行しても、同じ結果になる
 • 冪等性がない処理の例
 ◦ 「ある変数の値を 現在値 + x

    に更新する」 ◦ 実行のたびに違う値に更新される 
 11 process 初期値 + x
 初期値 + 2x
 初期値 + 3x

  12. バッチ処理を冪等に作るモチベーション
 • バッチ処理設計の重要な考慮事項の一つが「回復可能か」
 ◦ 「バッチ処理の採用と設計を考えてみよう」 (https://tech.mercari.com/entry/2019/02/27/112650) より • バッチ処理は様々な理由によって失敗する
 •

    失敗したときのリカバリー容易性は非常に大事
 • 冪等に作ることで、原因除去後に単純なリトライで回復可能に
 
 12 recovery
  13. 何回実行されても大丈夫な
 バッチ処理を作る
 13

  14. 方針は大きく2種類
 • 処理済み含めてやり直すパターン
 • 一度処理したものをスキップするパターン
 14

  15. 処理済み含めてやり直すパターン
 • バッチ処理が実行されるたびに全部の処理をやり直す
 • バッチ処理の個々のステップが冪等である前提が必要
 • 他マイクロサービス(or外部サービス)にもリクエストを送る処理の場合は リクエスト先にも冪等性が必要
 • 条件分岐を細かく考える必要がない


    • 再実行には時間がかかる
 15
  16. 一度処理したものをスキップするパターン
 • ステータス等で条件分岐し、処理済みのものをスキップする
 • 未処理のものだけを対象とするため、所要時間が短くて済む
 ◦ (未処理かどうかを判別するステップは必要だが) • 実装は条件分岐で複雑になりがち
 16

  17. どちらで実装している?
 • どちらのパターンも存在している
 • 要件によって選択
 • ステート管理が必要なものについては自ずと「一度処理したものをス キップするパターン」になるのではないかと
 • 中間集計など単純にレコードを生成するような処理かつ、全部やり直し

    ても大丈夫なぐらい時間的制約が緩いものは「処理済み含めてやり直 すパターン」により実装している
 ◦ もちろんレコードが重複して生成されないように適切なユニークキーを定義しておく前提 17
  18. 一度処理したものをスキップするパターンでの実装
 • バッチが失敗したときに中途半端状態が生じないように実装を気をつけ る
 • 処理の再開地点以外の状態で落ちていることがないように。DBのトラン ザクション機能などを使って防ぎましょう
 18 トランザクションの単位 


    再開地点

  19. 一度処理したものをスキップするパターンでの実装
 • ステップの中に他サービスへのリクエスト処理が含まれている場合はど うすればいいか?
 ◦ 他サービスにリクエストを送ったあとにリクエスト済みステータスに DB更新するようなケースを想定 ◦ DB更新でエラーが発生したら? ◦

    他サービスにキャンセルリクエストを送る? ◦ 他サービスが冪等に作られているならば、もう一度同じリクエストを 送るのは問題ないと考え、キャンセルせずにそのままリトライして解 消する 19
  20. その他の取り組み
 20

  21. 1. バッチを回す前の確認
 • バッチ処理が処理する材料データは揃っているか?
 • 前段バッチが異常なく終了していることを確認しよう
 • バッチ履歴管理テーブルを作り、正常に終了したことを記録
 • 後段のバッチ起動時に前段が終わっているかの確認を行っている


    21
  22. 1. バッチを回す前の確認
 • 前段の処理が別のマイクロサービスの場合は難しい
 • チーム間でどうやって前段の処理を知らせるか調整が必要
 • 加盟店精算の会計データ取り込み処理の場合
 ◦ 前段は会計システムのGCSへのデータエクスポート処理

    ◦ エクスポート処理終了時に指定名(export.done)の空ファイルを設 置してもらっている ◦ データ取り込み処理開始時にexport.doneが存在していない場合 はまだ前段が終わっていないとみなす 22
  23. 2. バッチが複数回処理された想定のテストを書く
 • 作ったバッチ処理、冪等になっていますか?
 • 下記のようなテストコードを書いて保証
 ◦ 正常にバッチが回った状態に再度バッチ処理を実行する ◦ 途中でバッチがコケた状態に再度バッチ処理を実行する

    23
  24. まとめ
 24

  25. まとめ
 • 回復可能性を高めるためにバッチ処理を冪等に作ろう • 何回バッチを実行しても結果が変わらないように処理を作る • 処理済み含めてやり直す or 処理済みのものをスキップするように作る •

    中途半端状態を作らないように設計しよう • 前段のバッチ処理が成功しているかを確認しよう • テストコードで冪等になっていることを保証しよう 25