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

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

各サービスの API リファレンスには「1 つのトランザクションに指定できる組み合わせ」という表があります。 このページは、その表を読むための前提をまとめたものです。

アクションの「種別」

各サービスのアクション表にある「種別」は、そのアクションをトランザクションの検証・消費・入手のどの並びに置けるかを表します。

アクションの名前や実際の効果と一致しないことがあります。名前が Verify でも消費アクションだったり、何も更新しないのに入手アクションだったりします。

各サービスの表の読み方

各サービスのページには、そのサービスの操作を並べた表があります。

意味
操作まとめられる単位。同じ行に並ぶ操作どうしは 1 件に統合されます
同じ行を重ねたときその行の操作を 1 つのトランザクションに複数指定した結果
入れ子越し同じ対象への更新が内側のトランザクションからも届いた場合の結果
別の対象になる境界ここが違えば別の対象なので、いくつ並べても衝突しません

同じ境界の中で、違う行の操作を重ねるとトランザクションが失敗します。 行が分かれているのは、値を合成する方法が定義できないからです。この失敗が起こるのはアクションを並列に実行する場合で、既定の直列実行では起こりません。詳しくは 直列実行と並列実行 を参照してください。

結果の欄には次の語を使います。

意味
統合される1 件にまとめられ、値が合算または結合されます
同値なら統合同じ値を指定していれば 1 件になります。値が違えば発行時にエラーになります
先勝ちエラーにならず、2 つ目以降が黙って捨てられます
失敗するトランザクションが失敗します (400)。リトライしても直りません
衝突しない重ねてかまいません

この 5 語で足りない場合、セルにはそのサービス固有の結果がそのまま書かれます。「後から届いたほうの値で上書きされる」「進行を触らない」のような記述がそれです。

検証アクションは読み取りしか行わないので、どの更新と同居させてもかまいません。検証の行だけは上の規則の例外です。

検証アクションどうしを重ねた場合、対象・検証タイプ・しきい値がすべて同じものだけが 1 つにまとめられます。どれか 1 つでも違えば、それぞれ独立に判定されます。

まとめられたときの扱いは 2 通りあり、アクションによって違います。

扱い結果
しきい値が合算されるまったく同じ検証を 2 回書くと、しきい値が 2 倍の検証 1 件になります
2 つ目が捨てられるまったく同じ検証なので、結果は変わりません

どちらになるかは各サービスのページに書いています。multiplyValueSpecifyingQuantityfalse を明示したアクションはグループ化の対象から外れ、常にそれぞれ独立に判定されます。

「同じ行を重ねたとき」の欄にある「発行時にエラー」は、トランザクションを組み立てる時点で弾かれます。入れ子越しの場合はその検査が働かないため、同じ食い違いが実行時の失敗になります。どちらも 400 で、リトライしても直りません。

表に該当する行が見つからない場合、その組み合わせは指定してかまいません。「書かれていないので分からない」ではなく「制限がない」と読んでください。別のサービスのアクションどうしも衝突しません。

失敗の種類とリトライ

表の記述いつ拒否されるかステータス
トランザクションが失敗します実行時400
発行時にエラーになります実行より前、トランザクションを組み立てる時点400

どちらもトランザクションの組み方そのものの問題です。リトライしても直りません。

コンフリクト (409) は状態が競合しただけなので、リトライしてかまいません。ただしサービスによっては、リトライしても解消しないコンフリクトがあります。該当する場合は各サービスのページに書いています。

入れ子になったトランザクション

アクションの中には、そのアクション自身がさらに別のトランザクションを発行するものがあります。次が該当します。

抽選 (GS2-Lottery)、交換 (GS2-Exchange)、強化と解放 (GS2-Enhance)、報酬の受け取り (GS2-Mission / GS2-LoginReward / GS2-Idle / GS2-Ranking2)、メッセージの開封 (GS2-Inbox)、シリアルコードの使用 (GS2-SerialKey)、陳列棚からの購入 (GS2-Showcase)、ノードの解放 (GS2-SkillTree)、倍率の適用 (GS2-Experience / GS2-Grade)、スクリプトの実行 (GS2-Script)、ステートマシンの開始 (GS2-StateMachine)

