feat(multi-instance): dcy-009 BAT-013 — RabbitMQ 다중 인스턴스 토폴로지 (REQ-048~051)

dcy-009(다중 인스턴스 RabbitMQ 토폴로지) 전체 SDLC: draft-req → confirm-req →
draft-batch(BAT-013) → run-batch-delivery → confirm-req-implemented.

- REQ-048 (Configuration): DaemonInstanceConfig.from_toml + DaemonConfig.from_config
  (env 병합, portfolio_hub_token 주입) + 데몬 --config 필수 argparse + .gitignore 인스턴스
  TOML + config/instance.example.toml
- REQ-049 (Interface): topology 동적 큐명 빌더 intent/snapshot/control_queue(instance_key)
  — default=리터럴 큐명(하위호환), named=`{base}.{instance_key}` + daemon per-instance 구독
- REQ-050 (Integration): trex.direct(topic)/trex.broadcast(fanout) exchange +
  declare_topology(instance_key) per-instance binding + control dual binding + direct_routing_key
- REQ-051 (Documentation): interface-contract/runtime-flows/project-structure 정합

하위호환: instance_key="default" → 기존 리터럴 큐명·RPC 경로 무회귀(FR-5).
split-brain 방지(NFR-3): 인스턴스 전용 큐. 민감값(NFR-4): TOML + gitignore.

