はじめに
こんにちは! Souzoh software engineerの@ogataka50です。「メルカリShops [フライング] アドベントカレンダー2022」の7日目を担当します。
本記事ではSouzohが運営しているメルカリShopsの注文システム安定化の歩みについて書きたいと思います。注文システムの概要についてはこちらのメルカリShops 注文システム反省会もご参照ください。
今回は注文システムで発生しうるデータ不整合、処理が中断された注文データの検知とその復旧を通じて注文システムの安定化を図った経緯について紹介します。
注文システムの概要と発生する問題
先に前提として注文システムの概要とそこで発生しえる問題について商品購入処理を例にして説明します。
購入処理は大まかに上記のようになっており、GCPのWorkflowsを利用して注文作成の処理に必要なAPI呼び出しを行って実現しています。
すべてのAPI呼び出しが成功すればよいのですが、関連serviceの障害や一時的なネットワークの問題などにより、処理の途中で失敗する場合があります。
多くのケースはWorkflowsによるretryで解消しますが、それでも処理が失敗するケースがあります(注文作成に関連するAPIは冪等性をもったAPIであるという前提です)
以下にいくつかの具体例を示します。
処理が中断され途中で止まってしまう注文
発生する問題の1つとして、購入処理の一部が失敗し処理が中断された注文が発生するというものがあります。
具体例としては上記のようになります。在庫確保完了後、配送情報を登録するためのorder serviceからshipping serviceへのAPI呼び出しが失敗するというケースです。
一時的なネットワークなどの問題であればretryで正常に処理が進みます。問題となるのはretryしても成功しない場合です。このような状態が一定時間以上続くとretry上限に達し処理が中断された注文データが発生します。
これをpending orderと呼んでいます。中断した箇所により、注文処理の中の一部のみ完了している状態などになります。お客さまから見ると購入ボタンを押したが、購入処理中のままという状態になり、早期に復旧させる必要があります。
Microservices間でのデータ不整合
上記のpending orderの派生として、Microservices間でのデータ不整合が発生するという問題も起こり得ます。
この問題が発生するのは上記のようなケースです。メルカリShopsではmerpayを利用して決済処理を実現しています。
メルカリShopsのorder serviceからpayment service、そしてmerapyに決済requestを行うことで決済処理を行っています。
メルカリShopsからmerpayへ決済リクエストを行ったがtimeoutやunknownなエラーが返ってきた場合、メルカリShopsとしては決済処理が成功していませんが、merpay側では決済処理が成功している可能性があります。この場合merpay側の決済処理は成功したが、その結果をメルカリShopsが正常に受け取れず一時的にmerpayとメリカリShopsの決済状態が一致しないという状態になります。
例として決済の不整合を扱いましたが、これはメルカリShopsのmicroservices間でも起こり得る問題です。
前置きが長くなってしまったのですが注文システムでは上記のようなpending order、データ不整合が発生する可能性があり、これらをどのように検知、復旧の改善を行ってきたかについてメルカリShopsのローンチから現在に至るまでの流れを紹介します。
メルカリShopsのローンチ前の対応方針
ローンチ前から前述のpending orderが一定数発生することはわかっていました。
しかしそれがどの程度発生するか、どのような不整合が多く発生するか予測することは難しい状態でした。一時的なエラーに関してはWorkflowsのretryにより防げるのは開発段階で確認していましたが、Workflowsをproduction環境で使うことはメルカリグループ内でもメルカリShopsが初めての事例でした。
そのためローンチ時は下記の方針で進めました。
- マニュアルによるpending orderの検知
- 重大な問題になり得る決済に関するデータ不整合の自動検知の実装
- 復旧に関してはマニュアル作業によるretry or rollback
pending orderの検知方法
Orderに関して何か操作を行う場合はOrderのstatus fieldを処理中として保存してから処理を開始します。Order作成時にはstatus=creating(作成中)
という状態に更新してから、各種個別の処理を実行します。更に各処理の進行はstepという別のfieldで管理しています。
そのため注文作成時に在庫を抑えるという処理を行っている時はstatus=creating, step=authorize_stock(在庫確保)
というような状態になります。
pending orderは処理中が一定時間以上続いているという状態になるので、status=creating
が一定時間以上経過している場合、処理が中断されているpending orderという判定をしています。
決済不整合を検知するreconciliation service
決済の不整合に関しては重大な障害につながる可能性があるため、自動的に検知できるように決済データをreconcile(突合)する専用のmicroserviceを用意しました。
reconcileの概要としては下記になります。
- merpayは決済データの作成や更新があった際にそのeventをCloud Pub/Subにpublishする仕組みになっています。
- 決済データのreconcileを行うreconciliation serviceはそのeventをsubscribeし、DBに保存します。
- Cloud Schedulerがreconciliation serviceのreconcile jobを定期実行します。jobはまず2.で保存したmerpayの決済データとそれに対応するメルカリShops内の決済データを取得し、それらを突合し、決済データが欠けている、値に相違があるなどを検知したらslackに通知します
ローンチ初期に関してはこちらの決済不整合の自動検知、pending orderに対してteam内でrotationを組みマニュアル対応するという体制で対応していました。
ローンチ後の改善
メルカリShopsローンチ後はネットワークや負荷などに由来した一時的なエラーはWorkflowsによるretry機構で正常に処理することができました。システム障害を除いて日常的に発生するエラーをWokflowsでカバーできたのはとても助かりました。
しかし運用してはじめてhandlingが漏れていたerror codeや、retryableなエラーをretryしていなかった、特定のエラーケースで冪等なレスポンスを返せていないなど大小様々な問題が発覚しました。
ローンチ後しばらくはそれらの検知と修正を行い、再発しないような対応を地道に行っていました。
検知・復旧の自動化へ
リリース後数ヶ月は上記のような地道な改善を続けていきました。その成果もあり、ほぼすべてのケースを適切にretry, fail, rollbackすることができるようになってきました。
この頃からマニュアル対応ではなく、検知と復旧の自動化に着手し始めました。
基本的にはこれまでマニュアルで行っていたoperationをsystem化する形で進めていきました。
決済不整合の復旧自動化
まずはじめに着手したのはもっとも早期に復旧させる必要がある決済の不整合に対する自動化でした。
これに関しては先程紹介したreconciliation serviceを機能拡張することで対応しました。
これまでは決済データの不整合を検知するとslackへの通知するだけでしたが、同時に不整合検知eventをpublishし、order serviceがそれをsubscribeし、対応する決済不整合を解消するjobを実行するように改修しました。
これにより、検知から不整合解消を自動化することができ、事象の発生から復旧までの時間とマニュアル作業の物理的/心理的コストを減らすことができました。
pending orderの復旧自動化
次にpending orderの検知、復旧自動化の対応を行いました。概要としては下記のようになっています。
Cloud Schedulerにより、hourlyでpending orderを検出するjobを実行します。pending orderが検出された場合、pendingになった要因により適切なrecovery jobをCloud Tasksを使って実行します。
Cloud Tasksを使うことでrecovery jobの並列実行とその同時実行数の制限しつつrecovery job実行とそのretryを制御しています。
これにより長時間に及ぶシステム障害などでpending orderが多く発生した時も安定して復旧処理を行うことができました。
これらの対応により現在ではほぼ人間が手を動かすことなく、注文システムで発生するデータ不整合/pending orderを検知・復旧の自動化を行うことができるようになりました。
まとめと今後の改善
ここまでメルカリShopsの注文システムで発生するデータ不整合、pending orderに対して、サービスローンチ初期から現在に至るまでの対応の歩みを紹介させてもらいました。
ローンチ初期では検知方法の担保と重大な問題になり得る決済のデータ不整合の自動検知までを実装し、ローンチ後はしばらくはマニュアル対応をしつつ問題が起きた際には根本原因の究明とその改善を行い、自動化の下準備を行いました。その後一通りのエラーケースをrecoveryできるまで改善をしてから、既存の検知の仕組みを拡張し、検知から復旧までを自動化しました。これにより注文システムの安定化を図ってきました。
今後はさらなるpending order発生の抑制や、復旧までの時間短縮、新規の決済手段の導入コスト軽減などを図っていこうと考えています。
株式会社ソウゾウではメンバーを大募集中です。メルカリShopsの開発やソウゾウに興味を持った方がいればぜひご応募お待ちしています。詳しくは以下のページをご覧ください。
Software Engineer
Software Engineer, Site Reliability
Software Engineer (Internship) – Mercari Group (※新卒採用に応募するにはまずインターンへの参加をお願いしています。)
またカジュアルに話だけ聞いてみたい、といった方も大歓迎です。こちら の申し込みフォームよりぜひご連絡ください!