트랜잭션 설정

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

GS2가 제공하는 마이크로서비스에는 대체로 네임스페이스 설정에 transactionSettingV2라는 필드가 존재합니다. 해당 네임스페이스가 발행하는 트랜잭션을 어떻게 실행할지는 이 설정으로 정해집니다.

TransactionSettingV2는 다음과 같은 구조를 가집니다.

타입활성화 조건필수기본값값 제한설명
distributorNamespaceIdstring
“grn:gs2:{region}:{ownerId}:distributor:default”~ 1024자트랜잭션 실행에 사용하는 GS2-Distributor 네임스페이스
enableParallelExecutionbool
false액션을 직렬이 아닌 병렬로 실행할지 여부

설정하는 항목은 이 2가지뿐입니다. 트랜잭션의 실행 방법에 관한 그 밖의 항목은 권장 구성으로 고정되어 있으므로, 어느 쪽을 선택하더라도 다음 3가지는 항상 성립합니다.

  • 트랜잭션은 발행된 시점에 서버가 실행합니다. 클라이언트가 스탬프 시트를 실행할 필요는 없습니다
  • 성패는 트랜잭션 전체가 하나입니다. 어떤 액션이 실패한 경우에는 그때까지 실행된 액션까지 함께 취소되어 하나도 반영되지 않습니다
  • 트랜잭션을 발행한 API의 응답이 반환되는 시점에 트랜잭션의 실행은 완료되어 있습니다

이 3가지는 트랜잭션을 발행한 API의 사전 스크립트에도 적용됩니다. 예를 들어 상품 구매 시의 스크립트를 설정한 경우, 그 스크립트가 GS2의 API를 호출하여 수행한 데이터 변경이나 스크립트가 발행한 트랜잭션도 상품 구매 API가 성공했을 때 함께 반영됩니다.

필드 설명

distributorNamespaceId

트랜잭션 실행에 사용하는 GS2-Distributor 네임스페이스를 설정합니다. 용도별로 트랜잭션 실행을 나누고 싶은 경우가 아니라면, 프로젝트에 준비되는 default 네임스페이스 그대로도 문제 없습니다.

enableParallelExecution

트랜잭션에 포함된 액션을 한 건씩 실행할지, 한꺼번에 동시에 실행할지를 설정합니다. 실질적으로 선택의 여지가 있는 것은 이 항목뿐입니다.

직렬 실행(기본값 / false)

각 액션은 앞서 실행된 액션의 쓰기를 바탕으로 동작합니다. 액션은 검증・소비・입수 순서로 한 건씩 실행되며, 다음을 할 수 있습니다.

  • 하나의 트랜잭션 안에서 같은 행을 여러 액션에서 갱신한다
  • 앞서 실행된 액션의 쓰기를 읽는다. List나 Query의 결과에서도, 중첩된 트랜잭션 내부에서도 읽을 수 있습니다
  • 트랜잭션을 발행한 API가 같은 요청 안에서 먼저 수행한 갱신을 읽는다. 사전 스크립트에 의한 변경도 여기에 포함되며, 액션의 쓰기와 순서대로 하나의 커밋으로 합류합니다
  • %{Gs2Xxx:ActionName.path[0].field}를 사용해 앞서 실행된 검증・소비 액션의 결과를 후속 검증・소비・입수 액션의 인수로 넘긴다

%{...}로 참조할 수 있는 것은 검증 액션과 소비 액션의 결과뿐이며, 입수 액션의 결과는 참조할 수 없습니다. 같은 이름의 액션이 여러 개 있는 경우 먼저 실행된 것이 우선합니다. 해결할 수 없는 플레이스홀더는 인수 값으로 그대로 남기 때문에, 숫자 필드라면 검증에 실패할 수 있습니다.

그 대가는 다음과 같습니다.

  • 응답 시간은 가장 느린 액션 1건분이 아니라 각 액션의 실행 시간의 합계가 됩니다
  • 하나의 트랜잭션에 포함할 수 있는 액션은 최대 20건입니다. 초과하여 발행하면 발행 시점에 실패합니다
  • 실행은 가장 먼저 실패한 액션에서 중지됩니다. 실행 중이던 페이즈의 결과는 그 실패한 액션까지가 반환되고 이후 페이즈는 비어서 반환됩니다. 병렬 실행이라면 후속 액션에서 관측할 수 있었을 5xx 에러가 관측되지 않게 되므로, 재시도 여부를 판단할 재료는 병렬 실행보다 좁아집니다

각 페이즈 안에서의 실행 순서는 액션 이름, 그다음 대상 리소스로 결정됩니다. 요청에 나열한 순서가 아니므로, 재배열해서 실행 순서를 지정할 수는 없습니다. 또한 소비 액션은 이름과 관계없이 반드시 입수 액션보다 먼저 실행됩니다.

병렬 실행(true)

액션은 동일한 데이터 스냅샷에 대해 병렬로 실행됩니다. 응답 시간은 가장 느린 액션 1건분이면 되고, 액션 수의 상한도 없습니다.

