GS2-Inbox 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-Inbox adds to it.
The transaction actions of GS2-Inbox address one message, identified by the combination of namespace, user, and message. Sending always creates a new message, so each send is its own target.
| Operation | Repeated in one transaction | Across a nested transaction | Boundary that separates targets |
|---|---|---|---|
Sending a messageSendMessageByUserId | Each one becomes its own message; they never collide | No collision | every send is its own target |
Opening a messageOpenMessageByUserId | Combined into one | Fails | namespace, user, message |
Deleting a messageDeleteMessageByUserId | Combined into one | Fails | namespace, user, message |
Opening and deleting are separate rows sharing a boundary, so one message cannot be opened and deleted in the same transaction. Different messages are different targets, so opening or deleting several of them is fine.
A message sent by a transaction is treated as not yet existing by the other actions of that transaction, because every action works from the state at the start of the transaction. Split the transaction where you want to send a message and then act on it.
Opening a message hands out whatever is attached to it. Those are issued as a transaction of their own, so the restrictions of whichever services they belong to apply, and so does the section below. Opening two messages that carry the same item is the case to watch for.
Take care with nested transactions
A message opened from the inside collides with the same message deleted from the outside, and the transaction fails. What the opened message hands out gathers into the same transaction as everything else, so check the pages of the services those belong to.
If you want to avoid these restrictions
Sending a message is an acquire action; opening and deleting are both consume actions. acquireActionUseJobQueue therefore cannot separate an open from a delete, and clearing that needs enableAtomicCommit turned off.
Concurrency and retries
Sending does not conflict however many requests overlap, because each send creates its own message.
Opening and deleting are written against the message as a whole, so concurrent updates to the same message make the one confirmed later return a conflict (409). Nothing is wrong with the request, so retrying will succeed; where the message has already been opened or deleted, the retry returns that as an error instead.
Consume Action
Gs2Inbox:OpenMessageByUserId
Mark Message as Opened by User ID
Marks the specified message as read (opened) in the specified user’s inbox. This is a simple state transition that sets isRead to true without executing any acquire actions. To mark as read and also execute associated rewards, use the Read API instead.
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 (.). | ||
| userId | string | ✓ | ~ 128 chars | User ID Specify #{userId} to substitute the currently logged-in user’s ID. | ||
| messageName | string | ✓ | UUID | ~ 36 chars | Message name Maintains a unique name for each message. Names are automatically generated in UUID (Universally Unique Identifier) format and used to identify each message. | |
| timeOffsetToken | string | ~ 1024 chars | Time offset token |
{
"action": "Gs2Inbox:OpenMessageByUserId",
"request": {
"namespaceName": "[string]Namespace name",
"userId": "[string]User ID",
"messageName": "[string]Message name",
"timeOffsetToken": "[string]Time offset token"
}
}action: Gs2Inbox:OpenMessageByUserId
request:
namespaceName: "[string]Namespace name"
userId: "[string]User ID"
messageName: "[string]Message name"
timeOffsetToken: "[string]Time offset token"transaction.service("inbox").consume.open_message_by_user_id({
namespaceName="[string]Namespace name",
userId="[string]User ID",
messageName="[string]Message name",
timeOffsetToken="[string]Time offset token",
})Gs2Inbox:DeleteMessageByUserId
Delete message by User ID
Permanently removes a message from the specified user’s inbox. The message record is deleted regardless of its read status.
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 (.). | ||
| userId | string | ✓ | ~ 128 chars | User ID Specify #{userId} to substitute the currently logged-in user’s ID. | ||
| messageName | string | ✓ | UUID | ~ 36 chars | Message name Maintains a unique name for each message. Names are automatically generated in UUID (Universally Unique Identifier) format and used to identify each message. | |
| timeOffsetToken | string | ~ 1024 chars | Time offset token |
{
"action": "Gs2Inbox:DeleteMessageByUserId",
"request": {
"namespaceName": "[string]Namespace name",
"userId": "[string]User ID",
"messageName": "[string]Message name",
"timeOffsetToken": "[string]Time offset token"
}
}action: Gs2Inbox:DeleteMessageByUserId
request:
namespaceName: "[string]Namespace name"
userId: "[string]User ID"
messageName: "[string]Message name"
timeOffsetToken: "[string]Time offset token"transaction.service("inbox").consume.delete_message_by_user_id({
namespaceName="[string]Namespace name",
userId="[string]User ID",
messageName="[string]Message name",
timeOffsetToken="[string]Time offset token",
})Acquire Action
Gs2Inbox:SendMessageByUserId
Send a message by User ID
Creates and delivers a new message to the specified user’s inbox. The message can include metadata (arbitrary JSON content) and readAcquireActions (rewards granted when the message is read). Message expiration can be set using either an absolute timestamp (expiresAt) or a relative duration (expiresTimeSpan) from the time of delivery. If expiresAt is specified, it takes priority over expiresTimeSpan. The message starts in an unread state (isRead=false).
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 (.). | ||
| userId | string | ✓ | ~ 128 chars | User ID Specify #{userId} to substitute the currently logged-in user’s ID. | ||
| metadata | string | ✓ | ~ 4096 chars | Metadata Arbitrary data representing the message content, such as a JSON string containing the message title, body text, sender information, and display parameters. GS2 does not interpret this value; it is passed through to the game client for rendering the message UI. Maximum 4096 characters. | ||
| readAcquireActions | List<AcquireAction> | [] | 0 ~ 100 items | Acquire Actions on Open The list of acquire actions executed when the user opens this message. Used to attach rewards such as items, currency, or resources to messages. Multiple actions can be combined to grant different reward types simultaneously. Up to 100 actions per message. | ||
| expiresAt | long | Expiration datetime Unix time, milliseconds | ||||
| expiresTimeSpan | TimeSpan | The period from the time a message was received (reference time) until it was deleted | ||||
| timeOffsetToken | string | ~ 1024 chars | Time offset token |
{
"action": "Gs2Inbox:SendMessageByUserId",
"request": {
"namespaceName": "[string]Namespace name",
"userId": "[string]User ID",
"metadata": "[string]Metadata",
"readAcquireActions": [
{
"action": "[string]Type of Acquire Action",
"request": "[string]JSON string of the request used when executing the action"
}
],
"expiresAt": "[long]Expiration datetime",
"expiresTimeSpan": {
"days": "[int]Days",
"hours": "[int]Hours",
"minutes": "[int]Minutes"
},
"timeOffsetToken": "[string]Time offset token"
}
}action: Gs2Inbox:SendMessageByUserId
request:
namespaceName: "[string]Namespace name"
userId: "[string]User ID"
metadata: "[string]Metadata"
readAcquireActions:
- action: "[string]Type of Acquire Action"
request: "[string]JSON string of the request used when executing the action"
expiresAt: "[long]Expiration datetime"
expiresTimeSpan:
days: "[int]Days"
hours: "[int]Hours"
minutes: "[int]Minutes"
timeOffsetToken: "[string]Time offset token"transaction.service("inbox").acquire.send_message_by_user_id({
namespaceName="[string]Namespace name",
userId="[string]User ID",
metadata="[string]Metadata",
readAcquireActions={
{
action="[string]Type of Acquire Action",
request="[string]JSON string of the request used when executing the action"
}
},
expiresAt="[long]Expiration datetime",
expiresTimeSpan={
days="[int]Days",
hours="[int]Hours",
minutes="[int]Minutes"
},
timeOffsetToken="[string]Time offset token",
})