검증: 신규 9 단위 + RabbitMQ 라우팅 2, 단위 271 passed,
인프라 게이트 301 passed(directed 격리 S-2 + broadcast dual S-3 + default 무회귀 S-4),
ruff clean. REQ-048~051 Implemented, BAT-013 release-candidate.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-07 15:10:36 +09:00
co-authored by Claude Opus 4.8
parent 7e23648489
commit 9f83c64a40
24 changed files with 1223 additions and 85 deletions
@@ -1,13 +1,13 @@
# 0. 문서 상태
- 상태: draft
- 상태: confirmed
- 문서 ID: dcy-009_20260607_multi-instance-rabbitmq-topology
- 이슈명: 인스턴스별 독립 RabbitMQ 채널 토폴로지 — trex-core 다중 인스턴스 운영을 위한 Topic Exchange + Fanout Exchange per-instance 큐 분리
- 작성 시각(KST): 2026-06-07
- 마지막 갱신 시각(KST): 2026-06-07 (instance_id → instance_key, TOML config 설계 반영)
- 마지막 갱신 시각(KST): 2026-06-07 (draft-req: REQ-048~051 생성)
- 대체됨: 없음
- 후속 Discovery 참조: 없음
- 생성된 REQ 참조: 없음
- 생성된 REQ 참조: REQ-048, REQ-049, REQ-050, REQ-051
# 1. 계획 상태 요약
@@ -45,7 +45,7 @@
- trex-core 데몬 운영자: `--config {path}.toml` CLI 인자로 인스턴스별 설정 파일 지정 + 큐 모니터링
- 제약사항
- pika `BlockingConnection`은 thread-safe하지 않다 — 현재 consumer 스레드별 독립 Connection 패턴은 변경 없이 유지해야 한다.
- 기존 durable 큐(`trex.intent.req` 등)가 이미 RabbitMQ 서버에 선언되어 있을 수 있다 → 새 큐명으로 전환 시 old 큐 처리 절차가 필요하다(OQ-1).
- 기존 durable 큐(`trex.intent.req` 등)가 이미 RabbitMQ 서버에 선언되어 있을 수 있다 → 새 큐명으로 전환 후 자연 소멸을 허용하며, RabbitMQ admin console에서 적절한 시점에 수동 삭제한다(OQ-1 Answered).
- strategy 모듈(orb-v1)이 routing key 변경에 맞게 publish 코드를 업데이트해야 한다 — 인터페이스 계약 정의가 선행되어야 한다.
- `portfolio_hub_token`과 `instance_key`는 `.env`에 넣지 않는다 — 기존 `config.py`의 `from_env()` 패턴 단독으로는 이 원칙을 지킬 수 없으므로 TOML 로더가 필수다.
@@ -77,14 +77,14 @@
- `docs/interface-contract.md`: routing key 계약 갱신 — `{instance_key}.{channel_type}` 규칙 추가
- 운영 영향:
- 인스턴스별 TOML 파일 생성 및 관리 필요(`instance_key`, `portfolio_hub_token` 포함)
- 기존 `trex.intent.req` 등 old 큐가 RabbitMQ에 잔류할 경우 배포 절차에 old 큐 정리 단계 필요(OQ-1)
- 기존 `trex.intent.req` 등 old 큐는 새 큐 전환 후 자연 소멸을 허용한다. RabbitMQ admin console에서 적절한 시점에 수동 삭제(OQ-1 Answered).
- `TREX_PORTFOLIO_HUB_TOKEN` env var는 TOML로 이관 후 `.env`에서 제거 필요
# 5. 요구사항 목록
## 기능 요구사항
- FR-1: 데몬 기동 시 `--config <path>.toml` CLI 인자를 지원한다. TOML 파일에서 최소 `instance_key`와 `portfolio_hub_token`을 읽는다. 이 두 값은 `.env`에서 읽지 않는다.
- FR-1: 데몬 기동 시 `--config <path>.toml` CLI 인자는 필수다. 인자 없이 기동하면 오류 메시지를 출력하고 즉시 종료한다. TOML 파일에서 최소 `instance_key`와 `portfolio_hub_token`을 읽는다. 이 두 값은 `.env`에서 읽지 않는다.
- FR-2: `instance_key` 값을 기반으로 모든 수신 큐명을 `{base}.{instance_key}` 형식으로 suffix화한다.
- FR-3: `trex.direct`(type: topic, durable) exchange를 선언하고, 각 인스턴스의 intent/control/snapshot 큐를 routing key `{instance_key}.intent`, `{instance_key}.control`, `{instance_key}.snapshot`으로 바인딩한다.
- FR-4: `trex.broadcast`(type: fanout, durable) exchange를 선언하고, 각 인스턴스의 control 큐를 이 exchange에 추가 바인딩(dual binding)한다. 전체 인스턴스 대상 제어 명령은 이 exchange에 publish한다.
@@ -124,7 +124,7 @@
- R-1 (Medium): strategy 모듈이 routing key를 변경하지 않으면 다중 인스턴스 환경에서 메시지가 올바른 인스턴스에 도달하지 않는다.
- 완화 방안: 인터페이스 계약에 routing key 규칙을 명시한다. `instance_key = "default"` 단일 인스턴스 시에는 routing key `default.intent` 등으로 publish하면 하위 호환 유지 가능.
- R-2 (Medium): 기존 durable 큐(`trex.intent.req` 등)가 RabbitMQ에 잔류하면 새 큐로 전환 후 old 큐에 consumer가 없어진다.
- 완화 방안: 배포 절차에 old 큐 삭제 단계를 명시한다(OQ-1). old 큐에 새로 publish되는 메시지는 없으므로 즉시 삭제해도 메시지 손실 없음.
- 완화 방안: 자연 소멸 허용. 배포 후 old 큐에 새로 publish되는 메시지가 없으므로 메시지 누적 없음. 적절한 시점에 RabbitMQ admin console에서 수동 삭제(OQ-1 Answered).
- R-3 (Low): `trex.broadcast` exchange 메시지가 모든 인스턴스에 동시 전달되므로 인스턴스별로 동일 제어 명령을 중복 처리한다.
- 완화 방안: kill-switch/pause 핸들러에 idempotency 보장(REQ-036 패턴, DB advisory lock). 중복 처리는 무해하게 동작.
- R-4 (Low): TOML 설정 파일이 실수로 git에 커밋되면 `portfolio_hub_token` 등 민감값이 노출된다.
@@ -136,18 +136,18 @@
- A-2: strategy 모듈(orb-v1)은 인터페이스 계약 변경에 맞춰 routing key를 업데이트할 수 있다.
- A-3: 인스턴스별 담당 portfolio 매핑은 TOML 설정 또는 strategy 모듈의 routing 설정으로 관리한다. trex-core 내부에 레지스트리 DB를 두지 않는다.
- A-4: `portfolio_hub_token`과 `instance_key`를 `.env`에서 TOML로 이관하면 기존 `.env` 기반 배포 스크립트를 수정해야 한다. KIS 자격증명 등 나머지 env var는 이번 범위에서 이관하지 않는다.
- A-5: TOML 파일 경로는 `--config` CLI 인자로 지정한다. 경로 지정 없이 기동하는 경우 동작에 대한 처리 방침은 OQ-2에서 결정한다.
- A-5: TOML 파일 경로는 `--config` CLI 인자로 지정한다. `--config` 인자는 필수이며, 지정하지 않으면 데몬이 오류 메시지를 출력하고 즉시 종료한다(OQ-2 Answered). 기본값 fallback은 제공하지 않는다.
## 오픈 질문
- OQ-1: 기존 durable 큐(`trex.intent.req` 등) 처리 방식 — 자연 소멸(수동 삭제) vs 배포 절차에 명시적 삭제 포함?
- 상태: Open
- 처리 방안: REQ 단계에서 배포 절차로 결정. old 큐에 publisher가 없어지면 메시지 누적은 없으나 RabbitMQ admin console에 불필요한 큐가 잔류한다.
- 종료 조건: REQ 초안 작성 시 배포 절차 섹션에 old 큐 처리 방식 명시.
- 상태: Answered
- 결정: 자연 소멸 허용. 배포 후 old 큐에 새로 publish되는 메시지는 없으므로 메시지 누적 없음. RabbitMQ admin console에서 적절한 시점에 수동 삭제. 배포 절차에 명시적 삭제 단계를 추가하지 않는다.
- 반영 위치: R-2 완화 방안
- OQ-2: `--config` 인자 없이 데몬 기동 시 동작 — 오류 종료(필수) vs `instance_key = "default"` 기본값으로 기동?
- 상태: Open
- 처리 방안: REQ 단계에서 결정. 다중 인스턴스 안전성을 위해 TOML 필수(오류 종료)를 권장하나, 기존 배포와의 호환성을 위해 기본값 허용도 고려 가능.
- 종료 조건: confirm-discovery 완료 전 Justin Ko 결정.
- 상태: Answered
- 결정: `--config` 필수. 인자 없이 기동 시 오류 메시지 출력 후 즉시 종료. 기본값 fallback 없음. 인스턴스 식별이 보장되지 않은 상태로 기동하는 것은 다중 인스턴스 환경에서 split-brain 위험을 내포하므로 명시적 실패가 안전하다.
- 반영 위치: A-5, FR-1
### 오픈 질문 처리 기준
@@ -172,16 +172,16 @@
# 8. REQ로 넘기기 전 확인 체크
- [ ] 범위(In Scope/Out of Scope) 확정
- 기준: OQ-1(old 큐 처리), OQ-2(TOML 필수 여부) Answered
- [ ] 성공 지표의 측정 방식/데이터 출처 확정
- 기준: S-1~S-5 각각 테스트 또는 e2e 근거 확정
- [ ] High 영향 리스크 우선순위 및 해소 책임자 지정
- 기준: R-1(routing key contract), R-2(old 큐), R-4(TOML git 노출) 완화 방안 REQ 단계 인계 여부 확인
- [ ] 모든 오픈 질문(OQ) `Open` 상태 없음 확인
- 기준: OQ-1, OQ-2 결정 완료
- [ ] Freeze 확정 담당자(CONFIRMED_BY) 지정
- 기준: Justin Ko 확인
- [x] 범위(In Scope/Out of Scope) 확정
- 기준: OQ-1(old 큐 처리) Answered, OQ-2(TOML 필수) Answered
- [x] 성공 지표의 측정 방식/데이터 출처 확정
- 기준: S-1~S-5 각각 테스트 또는 e2e 근거 확정 — S-1~S-5 측정 방식 및 데이터 출처 섹션 7에 전부 기입됨
- [x] High 영향 리스크 우선순위 및 해소 책임자 지정
- 기준: R-1~R-4 전부 Medium/Low — High 영향 리스크 없음. 모든 리스크에 완화 방안 기입됨. REQ 단계 인계 확정.
- [x] 모든 오픈 질문(OQ) `Open` 상태 없음 확인
- 기준: OQ-1, OQ-2 모두 Answered
- [x] Freeze 확정 담당자(CONFIRMED_BY) 지정
- 기준: Justin Ko 확인 — Section 11 Confirmed By: Justin Ko
# 9. 파일 처리 결과
@@ -198,20 +198,15 @@
# 10. 사용자 결정 필요 항목 요약
## DECIDE
- {{DECIDE_OLD_QUEUE_MIGRATION}}: 기존 durable 큐(`trex.intent.req` 등) 처리 방식 — 자연 소멸(수동 삭제) vs 배포 절차에 명시적 삭제 포함 (OQ-1)
- {{DECIDE_TOML_REQUIRED}}: `--config` 인자 없이 기동 시 동작 — 오류 종료(TOML 필수) vs `instance_key = "default"` 기본값으로 기동 허용 (OQ-2)
## CONFIRM
- {{CONFIRM_STRATEGY_MODULE_OUT_OF_SCOPE}}: strategy 모듈(orb-v1)의 routing key 변경은 이번 Discovery 범위 밖(trex-core는 계약 정의만, 구현은 strategy 모듈 담당)인지 확인
- {{CONFIRM_ENV_RESIDUAL}}: 나머지 env var(KIS 자격증명, PostgreSQL DSN 등)는 이번 범위에서 TOML로 이관하지 않고 `.env`에 유지하는 것으로 확정하는지 확인
- CONFIRM_STRATEGY_MODULE_OUT_OF_SCOPE: 확인됨. strategy 모듈(orb-v1)의 routing key 구현 변경은 이번 Discovery 범위 밖. trex-core는 exchange·바인딩 계약만 정의하며, strategy 모듈의 publish 코드 변경은 별도 Discovery 또는 REQ에서 다룬다(Section 5 Out of Scope 반영).
- CONFIRM_ENV_RESIDUAL: 확인됨. KIS 자격증명(`TREX_KIS_*`), PostgreSQL DSN, TREX_RABBITMQ_URL 등 기존 env var는 이번 범위에서 `.env` 유지. TOML 이관 대상은 `portfolio_hub_token`과 `instance_key`만(Section 5 Out of Scope + A-4 반영).
## DATA
- {{DATA_TOML_SCHEMA_FULL}}: TOML 파일에 포함할 전체 필드 목록(`instance_key`, `portfolio_hub_token` 외 추가 필드 유무) — REQ 단계 스키마 정의에 필요
- {{DATA_MULTI_INSTANCE_TRIGGER_TIMING}}: 실제 다중 인스턴스 운영이 필요한 시점(배포 계획) — NFR 우선순위 조정에 필요
- DATA_TOML_SCHEMA_FULL: REQ 단계 확정. 최소 필드: `instance_key` (string), `portfolio_hub_token` (string). 추가 필드는 REQ 단계 스키마 정의에서 확정한다.
- DATA_MULTI_INSTANCE_TRIGGER_TIMING: REQ/배포 단계 계측 항목. Discovery 단계 범위 밖. 실제 다중 인스턴스 운영 계획은 배포 시점 REQ에서 반영한다.
# 11. Discovery Freeze
@@ -225,8 +220,8 @@
## Freeze 플래그
- Handoff Decision: 보류
- Handoff Rationale: TOML 설정 파일 도입 + instance_key 기반 토폴로지 설계 완료. OQ-1(old 큐 처리), OQ-2(TOML 필수 여부) 미결 — confirm-discovery에서 결정 후 REQ 초안 작성 가능.
- Ready for REQ Drafting: false
- Confirmed By: TBD
- Confirmed At (KST): TBD
- Handoff Decision: REQ Drafting 진행 가능
- Handoff Rationale: TOML 설정 파일 도입 + instance_key 기반 토폴로지 설계 완료. OQ-1(old 큐 자연 소멸), OQ-2(TOML 필수) 모두 Answered. CONFIRM/DATA 항목은 REQ 단계에서 확정 가능 — confirm-discovery 후 REQ 초안 작성 진행 가능.
- Ready for REQ Drafting: true
- Confirmed By: Justin Ko
- Confirmed At (KST): 2026-06-07
+1 -1
View File
@@ -14,4 +14,4 @@
| dcy-006_20260605_strategy-module-rabbitmq-transport | 20260605 | trex-core ↔ 전략 모듈 통신 transport — RabbitMQ 도입 (스냅샷 read + intent 제출 + 이벤트) | [Discovery](./dcy-006_20260605_strategy-module-rabbitmq-transport.md) | confirmed |
| dcy-007_20260606_core-autonomous-safety-sell | 20260606 | core 자율 안전 매도 — stop-loss·시간 만기 자동 트리거 | [Discovery](./dcy-007_20260606_core-autonomous-safety-sell.md) | confirmed |
| dcy-008_20260607_emergency-stop-kill-switch | 20260607 | 비상 정지/강제 청산(kill-switch) — portfolio/전역 슬롯 청산 + 당일 전략 제외 | [Discovery](./dcy-008_20260607_emergency-stop-kill-switch.md) | confirmed |
| dcy-009_20260607_multi-instance-rabbitmq-topology | 20260607 | 인스턴스별 독립 RabbitMQ 채널 토폴로지 — trex-core 다중 인스턴스 운영을 위한 Topic Exchange + Fanout Exchange per-instance 큐 분리 | [Discovery](./dcy-009_20260607_multi-instance-rabbitmq-topology.md) | draft |
| dcy-009_20260607_multi-instance-rabbitmq-topology | 20260607 | 인스턴스별 독립 RabbitMQ 채널 토폴로지 — trex-core 다중 인스턴스 운영을 위한 Topic Exchange + Fanout Exchange per-instance 큐 분리 | [Discovery](./dcy-009_20260607_multi-instance-rabbitmq-topology.md) | confirmed |