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

# 트랜잭션 설정

Game Server Services의 트랜잭션 설정 설계 방침



GS2가 제공하는 마이크로서비스에는 대체로 네임스페이스 설정에 TransactionSetting이라는 필드가 존재합니다.
TransactionSetting은 다음과 같은 구조를 가집니다.

|  | 타입 | 활성화 조건 | 필수 | 기본값 | 값 제한 | 설명 |
| --- | --- | --- | --- | --- | --- | --- |
| enableAutoRun | bool |  | ✓| false |  | 발행한 트랜잭션을 서버 측에서 자동으로 실행할지 여부|
| enableAtomicCommit | bool | {enableAutoRun} == true | <span style="color: gray; ">✓</span>| false |  | 트랜잭션 실행을 원자적(atomic)으로 커밋할지 여부<br><span style="font-size:90%;">enableAutoRun이 true이면 필수</span>|
| transactionUseDistributor | bool | {enableAtomicCommit} == true | <span style="color: gray; ">✓</span>| false |  | 트랜잭션을 비동기 처리로 실행<br><span style="font-size:90%;">enableAtomicCommit이 true이면 필수</span>|
| commitScriptResultInUseDistributor | bool | {transactionUseDistributor} == true | <span style="color: gray; ">✓</span>| false |  | 스크립트 결과 커밋 처리를 비동기 처리로 실행할지 여부<br><span style="font-size:90%;">transactionUseDistributor가 true이면 필수</span>|
| acquireActionUseJobQueue | bool | {enableAtomicCommit} == true | <span style="color: gray; ">✓</span>| false |  | 입수 액션을 실행할 때 GS2-JobQueue를 사용할지 여부<br><span style="font-size:90%;">enableAtomicCommit이 true이면 필수</span>|
| distributorNamespaceId | string |  | ✓| "grn:gs2:{region}:{ownerId}:distributor:default" |  ~ 1024자 | 트랜잭션 실행에 사용하는 GS2-Distributor 네임스페이스|
| queueNamespaceId | string |  | ✓| "grn:gs2:{region}:{ownerId}:queue:default" |  ~ 1024자 | 트랜잭션 실행에 사용하는 GS2-JobQueue의 네임스페이스|

필드가 많고 그 용도도 복잡하므로 이 문서에서 자세히 설명합니다.

## 필드 해설

### enableAutoRun

발행한 트랜잭션을 자동으로 실행할지를 설정합니다.
여기서 false로 할 이유는 이제 거의 없습니다.

예전에 GS2에는 트랜잭션을 자동으로 실행하는 구조가 없어서, 발행된 트랜잭션을 수동으로 각 마이크로서비스에 요청하여 처리하는 것이 주요한 트랜잭션 실행 방법이었지만, 지금에 와서는 트랜잭션이 도중에 실패했을 때의 오류 처리를 복잡하게 만들 뿐입니다.

### enableAtomicCommit

GS2의 트랜잭션 시스템 역사에서도 비교적 나중에 추가된 기능이 이 AtomicCommit 기능입니다.
AtomicCommit 기능이 없던 시절에는 발행된 트랜잭션이 비동기 처리로 실행되었기 때문에, 예를 들어 GS2-Showcase에서 상품을 구매하는 API를 호출한 시점에는 구매 처리가 완료되지 않았고, 비동기 처리로 실행되는 트랜잭션의 결과가 반환되어야만 비로소 트랜잭션이 성공했는지 실패했는지를 알 수 있었습니다.
이러한 설계의 문제는 트랜잭션 실행 과정에서 오류가 발생했을 때 나타났습니다.

소비 액션에 성공한 후, 입수 액션 실행에 실패한 경우, 그것이 재시도할 가치가 있는 서버 오류였다면 자동으로 재시도되어 트랜잭션을 완료하도록 서버 측에서 노력하는 것이 자동 실행의 구조입니다.
하지만 입수 액션이 소지 수량 초과였거나, 복수 설정된 소비 액션 중 하나가 리소스 부족이었던 경우처럼 재시도해도 의미가 없는 상황이 되면 그 시점에서 트랜잭션은 실패하지만, 이미 성공한 소비 액션은 실행된 채로 남아 있는 경우가 있었습니다.

이러한 문제를 해결하기 위해 마련된 것이 AtomicCommit으로, GS2-Showcase의 상품 구매 API 응답을 반환하기 전에 소비 액션과 입수 액션을 실행하고, 모두 성공하면 결과를 데이터베이스에 반영하는 동작을 하게 됩니다.
이 설정을 통해 트랜잭션이 어중간한 상태로 실행되어 버리는 문제를 회피할 수 있습니다.
그렇기 때문에 새롭게 GS2를 이용한 게임을 개발할 때는 AtomicCommit을 활성화하여 개발을 진행할 것을 강력히 권장합니다.

