GS2-Limit Transaction Actions

Specification of verify/consume/acquire transaction actions

Combining actions, and concurrency

The background common to every service is collected in Combining Transaction Actions. Read that first; the rest of this section is what GS2-Limit adds to it.

The transaction actions of GS2-Limit address one counter, identified by the combination of namespace, user, limit model, and counter name.

Operation Repeated in one transaction Across a nested transaction Boundary that separates targets Under sequential execution mode
Counting up and down
CountUpByUserId CountDownByUserId
Any mix; combined and the values added up. Count ups with differing maxValue are rejected when the transaction is issued Added up namespace, user, limit model, counter name No change; it already succeeds by being added up
Deleting a counter
DeleteCounterByUserId
Combined into one Fails namespace, user, limit model, counter name Succeeds; combined into one even across a nested transaction, and can now sit alongside a count up in the same transaction (see below)
Verifying
VerifyCounterByUserId
Checks that match exactly are gathered into one. The thresholds are added up only when multiplyValueSpecifyingQuantity is explicitly true; the verify type does not matter, because writing it declares that the threshold is the amount for one unit. Checks with differing counts are each judged No collision, because it only reads counter, verify type, count No change; it already succeeds because it only reads

Counting up and down are one row because both are written as a pure increment on the same value; mixing them is fine. The result is judged against the total after combining. The count cannot go below zero and cannot exceed the limit set on the limit model, so a count up that fits on its own can still be rejected when combined with another one.

Count ups with differing upper limits cannot be placed against the same counter. Adding them up while the limits disagree would either drop a stricter limit or have a permissive increment rejected by a stricter one, so count ups with differing maxValue against one counter are rejected when the transaction is issued. Specify them separately.

Deleting a counter removes the counter itself rather than changing its value, so it cannot sit alongside anything else on that counter. Split the transaction where you want to reset a counter and then start counting again. Turning on sequential execution mode (enableSequentialExecution or TransactionSettingV2) lets a delete sit alongside a count up. The order is not yours to choose: counting up is a consume action and the delete is an acquire action, so the delete always runs last and removes whatever was just counted. A delete cannot be used to start a counter fresh and then count from zero in the same transaction.

A counter is a state (how many times something has been done), so its threshold does not scale with the quantity by default. What scales is the paired count up. Setting multiplyValueSpecifyingQuantity to true explicitly makes the threshold add up and scale with the quantity. Even then it is added up only where adding it up makes the check stricter. With greater and greaterEqual a larger threshold is harder to satisfy, so repeating the same check tightens it, and multiplyValueSpecifyingQuantity scales it with the purchased quantity. With less and lessEqual a larger threshold is easier to satisfy, so adding up would let a limit be evaded by repeating the check or by raising the quantity; these are only gathered into one and the quantity does not scale them. equal and notEqual turn into a different claim when added up, so they are only gathered into one as well.

Verify actions look at the counter as of the start of the transaction. You cannot verify a count that is reached by the same transaction.

Take care with nested transactions

Counting up and down are safe across a nested transaction: they are applied together as one update whichever route they arrive by.

Deleting a counter is not. A deletion arriving from the inside while the same counter is counted from the outside makes the transaction fail. Sequential execution mode removes this failure where the count is a count up: the inner transaction now runs inside the same sequential section as the outer one, and a count up followed by the delete composes instead of colliding. Where the delete runs first and a count down follows, the two still cannot be combined and the transaction fails.

If you want to avoid these restrictions

Counting down and deleting a counter are acquire actions; counting up is a consume action. Turning acquireActionUseJobQueue on clears a collision between a deletion and a count down, but a deletion together with a count up needs enableAtomicCommit turned off.

Concurrency and retries

Counting up and down do not conflict however many concurrent requests overlap, as long as the result stays within range. A conflict (409) is returned when concurrent count ups together exceed the limit, or when concurrent count downs together take the count below zero; retrying re-evaluates against the latest count.

A counter is reset on a schedule. A request that lands exactly on a reset boundary may return a conflict (409); retrying after the reset has settled will succeed.


Verify Action

Gs2Limit:VerifyCounterByUserId

Verify Counter value by User ID

Verifies that the specified user’s counter value satisfies the given condition. Supports 6 comparison operators: less, lessEqual, greater, greaterEqual, equal, notEqual.

Quantity specification supported: NO

