こんにちは。メルペイCash I/Oチームのshota sugiuraです。
これはMerpay Tech Openess Month 2022の12日目の記事です。
本記事では、振込申請機能のデータマイグレーションの振り返りをします。2年前にRobertが書いた記事の続編になっていますので、興味がある方はこちらもぜひ読んでみてください!
はじめに
私の所属するCash I/Oチームが管理しているマイクロサービスは、メルペイ残高への入金、およびメルペイ残高からの出金に関わる機能を提供することを責務としています。メルペイでは現在、入金の機能として、登録した銀行口座からの入金や、セブンATMからの現金チャージをサポートしています。出金機能は、お客さまがご自身の銀行口座を指定して、そこに指定した金額を振り込む機能になっています。アプリ上では、振込申請と表示されている機能です。
簡単にまとめると、メルペイと外の世界とのお金のやり取りに関わる機能を提供するサービスということになります。
振込申請機能のメルカリからの移行
本記事で話題にするのは、メルペイ残高からお客さまの銀行口座への出金に関わる処理のメルカリからの移行です。振込申請は、メルカリがローンチのほぼ直後から提供していた機能です。メルペイが立ち上がったときに、メルペイの管理下に移されましたが、機能自体はメルカリのモノリス上に実装されたままでした。この機能を切り離してメルペイのマイクロサービスに移すことで、より開発・運用・改善を行いやすいような体制を目指すプロジェクトが約2年半前に立ち上がりました。一昨年に行った第1段階のマイグレーションで、振込申請のコアなロジックはメルペイ側に移管したものの、申請処理に関わるユーザ情報等はモノリスに残ったままでした。具体的には、以下のようなシーケンスで処理が行われていました。
メルペイのマイクロサービスへの移行の最も大きな目的の一つとして、メルカリ側に障害が発生したときに振込申請の機能を停止しないようにするということがありました。しかし、図に見られるように依然としてメルカリのサービスに依存しているため、この状態ではメルカリ側がダウンするとそれにつられて振込申請も停止します。実際、振込申請に係る障害の多くはメルカリに起因するものでした。一連の処理で用いるデータの一部はメルカリ側にあるため、障害復旧や調査のためにメルカリのデータベースやコードを確認しに行く必要があり、オペレーションコストの高い状態にありました。これらの状況を踏まえて、2021年の秋頃、振込申請機能のメルペイへの完全移行を行うこととしました。
段階的ロールアウトによるゼロダウンタイム移行
一口にデータの移行と言っても、それだけで書籍が書かれるほど奥深いトピックです。この記事の中で、データ移行が様々な観点から分類されていて、非常に参考になりました。移行の計画をするにあたっては、なるべくお客さまに影響を与えないような工夫をしつつ、障害を避けて安全に移行する方法を考える必要があります。
2年前に行った第1段階のデータ移行では、アプリケーションの都合で一時的に機能を停止する必要がありました。なるべくダウンタイムが短くなるように実行しましたが、お客さまへ与える影響を考えると振込申請機能の停止はなるべく避けたいです。今回データ移行では、ゼロダウンタイムでの移行が可能であるという結論に至り、そのための移行フローのデザインを行いました。
検討の結果、段階的ロールアウト(Staged Rollout)によるデータ移行を行うことにしました。データを移行したのちに、一気にアプリケーションを切り替えるのではなく、ユーザの一部のみを移行先のアプリケーションに誘導し、残りのユーザは元々のアプリケーションで処理します。仮に新しいシステムで問題が発生したとしても、その影響を一部に抑えることができます。
以下に移行に向けて行った作業を紹介します。
1. 新しいデータスキーマの定義
移行対象のデータはもともとメルカリのMySQLに存在していました。メルペイでは全社的にGoogle Cloud Spannerをメインのデータベースとして利用しており、移行先のデータベースはSpannerになります。各データベースの特徴の違いを踏まえながら、新たなスキーマを定義します。
2. ビジネスロジックの実装
PHPで書かれているメルカリのAPIを、Goで書き直してメルペイのマイクロサービスに移植します。当然、元のAPIと完全に互換性を保つようなAPIを提供しなくてはなりません。エラーハンドリングを含めて、全ての仕様が一致するように実装します。
3. データの移行状況に合わせてリダイレクトする
段階的に切り替えていくということはつまり、データ移行の途中の段階で例えば10%のデータは移行済みでメルペイ側に、90%のデータは移行前でメルカリ側にある、といった状態が発生するということです。移行済みのデータに対する参照・更新はメルペイ側で処理したいため、そのための分岐処理をメルカリに持たせます。移行前のユーザーであればメルカリのMySQLからデータ取得を行いますが、移行後であれば2.でメルペイに実装した新しいAPIにリダイレクトして、Spannerから必要な情報の読み取りや更新を行います。具体的には、以下の図に示すような処理が走ります。
4. データ移行の実行 & モニタリング
ここでようやくデータの移行を行います。データ移行のためのバッチジョブを実装し、メルカリからメルペイへ徐々にデータを移行します。初期のリリースでは、対象を全データの1%程度に絞って移行を行い、異常が発生しないかモニタリングします。大丈夫そうであれば、徐々に移行するデータを増やしていき、全てのデータが移るまでこれを続けます。
5. メルカリAPIの呼び出しをやめる
データを100%移行できたら、もうメルペイからメルカリのAPIを叩く必要はありません。振込申請時に自身のデータベースからユーザ情報を参照するように変更してデータ移行の完了です。
当初の目的通り、振込申請に関わる機能をメルカリからメルペイに完全に切り出すことができました!移行の過程で一時的に分散していた振込関連のデータとロジックが適切にメルペイのマイクロサービスに集まり、運用のしやすい体制になっています。 振り返れば2019年から段階を踏みながら進めていた移行プロジェクトですが、ここで一旦区切りがつきました。
データ移行時に遭遇したトラブル
機能を移行するにあたって最も注意を払っていたのは、メルカリ上に実装されていた既存のAPIの仕様を変えず、互換性を保ったままロジックを移植することです。QAを実施する際も、既存の仕様を洗い出した上でテスト項目を作り、時間をかけてテストしたうえでリリースに臨みました。
しかし、1点意図していない仕様変更がメルペイ側に実装したロジックに含まれてしまっていたことが、リリース後に判明します。他チームからの報告を受けて、一部のクライアントからのリクエストを弾いてしまっていることに気づきました。幸いにも、リリースの初期段階で気づくことができ、またお客さま向けに提供している機能ではなかったため大事には至りませんでした。本来、コードレビューやQAの段階で発見すべきことでしたが、その過程では発見する事ができませんでした。リリース前のテストフローを見直す必要を認識するとともに、今回のように段階的に新しいコードをリリースすることの利点を改めて実感しました。
まとめ
サービスの移行プロジェクトに参加したのは自分としては初めてでした。利用する技術スタックの特徴の違いを踏まえた適切な機能の移植や、全体のアーキテクチャを考慮したデータ移行の計画の立て方など、非常に学びの多い開発でした。また、他の開発チームとのコミュニケーションなどの、開発に関するソフトスキルの重要性も感じることのできたプロジェクトでした。
振込申請の移行を終えて、Cash I/Oチームでは現在、 新たな入出金の手段を提供するための開発を行っています。既存の機能の改善を行いつつ、より利便性の高い決済サービスを目指した開発をこれからどんどん進めていきます。