다만 AtomicCommit은 만능이 아니며, 소비 액션과 입수 액션을 동시에 실행하기 때문에 액션 내에서 동일한 리소스에 대해 갱신 처리를 시도하면 오류가 발생합니다.
그러한 트랜잭션을 실행할 때는 AtomicCommit을 활성화할 수 없다는 점에 주의가 필요합니다.

또한 AtomicCommit을 활성화하면, 그 효력은 트랜잭션 실행뿐만 아니라 해당 API에서 실행되는 사전 스크립트에도 적용됩니다.
예를 들어 상품 구매 시의 스크립트를 설정한 경우, 그 스크립트에서 GS2의 API를 호출하여 데이터를 다시 쓰는 처리나, 스크립트가 발행한 트랜잭션도 GS2-Showcase의 상품 구매 API가 성공했을 때 결과가 반영되게 됩니다.

### transactionUseDistributor

enableAtomicCommit을 활성화한 상태에서 이용할 수 있는 옵션입니다.
트랜잭션 실행을 비동기 처리로 수행하도록 하는 설정입니다. 이 설정을 하면 소비 액션과 입수 액션은 AtomicCommit으로 처리되지만, 그 완료를 기다리지 않고 GS2-Showcase의 상품 구매 API는 응답을 반환합니다.
트랜잭션 결과를 기다릴 필요가 없을 때 활성화하거나, 상품 구매 시 스크립트에서 다시 쓰는 내용과 트랜잭션에서 다시 쓰는 내용이 충돌하는 경우에 활성화하는 용도를 상정하고 있습니다.

### commitScriptResultInUseDistributor

transactionUseDistributor를 활성화했을 때 이용할 수 있는 옵션입니다.
사전 스크립트에 의해 다시 쓰이는 처리도, 비동기 처리로 실행되는 소비 액션과 입수 액션의 트랜잭션에 포함시킬 수 있습니다.
이를 통해 스크립트의 실행 결과만 반영되는 것을 방지하면서, 트랜잭션 실행을 비동기 처리로 만드는 것이 가능해집니다.

### acquireActionUseJobQueue

enableAtomicCommit을 활성화한 상태에서 이용할 수 있는 옵션입니다.
발행하는 트랜잭션의 입수 액션을 GS2-JobQueue에 대한 잡 등록으로 전환하여, 비동기 처리로 입수 액션을 실행하도록 설정할 수 있습니다.
이 설정을 통해 소비 액션과 입수 액션이 경합하는 경우나, 복수 설정된 입수 액션 사이에 경합이 발생하는 경우의 문제를 회피할 수 있습니다.

### distributorNamespaceId

트랜잭션을 자동으로 실행할 때 사용하는 GS2-Distributor의 네임스페이스를 설정합니다.

### queueNamespaceId

트랜잭션의 입수 액션을 잡 큐를 경유하여 실행할 때 사용하는 GS2-JobQueue의 네임스페이스를 설정합니다.

## 어떤 트랜잭션 시스템을 이용해야 하는지 판단하는 흐름도

```mermaid
graph TD
  Start --> 1st["enableAutoRun = true, enableAtomicCommit = true"]
  1st --> Async{"게임에서 트랜잭션 실행 결과를<br/>사용할 필요가 있는가"}
  Async -- 기다릴 필요가 없음 --> UseDistributor2["transactionUseDistributor = true"]
  Async -- 기다릴 필요가 있음 --> Conflict{"경합 오류가 발생하는가"}
  Conflict -- 경합하지 않음 --> End
  Conflict -- 경합함--> 2nd{"경합하고 있는 내용을 조사"}
  2nd -- 경합하고 있는 것이<br/>스크립트와<br/>트랜잭션 --> UseDistributor["transactionUseDistributor = true"]
  2nd -- 트랜잭션의<br/>소비 액션끼리 --> NotAtomicCommit["enableAtomicCommit = false"]
  2nd -- 트랜잭션의<br/>소비 액션과<br/>입수 액션 ----> UseJobQueue["acquireActionUseJobQueue = true"]
  2nd -- 트랜잭션의<br/>입수 액션끼리 --> UseJobQueue
  UseDistributor2 --> 3rd{"사전 스크립트를<br/>설정하고 있는가"}
  3rd -- 설정하지 않음 --> Conflict
  3rd -- 설정함 --> UseScript{"스크립트의 결과도 트랜잭션과<br/>함께 반영되기를 원하는가"}
  UseScript -- 하지 않아도 됨 --> Conflict
  UseScript -- 원함 --> CommitScriptResultInUseDistributor["commitScriptResultInUseDistributor = true"]
  CommitScriptResultInUseDistributor --> Conflict
  NotAtomicCommit --> End
  UseJobQueue --> End
```