各サービスの組み合わせの表は、1 つのトランザクションに直接指定したアクションどうしの話です。内側のトランザクションが発行する更新は、それらと統合されません。これは 2 つの形で起こります。

  • 外側と内側 — トランザクションに直接指定したアクションと、内側のトランザクションが発行する更新が、同じ対象を触る
  • 内側どうし — 2 つ以上の入れ子アクションが、それぞれの内側で同じ対象を触る

後者を見落としがちです。10 連の抽選、複数ステップのログインボーナスをまとめて受け取る、陳列棚から 2 商品を買う、といった構成はすべてこの形になります。入れ子どうしも統合されないので、外側と内側の場合とまったく同じ扱いです。

どちらも、並列実行のもとでその対象が 2 つの書き込みを許さない場合はトランザクションが失敗します。この衝突は不正なトランザクションとして扱われるので、リトライしても解消しません。既定の直列実行では起こりません。

入れ子の中身をどう調べるか

入れ子のアクションが何を配る (消費する) かは、そのアクションが参照するマスタデータで決まります。抽選なら景品テーブル、ミッションなら報酬、シリアルコードならキャンペーン、ログインボーナスならボーナスモデルです。

配られるものの多くは GS2-Inventory のアイテム、GS2-Money2 の通貨、GS2-Experience の経験値です。同じ対象を外側でも触っていないかを、それぞれのサービスのページで確認してください。

直列実行と並列実行

ここまでの「失敗する」――同じ境界の中で違う行の操作が重なる、入れ子の内側と外側が同じ対象を触る――は、アクションをまとめて同時に実行することから来ています。実行の方針は、そのトランザクションを発行するネームスペーストランザクション設定 で決まります。

  • 直列実行(既定・enableParallelExecutionfalse)では、アクションは 1 件ずつ実行され、それぞれが直前までの結果を見ます。同じ行を複数のアクションから更新できるため、これらの失敗は起こりません。入れ子の内側から届く更新との衝突も同じで、外側のトランザクションを発行するネームスペースが直列実行であれば衝突しません。
  • 並列実行enableParallelExecutiontrue)では、アクションは同一のデータスナップショットに対して同時に実行されます。同じ行を 2 つのアクションが書き換えると、トランザクションは database:transaction:same.resource(400)で失敗します。表の「失敗する」はこの場合の結果です。

並列実行を選ぶのは、レスポンスタイムを切り詰めたい場合や、1 つのトランザクションに 21 件以上のアクションを含める必要がある場合です。その場合は表を読んで、同じ境界を触るアクションが重ならないようにトランザクションを組んでください。

どちらもネームスペースの設定で、そのネームスペースが発行するすべてのトランザクションに影響します。維持したい保証と見比べて選んでください。

「発行時にエラーになります」と書かれているものは、どちらの実行方針でも解消しません。実行の方針より前、トランザクションを組み立てる時点の判定だからです。

実行順序と、各アクションが見る状態

検証アクション、消費アクション、入手アクションの順に実行されます。この順序はどちらの実行方針でも変わりません。

直列実行では、各アクションは同じトランザクション内で先に実行されたアクションの書き込みを見ます。List や Query の結果でも、入れ子になったトランザクションの内側でも見えます。トランザクションを発行した API が同じリクエスト内で先に行った更新――事前スクリプトによる書き換えを含みます――も見えます。各フェーズの中での実行順はアクション名、次に対象リソースの順で決まるため、リクエストに並べた順で実行順を指定することはできません。

並列実行では、すべてのアクションがトランザクション開始時点の状態を基準に判定されます。同じトランザクションの他のアクションが行った変更は、どのアクションからも見えません。そのため「削除した結果を作成アクションが見る」「更新した結果を検証アクションが見る」といった組み方はできません。

ただし検証アクションと消費アクションが入手アクションより後に実行されることはありません。「入手したものを同じトランザクションで消費する」という組み方は、どちらの実行方針でもできません。順序が必要な場合はトランザクションを分けてください。