トランザクション設定

Game Server Services のトランザクション設定(V2)の設計方針

GS2 が提供するマイクロサービスには、概ねネームスペース設定に transactionSettingV2 というフィールドが存在します。 そのネームスペースが発行するトランザクションをどのように実行するかは、この設定で決まります。

TransactionSettingV2 は以下の構造を持ちます。

有効化条件必須デフォルト値の制限説明
distributorNamespaceIdstring
“grn:gs2:{region}:{ownerId}:distributor:default”~ 1024文字トランザクションの実行に使用する GS2-Distributor ネームスペース
enableParallelExecutionbool
falseアクションを直列ではなく並列に実行するか

設定する項目はこの 2 つだけです。どちらを選んでも、トランザクションの実行方法に関するそれ以外の項目は推奨構成に固定されているため、次の 3 点は常に成り立ちます。

  • トランザクションは発行された時点でサーバーが実行します。クライアントがスタンプシートを実行する必要はありません
  • 成否はトランザクション全体でひとつです。あるアクションが失敗した場合は、それまでに実行されたアクションもろとも取り消され、1 つも反映されません
  • トランザクションを発行した API の応答が返る時点で、トランザクションの実行は完了しています

この 3 点は、トランザクションを発行した API の事前スクリプトにも適用されます。 たとえば商品購入時のスクリプトを設定している場合、そのスクリプトが GS2 の API を呼び出して行ったデータの書き換えや、スクリプトが発行したトランザクションも、商品購入 API が成功したときにまとめて反映されます。

フィールドの解説

distributorNamespaceId

トランザクションの実行に使用する GS2-Distributor のネームスペースを設定します。 用途ごとにトランザクションの実行を分けたい場合を除き、プロジェクトに用意される default ネームスペースのままで問題ありません。

enableParallelExecution

トランザクションに含まれるアクションを 1 件ずつ実行するか、まとめて同時に実行するかを設定します。 実質的に選択の余地があるのはこの項目だけです。