Type Condition Required Default Value Limits Description
namespaceName string
✓
~ 128 chars Namespace name
Unique Namespace name. Specified using alphanumeric characters, hyphens (-), underscores (_), and periods (.).
userId string
✓
~ 128 chars User ID
Specify #{userId} to substitute the currently logged-in user’s ID.
limitName string
✓
~ 128 chars Usage Limit Model Name
The name of the limit model that this counter belongs to. Determines the reset schedule (daily, weekly, monthly, etc.) applied to this counter’s value.
counterName string
✓
~ 128 chars Counter Name
A unique identifier for this counter within the limit model. Multiple counters can share the same limit model with different names, allowing separate usage tracking (e.g., one counter per quest or per product) without creating separate limit models.
verifyType string (enum)
enum {
  “less”,
  “lessEqual”,
  “greater”,
  “greaterEqual”,
  “equal”,
  “notEqual”
}
✓
Type of verification
DefinitionDescription
lessPossession quantity is less than the specified value
lessEqualPossession quantity is less than or equal to the specified value
greaterPossession quantity is greater than the specified value
greaterEqualPossession quantity is greater than or equal to the specified value
equalPossession quantity is equal to the specified value
notEqualPossession quantity is not equal to the specified value
count int 0 0 ~ 2147483646 Count Value
The current usage count for this counter. Incremented by the countUp operation and compared against the maximum value specified at that time. Automatically reset to zero when the limit model’s reset timing is reached.
multiplyValueSpecifyingQuantity bool true Whether to multiply the value used for verification when specifying the quantity
timeOffsetToken string ~ 1024 chars Time offset token
{
    "action": "Gs2Limit:VerifyCounterByUserId",
    "request": {
        "namespaceName": "[string]Namespace name",
        "userId": "[string]User ID",
        "limitName": "[string]Usage Limit Model Name",
        "counterName": "[string]Counter Name",
        "verifyType": "[string]Type of verification",
        "count": "[int]Count Value",
        "multiplyValueSpecifyingQuantity": "[bool]Whether to multiply the value used for verification when specifying the quantity",
        "timeOffsetToken": "[string]Time offset token"
    }
}
action: Gs2Limit:VerifyCounterByUserId
request:
  namespaceName: "[string]Namespace name"
  userId: "[string]User ID"
  limitName: "[string]Usage Limit Model Name"
  counterName: "[string]Counter Name"
  verifyType: "[string]Type of verification"
  count: "[int]Count Value"
  multiplyValueSpecifyingQuantity: "[bool]Whether to multiply the value used for verification when specifying the quantity"
  timeOffsetToken: "[string]Time offset token"
transaction.service("limit").verify.verify_counter_by_user_id({
    namespaceName="[string]Namespace name",
    userId="[string]User ID",
    limitName="[string]Usage Limit Model Name",
    counterName="[string]Counter Name",
    verifyType="[string]Type of verification",
    count="[int]Count Value",
    multiplyValueSpecifyingQuantity="[bool]Whether to multiply the value used for verification when specifying the quantity",
    timeOffsetToken="[string]Time offset token",
})

Consume Action

Gs2Limit:CountUpByUserId

Count-up by User ID

Increments the specified user’s counter by the specified count-up value. If maxValue is specified, the counter will not exceed that limit; an Overflow error is returned if the operation would exceed the maximum. If the counter does not yet exist, it is automatically created.

Quantity specification supported: YES

Reversible action: YES

Type Condition Required Default Value Limits Description
namespaceName string
✓
~ 128 chars Namespace name
Unique Namespace name. Specified using alphanumeric characters, hyphens (-), underscores (_), and periods (.).
limitName string
✓
~ 128 chars Usage Limit Model Name
The name of the limit model that this counter belongs to. Determines the reset schedule (daily, weekly, monthly, etc.) applied to this counter’s value.
counterName string
✓
~ 128 chars Counter Name
A unique identifier for this counter within the limit model. Multiple counters can share the same limit model with different names, allowing separate usage tracking (e.g., one counter per quest or per product) without creating separate limit models.
userId string
✓
~ 128 chars User ID
Specify #{userId} to substitute the currently logged-in user’s ID.
countUpValue int 1 1 ~ 2147483646 Amount to count up
maxValue int 1 ~ 2147483646 Maximum value allowed to count up
timeOffsetToken string ~ 1024 chars Time offset token
{
    "action": "Gs2Limit:CountUpByUserId",
    "request": {
        "namespaceName": "[string]Namespace name",
        "limitName": "[string]Usage Limit Model Name",
        "counterName": "[string]Counter Name",
        "userId": "[string]User ID",
        "countUpValue": "[int]Amount to count up",
        "maxValue": "[int]Maximum value allowed to count up",
        "timeOffsetToken": "[string]Time offset token"
    }
}
action: Gs2Limit:CountUpByUserId
request:
  namespaceName: "[string]Namespace name"
  limitName: "[string]Usage Limit Model Name"
  counterName: "[string]Counter Name"
  userId: "[string]User ID"
  countUpValue: "[int]Amount to count up"
  maxValue: "[int]Maximum value allowed to count up"
  timeOffsetToken: "[string]Time offset token"