그 대가는 다음과 같습니다.

  • 어떤 액션에서 다른 액션의 쓰기를 읽을 수 없습니다
  • 두 액션이 같은 행을 갱신하면 트랜잭션은 database:transaction:same.resource(400)로 실패합니다
  • %{...}는 앞선 단계(검증 → 소비 → 입수 순)의 결과만 참조할 수 있습니다. 같은 단계 안의 액션에 대한 참조는 해결되지 않고 그대로 남으며, %{...}를 포함하는 단계는 앞선 단계의 완료를 기다린 후 실행되므로 그만큼 응답 시간이 길어집니다

같은 트랜잭션 안에서 두 개 이상의 액션이 같은 데이터를 갱신하지 않는다는 것을 보장할 수 있는 경우에만 활성화하십시오.

양쪽에서 변하지 않는 것

이 설정은 트랜잭션을 발행하는 시점의 동작을 바꾸지 않습니다. 같은 리소스를 대상으로 한 액션이 1건으로 통합되는 것, 조용히 버려지는 것, 에러로 거부되는 것은 직렬 실행에서도 병렬 실행에서도 동일하게 발행 시점에 일어납니다. 바뀌는 것은 액션이 실행에 도달한 이후의 동작뿐입니다. 어떤 액션을 하나의 트랜잭션에 함께 지정해도 되는지는 트랜잭션 액션의 조합을 참조하십시오.

어느 쪽을 선택할지 판단하는 플로차트

graph TD
  Start["transactionSettingV2를 설정"] --> Q3{"두 개 이상의 액션이 같은 데이터를<br/>갱신하지 않는다고 보장할 수 있는가"}
  Q3 -- 보장할 수 없다 --> Sequential["enableParallelExecution = false(기본값)"]
  Q3 -- 보장할 수 있다 --> Q4{"먼저 실행되는 액션의 결과를<br/>후속 액션에서 참조하는가"}
  Q4 -- 같은 단계 안에서 참조한다 --> Sequential
  Q4 -- 참조하지 않는다, 또는<br/>앞선 단계만 참조한다 --> Q1{"액션이 21건 이상이 되거나,<br/>응답 시간을 줄이고 싶은가"}
  Q1 -- 둘 다 아니다 --> Sequential
  Q1 -- 둘 중 하나에 해당한다 --> Parallel["enableParallelExecution = true"]

판단하기 어려운 경우에는 기본값인 직렬 실행 그대로 두십시오. 직렬 실행은 트랜잭션을 구성하는 방식에 대한 제약이 가장 적으며, 병렬 실행에서 실패하는 조합의 대부분이 직렬 실행에서는 성립합니다.

다만 직렬 실행에서는 21건 이상의 액션을 발행할 수 없으므로, 액션 수가 상한을 넘는 데다 같은 데이터로의 쓰기도 피할 수 없는 경우에는 트랜잭션을 분할하십시오.

구 TransactionSetting과의 관계

TransactionSettingV2가 등장하기 전에는 네임스페이스 설정의 transactionSetting으로 트랜잭션의 실행 방법을 지정했습니다. transactionSetting은 비권장입니다. TransactionSettingV2가 존재하지 않던 시기에 생성된 네임스페이스를 위해 남아 있으며, TransactionSettingV2가 설정되어 있지 않은 동안에만 적용됩니다. 새로 생성하는 네임스페이스에서는 사용하지 마십시오.

transactionSetting은 트랜잭션 실행에 관련된 요소――자동 실행(AutoRun), 원자적 실행(AtomicCommit), GS2-Distributor를 이용한 비동기 실행, 스크립트 결과의 일괄 적용, GS2-JobQueue를 통한 입수 액션의 비동기화, 직렬 실행――를 각각 개별 항목으로 공개하고 있기 때문에 권장되지 않는 조합도 만들 수 있습니다. 그 권장 구성을 하나의 설정으로 정리한 것이 TransactionSettingV2입니다.

TransactionSettingV2를 설정하면 transactionSetting의 각 항목은 다음과 같이 고정됩니다.

transactionSetting의 항목TransactionSettingV2에서의 값
enableAutoRuntrue
enableAtomicCommittrue
enableSequentialExecutionenableParallelExecution의 부정
transactionUseDistributorfalse
commitScriptResultInUseDistributorfalse
acquireActionUseJobQueuefalse

따라서 TransactionSettingV2를 사용하는 네임스페이스에서는 다음 3가지를 이용할 수 없습니다.

  • 트랜잭션을 클라이언트 실행 스탬프 시트로 실행한다
  • GS2-Distributor를 통한 AutoRun의 비동기 실행을 수행한다
  • 입수 액션을 GS2-JobQueue로 통합한다

transactionSetting을 계속 사용하는 것은 이러한 동작에 이미 의존하고 있는 네임스페이스에 한정하십시오. 또한 예전에 이러한 설정으로 회피하던 충돌의 대부분은 직렬 실행으로 해소됩니다. 사전 스크립트의 변경과 트랜잭션의 충돌은 같은 요청 안의 갱신이 하나의 커밋으로 합류함으로써, 소비 액션과 입수 액션의 충돌이나 입수 액션끼리의 충돌은 같은 행을 여러 액션에서 갱신할 수 있음으로써 각각 회피할 필요가 없어집니다.


트랜잭션 액션의 조합

하나의 트랜잭션에 어떤 액션을 함께 지정해도 되는지. 각 서비스의 API 레퍼런스에 있는 조합 표를 읽기 위한 전제 설명