直列実行(既定 / false

各アクションは、先に実行されたアクションの書き込みを踏まえて動作します。 アクションは検証・消費・入手の順に 1 件ずつ実行され、次のことができます。

  • 1 つのトランザクションの中で、同じ行を複数のアクションから更新する
  • 先に実行されたアクションの書き込みを読む。List や Query の結果でも、入れ子になったトランザクションの内側でも読めます
  • トランザクションを発行した API が同じリクエスト内で先に行った更新を読む。事前スクリプトによる書き換えもこれに含まれ、アクションの書き込みと順序どおりに 1 つのコミットへ合流します
  • %{Gs2Xxx:ActionName.path[0].field} を使って、先に実行された検証・消費アクションの結果を、後続の検証・消費・入手アクションの引数として渡す

%{...} で参照元にできるのは検証アクションと消費アクションの結果だけで、入手アクションの結果は参照できません。 同名のアクションが複数あるときは、最初に実行されたものが優先されます。 解決できないプレースホルダは引数の値としてそのまま残るため、数値フィールドであれば検証に失敗することがあります。

代償は次のとおりです。

  • レスポンスタイムは、最も遅いアクション 1 件分ではなく、各アクションの実行時間の合計になります
  • 1 つのトランザクションに含められるアクションは最大 20 件です。超えて発行すると発行時に失敗します
  • 実行は最初に失敗したアクションで停止します。実行中だったフェーズの結果はその失敗したアクションまでが返り、以降のフェーズは空で返ります。並列実行であれば後続のアクションで観測できたはずの 5xx エラーが観測されなくなるため、リトライすべきかどうかを判断する材料は並列実行より狭くなります

各フェーズの中での実行順は、アクション名、次に対象リソースの順で決まります。 リクエストに並べた順ではないため、並べ替えて実行順を指定することはできません。 なお、消費アクションは名前によらず必ず入手アクションより先に実行されます。

並列実行(true

アクションは同一のデータスナップショットに対して並列に実行されます。 レスポンスタイムは最も遅いアクション 1 件分で済み、アクション数の上限もありません。

代償は次のとおりです。

  • あるアクションから他のアクションの書き込みを読むことはできません
  • 同じ行を 2 つのアクションが書き換えると、トランザクションは database:transaction:same.resource(400)で失敗します
  • %{...} は先行するフェーズ(検証 → 消費 → 入手の順)の結果だけを参照できます。同じフェーズ内のアクションへの参照は解決されずにそのまま残ります。また %{...} を含むフェーズは先行するフェーズの完了を待ってから実行されるため、その分だけレスポンスタイムが伸びます

同一トランザクション内で同じデータを 2 つ以上のアクションが書き換えないことを保証できる場合にだけ有効にしてください。

両者で変わらないこと

この設定は、トランザクションを発行する時点の挙動を変えません。 同じリソースを対象にしたアクションが 1 件に畳み込まれること、黙って捨てられること、エラーで拒否されることは、直列実行でも並列実行でも同じように発行時に起こります。 変わるのは、アクションが実行に到達したあとの挙動だけです。 どのアクションを 1 つのトランザクションに一緒に指定してよいかは トランザクションアクションの組み合わせ を参照してください。

どちらを選ぶべきか判断するフローチャート

graph TD
  Start["transactionSettingV2 を設定"] --> Q3{"同じデータを 2 つ以上の<br/>アクションが書き換えないと<br/>保証できるか"}
  Q3 -- 保証できない --> Sequential["enableParallelExecution = false(既定)"]
  Q3 -- 保証できる --> Q4{"先に実行されるアクションの結果を<br/>後続のアクションから参照しているか"}
  Q4 -- 同じフェーズ内で参照している --> Sequential
  Q4 -- 参照していない、または<br/>先行するフェーズだけを参照している --> Q1{"アクションが 21 件以上になる、または<br/>レスポンスタイムを切り詰めたいか"}
  Q1 -- どちらでもない --> Sequential
  Q1 -- どちらかに当てはまる --> Parallel["enableParallelExecution = true"]

判断に迷う場合は既定の直列実行のままにしてください。 直列実行はトランザクションの組み方に対する制約が最も少なく、並列実行で失敗する組み合わせのほとんどが直列実行では成立します。

なお直列実行では 21 件以上のアクションを発行できないため、アクション数が上限を超えるうえに同じデータへの書き込みも避けられない場合は、トランザクションを分割してください。

旧 TransactionSetting との関係

TransactionSettingV2 が登場する前は、ネームスペース設定の transactionSetting でトランザクションの実行方法を指定していました。 transactionSetting は非推奨です。TransactionSettingV2 が存在しなかった頃に作成されたネームスペースのために残されており、TransactionSettingV2 が設定されていない間だけ適用されます。 新しく作成するネームスペースでは使用しないでください。

transactionSetting は、トランザクションの実行に関わる要素――自動実行(AutoRun)、アトミック実行(AtomicCommit)、GS2-Distributor を利用した非同期実行、スクリプト結果の一括適用、GS2-JobQueue による入手アクションの非同期化、直列実行――をそれぞれ個別の項目として公開しているため、推奨されない組み合わせも作れてしまいます。 その推奨構成を 1 つの設定としてまとめたものが TransactionSettingV2 です。

TransactionSettingV2 を設定すると、transactionSetting の各項目は次のように固定されます。

transactionSetting の項目TransactionSettingV2 での値
enableAutoRuntrue
enableAtomicCommittrue
enableSequentialExecutionenableParallelExecution の否定
transactionUseDistributorfalse
commitScriptResultInUseDistributorfalse
acquireActionUseJobQueuefalse

そのため、TransactionSettingV2 を使うネームスペースでは次の 3 つを利用できません。

  • トランザクションをクライアント実行のスタンプシートとして実行する
  • GS2-Distributor による AutoRun の非同期実行を行う
  • 入手アクションを GS2-JobQueue に畳み込む

transactionSetting を使い続けるのは、これらの挙動にすでに依存しているネームスペースに限ってください。 なお、かつてこれらの設定で回避していた競合の多くは、直列実行によって解消します。 事前スクリプトの書き換えとトランザクションの競合は同じリクエスト内の更新が 1 つのコミットへ合流することで、消費アクションと入手アクションの競合や入手アクションどうしの競合は同じ行を複数のアクションから更新できることで、それぞれ回避する必要がなくなります。


トランザクションアクションの組み合わせ

1 つのトランザクションにどのアクションを一緒に指定してよいか。各サービスの API リファレンスにある組み合わせの表を読む前提となる説明