6.1 KiB
런타임 아키텍처
프로세스
| 프로세스 | 시작점 | 책임 |
|---|---|---|
| gateway API | app/gateway-api/src/server.ts |
계정, session, profile, admin operation |
| gateway orchestrator | app/gateway-api/src/orchestrator/orchestratorServer.ts |
DB queue, build, PM2 reconciliation |
| game API | app/game-api/src/server.ts |
profile tRPC, SSE, worker transport |
| turn daemon | app/game-engine/src/turn/cli.ts |
schedule, command, 월간 lifecycle, DB flush |
| battle worker | app/game-api/src/battleSim/worker.ts |
격리된 전투 시뮬레이션 |
| auction worker | app/game-api/src/auction/worker.ts |
경매 timer와 낙찰 처리 |
| tournament worker | app/game-api/src/tournament/worker.ts |
대회 진행과 결과 처리 |
Gateway API와 game API는 기본적으로 0.0.0.0에 bind합니다. 실제 port와
prefix는 환경 변수와 배포 profile이 결정합니다.
Gateway 실행
resolveGatewayApiConfigFromEnv()가 PostgreSQL schema, Redis prefix, session
TTL, game-token secret, OAuth, local-account 정책과 orchestrator 설정을
검증합니다.
Gateway API는 다음 저장 경계를 사용합니다.
AppUser,SystemSetting: 계정과 정책GatewayProfile: profile, scenario, port, 상태와 build 결과GatewayOperation: build/reset/open/close 등 실행 요청과 결과- Redis: gateway session, OAuth 임시 상태, flush channel
Orchestrator는 GatewayOperation을 claim하고 source ref를 commit으로
해결합니다. WorkspaceManager가 commit별 worktree를 준비하고 build runner가
artifact를 만들며 Pm2ProcessManager가 profile process를 조정합니다.
재시작 시 DB 상태와 process 상태를 reconciliation합니다.
Game API 실행
resolveGameApiConfigFromEnv()가 PROFILE, SCENARIO,
GAME_PROFILE_NAME, API·SSE·upload 경로와 worker timeout을 결정합니다.
기본 profile name은 ${profile}:${scenario}입니다.
Fastify server는 요청에서 game token을 검증해 GameApiContext.auth에
넣습니다. Router는 public procedure와 인증 procedure를 구분하고, 변경
대상 장수·국가·archive owner를 session actor와 DB에서 해석합니다.
Mutation은 두 형태입니다.
- API transaction으로 끝나는 mutation은
executeInputEvent()가target=APIevent를 만들고 결과와 event 상태를 같은 transaction에서 commit합니다. - turn world가 필요한 mutation은 daemon transport가
target=DAEMONevent를 만들고 turn daemon의 처리 대상으로 전달합니다.
같은 requestId의 완료·처리 중 event는 중복 수락하지 않습니다. 실패 event는
claim 가능한 상태에서 attempts를 증가시켜 재처리합니다.
Turn daemon 조립
createTurnDaemonRuntime()은 다음 순서로 런타임을 구성합니다.
DatabaseTurnDaemonLease가 profile lease와 fencing token을 확보합니다.loadTurnWorldFromDatabase()가 world, 장수, 국가, 도시, 외교, 예약 턴, event와 resource snapshot을 읽습니다.- scenario, map, unit set과 command profile을 적재합니다.
- action-module bundle, AI, command registry, 월간 event action을 조립합니다.
EngineStateManager가 in-memory mutation과 transaction flush를 묶습니다.TurnDaemonLifecycle이 control queue와 schedule을 실행합니다.
Lifecycle은 가장 빠른 장수 턴과 다음 tick 중 앞선 시각을 선택합니다. pause gate, 수동 run, shutdown과 budget을 같은 loop에서 처리합니다.
한 번의 실행과 저장
lease/fencing 확인
-> input_event claim
-> in-memory command 또는 calendar action
-> world dirty state와 side effect 수집
-> EngineStateManager transaction
-> world/general/nation/city/turn/log flush
-> input_event result/status 갱신
-> checkpoint 갱신
-> commit
-> realtime 알림
Transaction 실패 시 in-memory snapshot을 복원하고 event는 재시도 가능한 실패 상태로 남깁니다. Lease 소유권을 잃은 daemon은 flush를 확정하지 않습니다.
예약 턴은 revision/CAS와 lease를 사용합니다. API의 편집과 daemon의 실행이 경합해도 오래된 revision이 새 queue를 덮어쓰지 않게 합니다.
월간 경계
Calendar handler는 turn time이 월 경계를 지날 때 scenario event table의
action을 순서대로 실행합니다. Core event, 수입, 국가 등급, NPC, 이민족,
도시 공급, 전쟁 수입, 특기, 유니크·유산, 베팅, 통일·연감 처리는
app/game-engine/src/turn/monthly*.ts, calendarHandlers.ts,
unificationHandler.ts, yearbookHandler.ts에서 조립됩니다.
Action 순서, RNG 소비와 persistence 순서는 ref 호환 계약입니다. 새 handler는 event catalog, in-memory state, dirty marking, flush와 reload 검증까지 연결합니다.
장애 경계
- PostgreSQL은 gameplay mutation과 daemon 소유권의 기준입니다.
- Redis pub/sub, SSE와 flush notification은 commit 이후 best-effort입니다.
- Worker timeout은 요청 실패로 반환하며 DB commit 여부를 별도로 확인합니다.
- API·daemon process 재시작은
InputEvent, lease, checkpoint와 operation 상태에서 이어집니다. - 운영 process와 외부 Caddy 상태는 local build·mock E2E로 증명되지 않습니다.
Build와 배포
Gateway operation은 source commit, worktree, build artifact와 process를
연결합니다. tools/build-scripts/build-server.mjs는 profile resource 복사만
담당합니다.
외부 공개 경로는 /gateway/, /che/, /hwe/입니다. frontend base,
tRPC, SSE, upload와 direct navigation은 해당 prefix를 유지합니다.
/image/*는 Caddy의 별도 파일 시스템 경로입니다.