2019.12.18 Wed.1 Tech Talk vol.2 Backend Engineer ~マイクロサービスの冪等性 ~バッチ処理と冪等性kaznishi
View Slide
自己紹介 ● kaznishi ● Twitter: @kaznishi1246 ● Backend Engineer ● Code Payment Team ● 2018年11月入社 15%35%50%2
自己紹介 ● 担当しているマイクロサービス ○ 加盟店精算○ 補助金接続○ 加盟店レポート● いずれもバッチ処理がメイン! 15%35%50%3
バッチ処理の紹介 4
加盟店精算の様々なバッチ処理 5会計 精算 決済 銀行残高会計データ取り込み処理(毎日)※会計サービスにリクエストを送って取得するのではなく、 会計サービスがストレージ(GCS)にファイルとして 吐き出したものを取り込み
加盟店精算の様々なバッチ処理 6会計 精算 決済 銀行残高会計データ中間集計処理(毎日)
加盟店精算の様々なバッチ処理 7会計 精算 決済 銀行残高精算金額集計(締め)処理(月2回)
加盟店精算の様々なバッチ処理 8会計 精算 決済 銀行残高入金指示処理(月2回)
バッチ処理と冪等性 今回のテーマ9
冪等性 ● ある操作を1回実行しても、複数回実行しても、同じ結果になる ● 冪等性がある処理の例 ○ 「ある変数の値を x に更新する」○ 何回実行しようが結果は x 10processx x x
冪等性 ● ある操作を1回実行しても、複数回実行しても、同じ結果になる ● 冪等性がない処理の例 ○ 「ある変数の値を 現在値 + x に更新する」○ 実行のたびに違う値に更新される 11process初期値 + x 初期値 + 2x 初期値 + 3x
バッチ処理を冪等に作るモチベーション ● バッチ処理設計の重要な考慮事項の一つが「回復可能か」 ○ 「バッチ処理の採用と設計を考えてみよう」 (https://tech.mercari.com/entry/2019/02/27/112650) より● バッチ処理は様々な理由によって失敗する ● 失敗したときのリカバリー容易性は非常に大事 ● 冪等に作ることで、原因除去後に単純なリトライで回復可能に 12recovery
何回実行されても大丈夫な バッチ処理を作る 13
方針は大きく2種類 ● 処理済み含めてやり直すパターン ● 一度処理したものをスキップするパターン 14
処理済み含めてやり直すパターン ● バッチ処理が実行されるたびに全部の処理をやり直す ● バッチ処理の個々のステップが冪等である前提が必要 ● 他マイクロサービス(or外部サービス)にもリクエストを送る処理の場合はリクエスト先にも冪等性が必要 ● 条件分岐を細かく考える必要がない ● 再実行には時間がかかる 15
一度処理したものをスキップするパターン ● ステータス等で条件分岐し、処理済みのものをスキップする ● 未処理のものだけを対象とするため、所要時間が短くて済む ○ (未処理かどうかを判別するステップは必要だが)● 実装は条件分岐で複雑になりがち 16
どちらで実装している? ● どちらのパターンも存在している ● 要件によって選択 ● ステート管理が必要なものについては自ずと「一度処理したものをスキップするパターン」になるのではないかと ● 中間集計など単純にレコードを生成するような処理かつ、全部やり直しても大丈夫なぐらい時間的制約が緩いものは「処理済み含めてやり直すパターン」により実装している ○ もちろんレコードが重複して生成されないように適切なユニークキーを定義しておく前提17
一度処理したものをスキップするパターンでの実装 ● バッチが失敗したときに中途半端状態が生じないように実装を気をつける ● 処理の再開地点以外の状態で落ちていることがないように。DBのトランザクション機能などを使って防ぎましょう 18トランザクションの単位 再開地点
一度処理したものをスキップするパターンでの実装 ● ステップの中に他サービスへのリクエスト処理が含まれている場合はどうすればいいか? ○ 他サービスにリクエストを送ったあとにリクエスト済みステータスにDB更新するようなケースを想定○ DB更新でエラーが発生したら?○ 他サービスにキャンセルリクエストを送る?○ 他サービスが冪等に作られているならば、もう一度同じリクエストを送るのは問題ないと考え、キャンセルせずにそのままリトライして解消する19
その他の取り組み 20
1. バッチを回す前の確認 ● バッチ処理が処理する材料データは揃っているか? ● 前段バッチが異常なく終了していることを確認しよう ● バッチ履歴管理テーブルを作り、正常に終了したことを記録 ● 後段のバッチ起動時に前段が終わっているかの確認を行っている 21
1. バッチを回す前の確認 ● 前段の処理が別のマイクロサービスの場合は難しい ● チーム間でどうやって前段の処理を知らせるか調整が必要 ● 加盟店精算の会計データ取り込み処理の場合 ○ 前段は会計システムのGCSへのデータエクスポート処理○ エクスポート処理終了時に指定名(export.done)の空ファイルを設置してもらっている○ データ取り込み処理開始時にexport.doneが存在していない場合はまだ前段が終わっていないとみなす22
2. バッチが複数回処理された想定のテストを書く ● 作ったバッチ処理、冪等になっていますか? ● 下記のようなテストコードを書いて保証 ○ 正常にバッチが回った状態に再度バッチ処理を実行する○ 途中でバッチがコケた状態に再度バッチ処理を実行する23
まとめ 24
まとめ ● 回復可能性を高めるためにバッチ処理を冪等に作ろう● 何回バッチを実行しても結果が変わらないように処理を作る● 処理済み含めてやり直す or 処理済みのものをスキップするように作る● 中途半端状態を作らないように設計しよう● 前段のバッチ処理が成功しているかを確認しよう● テストコードで冪等になっていることを保証しよう25