transaction.service("limit").consume.count_up_by_user_id({
    namespaceName="[string]Namespace name",
    limitName="[string]Usage Limit Model Name",
    counterName="[string]Counter Name",
    userId="[string]User ID",
    countUpValue="[int]Amount to count up",
    maxValue="[int]Maximum value allowed to count up",
    timeOffsetToken="[string]Time offset token",
})

Acquire Action

Gs2Limit:CountDownByUserId

Count-down by User ID

Decrements the specified user’s counter by the specified count-down value. The counter value will not go below 0.

Quantity specification supported: YES

Reversible action: YES

Type Condition Required Default Value Limits Description
namespaceName string
✓
~ 128 chars Namespace name
Unique Namespace name. Specified using alphanumeric characters, hyphens (-), underscores (_), and periods (.).
limitName string
✓
~ 128 chars Usage Limit Model Name
The name of the limit model that this counter belongs to. Determines the reset schedule (daily, weekly, monthly, etc.) applied to this counter’s value.
counterName string
✓
~ 128 chars Counter Name
A unique identifier for this counter within the limit model. Multiple counters can share the same limit model with different names, allowing separate usage tracking (e.g., one counter per quest or per product) without creating separate limit models.
userId string
✓
~ 128 chars User ID
Specify #{userId} to substitute the currently logged-in user’s ID.
countDownValue int 1 1 ~ 2147483646 Amount to count down
timeOffsetToken string ~ 1024 chars Time offset token
{
    "action": "Gs2Limit:CountDownByUserId",
    "request": {
        "namespaceName": "[string]Namespace name",
        "limitName": "[string]Usage Limit Model Name",
        "counterName": "[string]Counter Name",
        "userId": "[string]User ID",
        "countDownValue": "[int]Amount to count down",
        "timeOffsetToken": "[string]Time offset token"
    }
}
action: Gs2Limit:CountDownByUserId
request:
  namespaceName: "[string]Namespace name"
  limitName: "[string]Usage Limit Model Name"
  counterName: "[string]Counter Name"
  userId: "[string]User ID"
  countDownValue: "[int]Amount to count down"
  timeOffsetToken: "[string]Time offset token"
transaction.service("limit").acquire.count_down_by_user_id({
    namespaceName="[string]Namespace name",
    limitName="[string]Usage Limit Model Name",
    counterName="[string]Counter Name",
    userId="[string]User ID",
    countDownValue="[int]Amount to count down",
    timeOffsetToken="[string]Time offset token",
})

Gs2Limit:DeleteCounterByUserId

Delete Counter by User ID

Deletes the specified user’s counter, resetting the usage count. This effectively removes the limit restriction for this counter, allowing the user to start counting from 0 again.

Quantity specification supported: NO

Reversible action: NO

Type Condition Required Default Value Limits Description
namespaceName string
✓
~ 128 chars Namespace name
Unique Namespace name. Specified using alphanumeric characters, hyphens (-), underscores (_), and periods (.).
limitName string
✓
~ 128 chars Usage Limit Model Name
The name of the limit model that this counter belongs to. Determines the reset schedule (daily, weekly, monthly, etc.) applied to this counter’s value.
userId string
✓
~ 128 chars User ID
Specify #{userId} to substitute the currently logged-in user’s ID.
counterName string
✓
~ 128 chars Counter Name
A unique identifier for this counter within the limit model. Multiple counters can share the same limit model with different names, allowing separate usage tracking (e.g., one counter per quest or per product) without creating separate limit models.
timeOffsetToken string ~ 1024 chars Time offset token
{
    "action": "Gs2Limit:DeleteCounterByUserId",
    "request": {
        "namespaceName": "[string]Namespace name",
        "limitName": "[string]Usage Limit Model Name",
        "userId": "[string]User ID",
        "counterName": "[string]Counter Name",
        "timeOffsetToken": "[string]Time offset token"
    }
}
action: Gs2Limit:DeleteCounterByUserId
request:
  namespaceName: "[string]Namespace name"
  limitName: "[string]Usage Limit Model Name"
  userId: "[string]User ID"
  counterName: "[string]Counter Name"
  timeOffsetToken: "[string]Time offset token"
transaction.service("limit").acquire.delete_counter_by_user_id({
    namespaceName="[string]Namespace name",
    limitName="[string]Usage Limit Model Name",
    userId="[string]User ID",
    counterName="[string]Counter Name",
    timeOffsetToken="[string]Time offset token",
})