GS2 Blog

Game Server Services(https://gs2.io/) の最新情報をお届けします

マイクロサービス間でトランザクション処理を行う

みなさん、こんにちは。GS2 の丹羽です。

本日 GS2-Showcase という新サービスを告知しました。
gs2.hatenablog.com

このサービスを開発するにあたって新しいトランザクションシステムを導入することにしました。
このトランザクションシステムを GS2 内ではスタンプラリーと呼ぶことにしています。

マイクロサービスにおけるデータ更新の課題

まず、最初にマイクロサービス内におけるデータ更新の課題について振り返ってみます。
マイクロサービス間の結合度を下げる為には、まずは依存関係をなるべく少なく保つ必要があります。

たとえば、今回発表した GS2-Showcase を実装するためには最大4つのシステムが連携することになりました。
代表的な例として、課金通貨を使用してゲーム内通貨を購入するケースで必要となる操作を整理します。

  • GS2-Showcase / 商品が陳列されている(販売期間内である)
  • GS2-Money / 消費する通貨が商品価格に対して十分である
  • GS2-Gold / 購入する通貨が購入後もウォレットの上限に達しない
  • GS2-Limit / 商品の購入回数をカウントアップする
  • GS2-Money / 通貨を消費する
  • GS2-Gold / 通貨を加算する

このようなアクションを同期的に実行しなければ GS2-Showcase の機能を満足に提供することは出来ません。
しかし、マイクロサービス間では異なるデータベースを持ちますし、何らかの方法で排他処理を実装する必要がありました。

スタンプラリー

そこで、GS2 では新たにスタンプラリーというトランザクションシステムを用意しました。
スタンプラリーには以下の概念が存在します。

スタンプシート

スタンプラリーを実行する単位です。
スタンプシートには複数のスタンプタスクと、スタンプラリーを完走したときに得られる報酬。報酬を得るためのエンドポイントが記録されています。

スタンプタスク

スタンプタスクはスタンプラリーの報酬を得るために起こさなければならないアクションが記録されています。
スタンプタスクは主にスタンプラリーを完走したときに得られる報酬を得るための対価を支払うアクションの定義となります。

例を分類

先ほどの例をスタンプラリーの概念で分類してみます。

状態保証

  • GS2-Showcase / 商品が陳列されている(販売期間内である)
  • GS2-Money / 消費する通貨が商品価格に対して十分である
  • GS2-Gold / 購入する通貨が購入後もウォレットの上限に達しない

状態保証はスタンプラリーを開始するために必要となる条件です。
今回は GS2-Showcase がスタンプシートを発行することになりますので『GS2-Showcase / 商品が陳列されている(販売期間内である)』に関する保証は容易です。
GS2-Showcase では商品の購入アクション実行時(スタンプシート発行直前)に GS2-Script を呼び出すことが出来ます。
このスクリプト内で『GS2-Money / 消費する通貨が商品価格に対して十分である』『GS2-Gold / 購入する通貨が購入後もウォレットの上限に達しない』を検証することで状態保証を行えます。

さらに、スタンプラリー実行中に状態が変化しないことを保証する為に GS2-Money / GS2-Gold のウォレットに対してロックを取得するといいでしょう。
その際に、その後の通貨の消費や加算処理の妨げにならないよう、スクリプトにはスタンプラリーにおいて使用するトランザクションIDが渡りますので、それを使用して GS2-Lock でロックを取得します。

ウォレットが条件を満たしていない場合や、ロックの取得に失敗した場合はスクリプトからエラー応答をすることで、スタンプシートの発行を止めることが出来ます。

変化要求

  • GS2-Limit / 商品の購入回数をカウントアップする
  • GS2-Money / 通貨を消費する

次に、変化要求です。
スタンプシートのスタンプタスクを全て実行することで変化要求に応えることが出来ます。
スタンプタスクは実行されたかという状態をサーバ側で管理しており、複数回実行出来ないようになっています。
『GS2-Limit / 商品の購入回数をカウントアップする』と『GS2-Money / 通貨を消費する』はそれぞれのマイクロサービスにタスクを送信することで、実行出来ます。
仮に処理が失敗した場合もリトライをしたり、場合によってはロールバックしましょう。

報酬

  • GS2-Gold / 通貨を加算する

スタンプラリーを終えたら、スタンプシートを消費することで、報酬が得られます。
指定されたエンドポイントにスタンプシートを送信すると、全てのタスクが完了しているかを検証し、問題が無ければ報酬を付与してスタンプシートを無効化します。

最後にスタンプシートに設定された、スタンプシートを消費したときに呼び出される GS2-Script でロックを解除すれば処理は終わりです。

不正対策

ゲームに不正行為はつきものです。スタンプラリーではどのような不正対策をおこなっているのでしょうか。

署名

スタンプタスク毎/スタンプシート全体 に署名がつけられています。
これによって、スタンプタスク内に含まれる 対価の消費数量 や、スタンプシートに含まれるタスクの数や種類を差し替えようとしても出来ないようになっています。

スタンプシートの無効化

報酬を得たスタンプシートはその時点で無効化しているため、全てのタスクを終えた1つのスタンプシートを使って何度も報酬を得ることは出来ません。

状態保証の不正取得

変化が起きる前にスタンプシートを何度も取得して購入回数制限などを突破しようとしたとします。
この場合、GS2-Lock を使用してリソースのロックを取得している場合は、2回目以降はそのロックが取れずに失敗します。
仮に GS2-Lock を使っていなかったとしても、GS2-Limit で購入回数をカウントアップするタスクを実行する際に不正に取得したスタンプシートでは回数上限に達してしまいタスクを完了出来ないため、報酬を得ることも出来ません。

スタンプシートの特徴

スタンプタスクを実行する際には、誰が何のためにタスクを実行しようとしているのか、消費されるマイクロサービスは知る必要がありません。
『スタンプシートを発行出来るのは信頼出来るシステムからに限定されている』という前提で信頼してタスクを実行します。

スタンプシートを消費して報酬を付与する際も同様で、誰が何の前提処理をして報酬を付与しているのかを、報酬を付与する側のマイクロサービスは知る必要がありません。

この信頼関係を前提として、各マイクロサービスはタスク・シートを処理することで、疎結合ながらも一貫したデータ更新が行えます。

スタンプシートの課題

スタンプシートは特性上、報酬は1つのマイクロサービスに対して行う必要があります。
そのため、複数のマイクロサービスに影響を与える報酬を同期的に処理することは出来ません。

GS2 では複数の報酬があるものに関しては GS2-JobQueue を使用したジョブを報酬毎に登録することで、結果整合で報酬が付与される仕組みで対応する予定です。

それでは、また。

(C) Game Server Services, Inc.