# RTGS 3센터 삼중화 — 이관·기동 체크리스트

> 대상: 고사양 PC(40코어/128GB) 이관 후 **테스트 시작**용. 코드(B1/B2/B3)·설정공통화·스크립트는
> 8코어 PC에서 구현·빌드·단위테스트 완료(2026-07-13). 이 문서대로 기동하면 3센터 실측 가능.

## 0. 구현 요약 (이번에 반영된 것)
| 항목 | 내용 | 파일 |
|---|---|---|
| B1 무손실 순번기 | SEQUENCE `global_seq_seq` + `journal_outbox`(durable-before-publish) + 재발행 스케줄러 | `sequencer/SequencerService.kt`, `louisvuitton/.../db/migration/V4__seq_outbox.sql` |
| B1 리더선출 | 순번기 그룹 `sequencer` 단일 리더(Kafka 리밸런스). **전역 단일 인스턴스 = DC1** | 동상 |
| B2 정족수 | `rtgs.result`=applied-ack, `QuorumAggregator`가 센터 집계 → 과반(2/3) ACCC | `prada/QuorumAggregator.kt`, `prada/PradaService.kt` |
| B3 gap 타임아웃 | `GapBuffer` + 스케줄러: 정체 시 phantom skip(단일 파티션 근거) + `rtgs.journal.gap.timeout` 지표 | `hermes/GapBuffer.kt`, `hermes/HermesService.kt` |
| 설정공통화 | 원장/엣지 groupId 센터접미사(`hermes-DC1`…) 파라미터화, 순번기만 단일 그룹 | dior/hermes/prada/chanel Service |
| 이관 스크립트 | `run-dc2/dc3.cmd`, `infra/init-3centers-db.cmd`, `run-dc1.cmd`(3센터 모드 지원) | 루트/infra |

- 단위테스트: `QuorumAggregatorTest`(6), `GapBufferTest`(6) — `gradlew build` 통과.

## 1. 이관 검증 (고사양 PC 도착 직후)
1. `C:\ai-dev` 트리를 동일 경로로 복사(포터블 무설치 철학 — 경로만 같으면 됨).
2. 빌드 확인: `cd C:\ai-dev\workspace\rtgs\backend && gradlew.bat build`
   - JDK21 고정(`gradle.properties`의 `org.gradle.java.home`)이라 별도 설정 불필요.
   - 기대: `BUILD SUCCESSFUL`, 단위테스트 12건 통과.
3. 단일센터 스모크(선택): `run-dc1.cmd`(기본 count=1, db=rtgs) → Chanel :8091로 1건 이체 → ACCC 확인.

## 2. 3센터 기동 절차 (순서 중요)
```
1) 공유 인프라 + DC1(3센터 모드) 기동:
     set CENTER_COUNT=3 & set POSTGRES_DB=rtgs_dc1 & run-dc1.cmd
   → postgres:5433, kafka:9092, 단일 순번기 :8090, DC1 원장/엣지(809x) 기동.

2) 3개 DB 생성(멱등):
     infra\init-3centers-db.cmd
   → rtgs_dc1/dc2/dc3 (스키마는 각 센터 LouisVuitton의 Flyway가 생성).
   ※ DC1은 이미 기동 중이라 rtgs_dc1은 DC1 LouisVuitton이 마이그레이션함.

3) DC2 기동:  run-dc2.cmd   (원장/엣지만, 819x, db=rtgs_dc2)
4) DC3 기동:  run-dc3.cmd   (원장/엣지만, 829x, db=rtgs_dc3)
```
- **순번기는 전역 단일(DC1 :8090)**. DC2/DC3는 저널(rtgs.journal, 단일 파티션)을 **독립 그룹**으로
  전량 재생 → 각자 rtgs_dc2/dc3 원장에 동일 상태 도달(결정론적 재생).
- 완결: 각 센터 Prada가 `rtgs.result`를 전 센터분 집계 → 서로 다른 센터 **2/3** 도달 시 ACCC.

## 3. 검증 항목 (§설계서 6장 대응)
| 시나리오 | 방법 | 기대 |
|---|---|---|
| 평상시 3센터 | Chanel(:8091 등)로 이체 N건 | 3 DB의 transfer/account **완전 일치**(총액·상태·순번), 즉시 과반 ACCC |
| 1센터 강제종료 | DC3 창 닫기 → 이체 계속 | 남은 2센터 ack=2 → 과반 유지 → **완결 지속**, 이중지급 0 |
| 재해센터 복구 | DC3 run-dc3 재기동 | 밀린 저널 따라잡기(earliest 재생) → 잔액 일치 수렴 |
| 2센터 종료 | DC2·DC3 종료 → 이체 | ack=1 → 과반 미달 → **ACSP(완결 보류)**, 오처리 0 |
| 2센터 복구 | DC2·DC3 재기동 | 따라잡기 후 보류분 **ACCC 재개** |
| 순서 gap | (인위적 phantom) | `rtgs.journal.gap.timeout` 증가 + skip 후 진행 재개 |

**대사(정합성) 확인 쿼리** (psql, 각 DB 반복):
```
psql -h localhost -p 5433 -U rtgs -d rtgs_dc1 -c "SELECT status,count(*),sum(amount) FROM transfer GROUP BY status ORDER BY status;"
psql ... -d rtgs_dc2 -c "동일"
psql ... -d rtgs_dc3 -c "동일"
-- 3개 결과가 동일해야 함. account 총합도 세 DB 동일해야 함.
```

## 4. 포트·토폴로지 참조
| 서비스 | DC1 | DC2 | DC3 | 그룹 |
|---|---|---|---|---|
| Sequencer | 8090 | (없음) | (없음) | `sequencer`(단일 리더) |
| Chanel | 8091 | 8191 | 8291 | `chanel-DCx` |
| Dior | 8092 | 8192 | 8292 | `dior-DCx` |
| Hermes | 8093 | 8193 | 8293 | `hermes-DCx` |
| Prada | 8094 | 8194 | 8294 | `prada-DCx` |
| Gucci | 8095 | 8195 | 8295 | (무상태) |
| LouisVuitton | 8099 | 8199 | 8299 | (Flyway/관리) |
- 공유: PostgreSQL 5433(rtgs_dc1/dc2/dc3), Kafka 9092(단일 파티션 저널), Prometheus 9090, Grafana 3000, ELK.

## 5. 주의·후속 (테스트 중 확인)
- **순번기 HA(선택 검증)**: 리더선출 시연하려면 순번기 인스턴스를 추가 기동하되 **반드시 동일
  SEQUENCE DB**(POSTGRES_DB=rtgs_dc1)를 바라보게 할 것. 서로 다른 DB면 순번 충돌.
- **Gucci 콜백 시드**: `institution_endpoint`가 `localhost:8095`(DC1 Gucci)로 시드됨. 센터별
  콜백 왕복까지 볼 땐 DC2/DC3 endpoint URL 조정 필요(핵심 정족수 검증엔 무관).
- **phantom skip 임계**: `rtgs.gap-timeout-ms`(기본 5000) — 실측 부하에 맞게 조정 가능(env/프로퍼티).
- **재발행 grace**: `rtgs.outbox-republish-grace-ms`(기본 3000) — 인라인 발행과의 경합 회피 창.
- 21 JVM 동시기동은 고사양 PC 전제. 8코어에선 경량 스모크만 권장.
