> For the complete documentation index, see [llms.txt](/llms.txt)

# Combining Transaction Actions

Which actions may be placed in one transaction together. Background for reading the combination tables in the API reference of each service.




The API reference of each service has a table titled "what you can put in one transaction".
This page collects the background you need in order to read those tables.

## The "kind" column

The "kind" column in each service's action table says which of the three lists of a transaction the action belongs to: verify, consume, or acquire.

It does not always match the name of the action or what it actually does. An action named `Verify` may be a consume action, and an action that updates nothing may be an acquire action.

## How to read the table on each service's page

Each service's page carries a table of that service's operations.

| Column | Meaning |
| --- | --- |
| Operation | The unit that gets combined. Operations sharing a row are combined into one |
| Repeated in one transaction | What happens when the operations of that row are specified more than once |
| Across a nested transaction | What happens when an update to the same target also arrives from an inner transaction |
| Boundary that separates targets | Differ here and they are different targets, so any number of them may be listed |

**Within one boundary, operations from different rows cannot be placed together: the transaction fails.** Rows are separate precisely because there is no defined way to compose their values.

The result columns use these words.

| Word | Meaning |
| --- | --- |
| combined | Merged into one, with the values added up or concatenated |
| combined if equal | Merged into one if they specify the same value; rejected when the transaction is issued if the values differ |
| first wins | Not an error; the second and later ones are silently dropped |
| fails | The transaction fails (400). Retrying does not help |
| no collision | They may be placed together |

Where those five words are not enough, the cell simply states what that service does, as in "the value that arrives later overwrites the other" or "does not touch the progress".

Verify actions only read, so they may sit alongside any update. The verify row is the one exception to the rule above.

When verify actions are stacked, **only those matching in target, verify type, and threshold** are gathered into one. Differ in any of the three and each is judged on its own.

Being gathered behaves in one of two ways, depending on the action.

| Behaviour | Result |
| --- | --- |
| thresholds are added up | Writing exactly the same check twice gives a single check with twice the threshold |
| the second is dropped | The checks are identical, so the result is unchanged |

Each service's page says which one applies. An action that explicitly passes `multiplyValueSpecifyingQuantity` as `false` is excluded from the grouping and is always judged on its own.

"Rejected when the transaction is issued", in the repeated-in-one-transaction column, means the transaction is refused while it is being put together. Across a nested transaction that check does not run, so the same disagreement surfaces as a failure at run time instead. Both are 400 and neither is fixed by retrying.

If you cannot find a row that matches your case, the combination is allowed. Read the absence of a row as "no restriction", not as "undefined". Actions of different services never collide either.

## Kinds of failure, and when to retry

| Wording in the table | When it is refused | Status |
| --- | --- | --- |
| The transaction fails | At run time | 400 |
| Rejected when the transaction is issued | Earlier, while the transaction is being put together | 400 |

Both are problems with how the transaction is put together. Retrying does not help.

A conflict (409) only means the state was contended, so it can be retried. Some services have conflicts that a retry cannot clear; where that is the case, their page says so.

## Nested transactions

Some actions issue a transaction of their own. These are the ones that do:

a lottery draw (GS2-Lottery), an exchange (GS2-Exchange), enhancing and unleashing (GS2-Enhance), taking a reward (GS2-Mission / GS2-LoginReward / GS2-Idle / GS2-Ranking2), opening a message (GS2-Inbox), using a serial code (GS2-SerialKey), buying from a showcase (GS2-Showcase), releasing a node (GS2-SkillTree), applying a multiplier (GS2-Experience / GS2-Grade), running a script (GS2-Script), and starting a state machine (GS2-StateMachine).

Each service's combination table is about **actions specified directly on one transaction**. Updates issued by an inner transaction are not combined with them. This happens in two shapes:

- **outer and inner** — an action specified directly on the transaction and an update issued by an inner transaction address the same target
- **inner and inner** — two or more nested actions each touch the same target from inside

The second is the one that gets missed. Drawing ten times, taking several login bonus steps at once, and buying two items from a showcase all take this shape. Sibling inner transactions are not combined either, so they behave exactly like the outer-and-inner case.

In both shapes, the transaction fails where the target does not allow two writes. A collision like this is treated as a malformed transaction, so retrying does not clear it.

### How to find out what a nested action does

What a nested action grants or consumes is decided by the master data it refers to: the prize table of a lottery, the rewards of a mission, the campaign of a serial code, the bonus model of a login bonus.

Most of what they grant belongs to GS2-Inventory (items), GS2-Money2 (currency), or GS2-Experience (experience). Check the pages of those services for whether the outer transaction touches the same target.

## If you want to avoid these restrictions

The restrictions above come from executing a transaction as a single atomic commit. You can change how a transaction is executed with the [transaction setting]() of **the namespace that issues it**. A collision with an update arriving from an inner transaction is avoided the same way, by changing this setting on the namespace that issues the outer transaction.

- Turning `enableAtomicCommit` off makes the actions run one at a time instead of together, so several actions may update the same target without colliding. In exchange, the transaction no longer succeeds or fails as a whole, and a partially applied state becomes possible.
- Turning `acquireActionUseJobQueue` on keeps the atomic commit but routes the acquire actions through GS2-JobQueue when there are two or more of them, so they run one at a time. Verify and consume actions stay atomic. In exchange, the acquire actions become asynchronous and are not yet reflected when the transaction responds.

Both are settings of the namespace and affect every transaction it issues, so weigh them against the guarantees you want to keep.

`acquireActionUseJobQueue` separates acquire actions from each other only. It does not help where the failure comes from pairing an acquire action with a consume action. Each service's page says which of the two applies.

Anything described as "rejected when the transaction is issued" is lifted by neither setting: that decision is made while the transaction is put together, before any execution policy applies.

## The order of execution, and what each action sees

Verify actions run first, then consume actions, then acquire actions. No setting changes that order.

Under the default atomic commit, every action is judged against the state at the start of the transaction. Changes made by the other actions of the same transaction are invisible to all of them. A transaction therefore cannot express "the create action sees the result of the delete" or "the verify action sees the result of the update".

When `enableAtomicCommit` is off, and when `acquireActionUseJobQueue` is on, the acquire actions run one at a time and each of them sees the results so far. With the former the consume actions also run one at a time, so the acquire actions see their results too.

Verify and consume actions never run after acquire actions, though. No setting lets a transaction consume what it acquired. Split the transaction where an order is required.



