GS2 Blog

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

トランザクション処理を同期実行できるようになりました

はじめに

GS2におけるトランザクションとは、何らかのリソースを消費して、なんらかのリソースを入手する一連の操作を指します。
たとえば「スタミナを消費してクエストを開始状態にする」であったり「クエストの開始状態を削除する代わりに、クエストのクリア報酬を得る」であったり「ミッションの達成報酬を受け取り済みにする代わりに、達成報酬を得る」というような操作を指します。

機能追加の背景

GS2 におけるトランザクション処理は非同期処理が基本です。
これは、マイクロサービスごとにサービス障害が発生した際や、通信エラーのようなリトライで改善する可能性があるエラーハンドリングをGS2サイドでよしなに行う狙いがあります。

一方で、ゲーム開発者のニーズを見ていると、リトライは自分たちでやるので、処理の完了を把握できるようにして欲しいというニーズがあることも感じていました。

追加された機能の詳細

TransactionSetting に enableAtomicCommit というパラメータが追加され、こちらを有効化することでトランザクション処理が同期的に実行されるようになります。
これまでGS2の非同期処理は、徐々に結果が反映されていく「結果整合」という方式でデータの更新を行ってきました。

enableAtomicCommit を有効にすると、All or Nothing で更新処理が行われます。
つまり、トランザクション内のいずれかのアクションの実行に失敗すると、すべての更新処理が無かったことになります。

これまで GS2-Exchange の Exchange のようなトランザクションを発行するAPIを呼び出すと、トランザクションIDが応答され、サーバーから非同期処理が完了したトランザクションIDの通知を受け取ったら、実行結果を取得する流れでトランザクションの実行結果を取得していましたが、enableAtomicCommit を有効にした場合、トランザクションの実行に失敗すると Exchange APIの呼び出しが失敗し、成功した場合は戻り値にトランザクションの実行結果も含まれるようになります。

これはゲーム開発者にとっては、より直感的なインターフェースとして操作できるようになると思いますが、以下の制約が生じます。

1回のトランザクションで更新可能なリソースが最大100であること

トランザクションアクションで、あらたなトランザクションを発行する場合、そのトランザクションを発行するマイクロサービスのネームスペース設定も enableAtomicCommit が有効になっている場合、それらのトランザクションの実行も同期実行の対象となります。
つまり、トランザクションの発行ツリーが巨大になると、更新するリソースの数が100を超える可能性があり、その場合エラーとなります。

1回のトランザクションの中で同一リソースに対する更新は一度しか行えないこと

消費と入手で同じリソースを操作するようなトランザクションを発行すると、トランザクションの実行時に失敗します。
これは、トランザクションツリーが深くなった時に意図せず同じリソースを更新しようとした時にも発生し得るため注意が必要です。

トランザクションの分割

同期実行のツリーが大きくなると意図しない結果を招く可能性があることはここまでの解説で理解いただけたと思います。
同期実行ツリーを大きくしすぎないために取れる対策として、2つの方法を用意しています。

GS2-JobQueueを利用

これまでは、複数の入手アクションが設定されている場合、GS2-JobQueue を利用することが義務付けられていました。
これは、入手アクションが複数存在する場合、一旦 GS2-JobQueue に「入手アクションを実行する」というジョブを登録することで、結果整合的に入手アクションを実行するためにそのような仕様になっていました。

しかし、同期実行時にはこの制限はなく、複数の入手アクションを設定しても GS2-JobQueue を使用する必要は必ずしもなく、デフォルトの振る舞いとしては使用しないようになっています。
つまり、複数の入手アクションを設定した GS2-Exchange の交換レートがあったとして、Exchange API を呼び出すと、Exchange が正常応答した時には、すべての入手アクションの実行が保障され、Exchange API の結果にもすべての入手アクションの実行結果が含まれます。

先ほど例示したような消費アクションと入手アクションで同じリソースを操作しようとする場合、GS2-JobQueue を経由するようにすることでトランザクションエラーを回避することができます。
具体的には、TransactionSetting で acquireActionUseJobQueue に true を設定します。
この設定を行うと、Exchange を呼び出した段階では消費のみが行われ、入手アクションとして GS2-JobQueue へのジョブ登録が行われます。このトランザクションでは同じリソースを更新することはありませんのでエラーにはなりません。
非同期処理で GS2-JobQueue でリソースの入手処理が行われますが、ここでは消費処理とは異なるトランザクションとして実行されますので、トランザクションエラーにはなりません。

GS2-Distributor の非同期自動実行を利用

同期実行のメリットは All or Nothing でトランザクションを実行できる点にもあります。
これまで通りトランザクションの実行は非同期でいいのだけれど、トランザクションの実行は All or Nothing で行って欲しい場合は TransactionSetting の transactionUseDistributor に true を指定することで、GS2-Exchange のようなトランザクションを発行するAPIを呼び出した段階ではトランザクションの実行完了を待たずに、トランザクションIDのみを応答するこれまでの使用を踏襲した上で、トランザクションの実行は同期的に実行が可能です。


色々とややこしい解説をしましたが、ゲームが発行するトランザクションの全てを置き換えることはできませんが、9割以上は同期実行が可能だと思われます。
同期実行を有効にするとトランザクションの実行にかかる時間はこれまでの仕様よりも短くなる傾向にあります。
ニーズに合わせて利用を検討してみてください。

(C) Game Server Services, Inc.