Game Engine
What is Transaction Processing?
Transaction processing in GS2 refers to the mass execution of exchanging resources defined as “consume action” and “get action”. APIs that issue transactions are such as GS2-Exchange’s exchange execution function (Exchange), GS2-Showcase’s product purchase function (Buy), and GS2-Quest’s quest start function (Start).
Consumption actions" are user data operations that are disadvantageous to the player, while “acquisition actions” are user data operations that are beneficial to the player.
In GS2-Inventory, the consumption of an item is a “Consume Action” and the acquisition of an item is an “Acquire Action”. For a slightly different example, in GS2-Inbox, setting the open flag on a message is a “consume action” and receiving an item attached to a message is an “acquire action. Understand that in GS2, the game cycle is achieved by repeatedly issuing and executing transactions that rewrite user data.
Transactions and Stamp Sheets
In GS2, transaction processing was called a “stamp sheet. As a result, you will find such descriptions throughout the code and documentation. In recent years, this name is no longer actively used and is referred to as “transaction processing,” but it should be understood to refer to the same thing.
A similar term is “stamp task,” which refers to a “consumption action” contained within a transaction.
Transaction Execution
When you call Exchange in GS2-Exchange or Buy in GS2-Showcase, an object called EzTransactionDomain is returned. EzTransactionDomain has a Wait function, which can be called to wait for the transaction to complete. However, it is strongly recommended to implement a timeout since there is no guarantee of the amount of time it will take for transaction processing to complete.
The Wait function has an argument called all, which can be set to true to wait for the completion of the transaction if a new transaction is issued within the transaction processing.
If the Wait function is not called, Gs2Domain::Dispatch must be called periodically.
Deep Dive
The rest of this section describes the detailed implementation of transaction processing. Most developers do not need to understand what is written below.
The order in which the consume and acquire actions are executed
The order of execution is controlled so that the “get action” can be executed after all the “consume actions” included in the transaction have been executed. Therefore, the order of execution is “consume action” first and “acquire action” second. This is an important specification to achieve cheat tolerance.
This process flow is compared to the “approval process” in which a Japanese company makes a decision, Ask the various executives (microservices) for permission (to perform the consumption action) and have them put their stamp on the request for approval, We called it a stamp sheet because when you get permission from all the microservices, you can execute what you really wanted to do (obtain action).
Execute transaction
The microservices provided by GS2 have the ability to execute a process by passing the “consume action” or “obtain action” contained in a transaction. However, it is difficult to determine which action contained in a transaction should be passed to which API of which microservice, depending on the type of action, and executing it can be a challenge.
This is where GS2-Distributor microservice comes into play. When GS2-Distributor receives a transactional action, it has a process that forwards it to the appropriate microservice API depending on the content of the action. This allows you to simply pass transaction data to GS2-Distributor and execute the transaction without having to think about it.
Error Handling
The next thing to consider is error handling; GS2 provides a variety of microservices, and when a failure occurs, it may occur on a per-microservice basis. In other words, there could be a case where the “consume action” is successfully executed, but the “get action” fails. In this case, the “get action” is not executed successfully until the “consume action” succeeds. In this case, if the player does not retry until the “get action” succeeds, the process will stop with the player losing money.
Automatic Execution of Transaction Processing
In the past, GS2 left error handling to the game developer. However, since it is not appropriate to leave error handling, which is not highly productive, to many GS2 users, the “automatic transaction execution” function was developed to ensure that GS2 takes care of this responsibility.
The “TransactionSetting” item is always included in the namespace settings of microservices that have the ability to issue transactions. In the “TransactionSetting” section, there is a flag called “EnableAutoRun” to enable auto-execution, which is now enabled by default in the configuration using the management console.
With transaction auto-execution enabled, when calling an API that issues a transaction, such as GS2-Showcase’s Buy, the API will respond only with the transaction ID and not the transaction payload. Instead, data is handed off internally to GS2-Distributor to execute the transaction. The GS2-Distributor then executes the received transaction and retries if an error occurs.
Because it operates through this mechanism, processing is usually completed within one second, but there is no guarantee as to how long it will take to complete transaction processing, as retries may occur if an error occurs.
Waiting for auto-executed transactions to complete and retrieve results
When auto-execution of a transaction is enabled, it is unclear whether the transaction is completed or uncompleted as it is. GS2-Distributor therefore has a mechanism to send a notification to the game when the transaction auto-execution is complete.
To use this functionality, the GS2-Distributor must be configured with a GS2-Gateway namespace to issue notifications to, and games must be configured with a user ID to receive notifications for the GS2-Gateway namespace. Projects created after August 2023 will automatically create a GS2-Distributor or GS2-Gateway named default, and will use this namespace for these processes if not specifically configured in the SDK.
The content of the notification includes a “transaction ID”, and GS2-Distributor provides an API to retrieve the results of the transaction execution by specifying the “transaction ID”.
Executing multiple get actions
As explained above, multiple “consume actions” can be set, but only one “acquire action” can be set. This is due to the cheat-resistant backstop of the transaction, which states that the get action can be executed after all spend actions have been executed. On top of this mechanism, it means that having multiple acquisition actions would complicate things in many ways.
However, it is conceivable in various cases that there are multiple acquisition actions to increase or decrease resources in the game. This is where the GS2-JobQueue microservice comes in. GS2-JobQueue provides a mechanism for delayed execution of “acquisition actions. GS2-JobQueue can register up to 10 jobs in a single API call.
There is a get action called “register job to GS2-JobQueue,” and if multiple get actions are set, they are internally converted to this get action when a transaction is issued. If the number of acquisition actions exceeds 10, it is converted to “register job to GS2-JobQueue” and up to 100 acquisition actions can be included in a single transaction. Projects created after August 2023 will now create a GS2-JobQueue named “default” and use this namespace for processing. You can use any queue by specifying the namespace ID in JobQueueNamespaceId in “TransactionSetting,” but there is no need to do so unless there is a special reason.
Executing GS2-JobQueue
GS2-JobQueue execution has the same past as transactions. Execution of jobs registered with GS2-JobQueue also required explicitly calling the execution API for jobs registered with GS2-JobQueue. The job was removed from the job queue upon successful execution, and since the return value of the job’s execution API included in the response value whether the job queue was empty, it was left to the game developer’s responsibility to have the process rotate until it was empty.
If a job failed to execute, the job was not removed from the job queue, so retries could be easily implemented by repeatedly running the job until the queue was empty.
GS2-JobQueue auto-execution
However, since it is not appropriate to leave this unproductive process to many GS2 users, the “automatic job execution” function was designed to ensure that GS2 would take care of this responsibility. The GS2-JobQueue namespace configuration provides an auto-execute flag for the job queue, which is now enabled by default.
As with transactions, when auto-execution is enabled, processing begins automatically when a job is queued, and when execution is complete, GS2-JobQueue notifies the GS2-Gateway set in the GS2-JobQueue namespace of the “completed job ID”. GS2-JobQueue can specify a “job ID” to obtain the results of job execution. Projects created after August 2023 will automatically create a GS2-Gateway named default, and will use this namespace for these processes if not specifically configured in the SDK.
Who is EzTransactionDomain::Wait
Now, I have explained the internal processing at length. Finally, let’s consider again what EzTransactionDomain’s Wait does.
When GS2-Showcase::Buy is called, a transaction is issued, and if auto-execution is enabled, it waits for a completion notification of the transaction ID issued by GS2-Gateway. If auto-execution is disabled, the returned transaction data is passed to GS2-Distributor for execution.
If auto-execution is enabled, the transaction execution result is retrieved from GS2-Distributor; if not auto-execution, the result of explicitly calling GS2-Distributor is used to retrieve the transaction execution result. If there was a transaction issue process involved, it creates a new EzTransactionDomain and calls its EzTransactionDomain::Wait if all is true.
If the “get action” of the executed transaction was a job registration to GS2-JobQueue, there is additional processing. If auto-execution of GS2-JobQueue was enabled, it waits for the execution completion notification of the registered job ID from GS2-Gateway; if auto-execution was disabled, it explicitly executes the job in GS2-JobQueue. If the job execution result includes transaction issuance processing or job registration to GS2-JobQueue, it also waits for those processes to complete if all is true.
Who is Gs2Domain::Dispatch?
When GS2-Distributor’s transaction execution or GS2-JobQueue’s job execution completion notification is received from GS2-Gateway, the SDK’s local cache is rewritten according to the result of the processing. This makes it possible to retrieve the latest value without calling the API for retrieving the quantity of items in the possession of an item as a result of a change in the quantity of items in the possession of an item due to transaction processing.
Since Gs2Domain::Dispatch is called in EzTransactionDomain::Wait when waiting for notification from GS2-Gateway, it is not necessary to explicitly call Gs2Domain::Dispatch when using Wait. However, if you do not call EzTransactionDomain::Wait or if you specify false for the all argument, you must call Gs2Domain::Dispatch otherwise the cache will not reflect the results of execution by transactions or job queues.