From bc79f60274f73a9b6944e296099964ec065efae8 Mon Sep 17 00:00:00 2001 From: Justin Ko Date: Fri, 19 Jun 2026 21:21:19 +0900 Subject: [PATCH] =?UTF-8?q?docs(dcy-031):=20OPS-005=20=EC=9D=B4=EB=B2=A4?= =?UTF-8?q?=ED=8A=B8=20=EB=B0=9C=ED=96=89=20=EC=8B=A0=EB=A2=B0=EC=84=B1=20?= =?UTF-8?q?Discovery=20+=20REQ-091~094?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 운영팀 OPS-005(이벤트 발행 실패) 코드검증 → dcy-031 confirm-discovery. 스코프 B(재시도/강제재연결)+A(idle keepalive)+D(DLQ 가시성/emit 경보). REQ-091~094 draft-req → confirm-req 완료(4건 Approved). M2 트랙B. Co-Authored-By: Claude Opus 4.8 Claude-Session: https://claude.ai/code/session_01VHtM5Ws1HKCKQ4JGi6GRb5 --- ..._event-publisher-connection-reliability.md | 182 ++++++++++++++++++ docs/discovery/index.md | 1 + ...ublisher-reliability-baseline-alignment.md | 101 ++++++++++ ...q-091_publish-retry-and-force-reconnect.md | 105 ++++++++++ .../req-092_publish-idle-keepalive.md | 102 ++++++++++ ...093_dlq-visibility-and-emit-error-alert.md | 104 ++++++++++ docs/srs/index.md | 9 +- 7 files changed, 603 insertions(+), 1 deletion(-) create mode 100644 docs/discovery/dcy-031_20260619_event-publisher-connection-reliability.md create mode 100644 docs/srs/Documentation/req-094_event-publisher-reliability-baseline-alignment.md create mode 100644 docs/srs/Exception/req-091_publish-retry-and-force-reconnect.md create mode 100644 docs/srs/Exception/req-092_publish-idle-keepalive.md create mode 100644 docs/srs/Exception/req-093_dlq-visibility-and-emit-error-alert.md diff --git a/docs/discovery/dcy-031_20260619_event-publisher-connection-reliability.md b/docs/discovery/dcy-031_20260619_event-publisher-connection-reliability.md new file mode 100644 index 0000000..b950c96 --- /dev/null +++ b/docs/discovery/dcy-031_20260619_event-publisher-connection-reliability.md @@ -0,0 +1,182 @@ +# 0. 문서 상태 +이 문서의 현재 상태와 식별 정보를 기록한다. 문서 추적과 승인 흐름에 필요한 최소 메타데이터를 적는다. + +- 상태: confirmed +- 문서 ID: dcy-031_20260619_event-publisher-connection-reliability +- 이슈명: 이벤트 발행 연결 신뢰성 — idle heartbeat 사망 후 첫 발행 유실 + DLQ 가시성 (OPS-005) +- 작성 시각(KST): 2026-06-19 23:00 +- 마지막 갱신 시각(KST): 2026-06-19 23:50 +- 대체됨: 없음 +- 후속 Discovery 참조: 없음 +- 생성된 REQ 참조: REQ-091, REQ-092, REQ-093, REQ-094 + +# 1. 계획 상태 요약 +현재 Discovery가 어떤 배경에서 작성되고 있는지, 연계 문서가 있는지, 지금 시점의 해석이 무엇인지 짧게 요약한다. + +- 연계 문서 현황: + - 관련 점수 파일: 미생성 + - 관련 TODO/메모: ROADMAP.md(M2 트랙B), memory/trex-backlog.md(Task #16) +- 해석: + - 운영팀 OPS-005 리포트(trex-core RabbitMQ 발행 실패)를 코드로 검증한 결과, 발행 전용 연결의 idle heartbeat 사망 후 첫 발행이 재시도·DLQ 없이 유실되는 실결함을 확인했다. + - M2(매도전략 tsl 라이브, 6/28 목표)에서 슬롯 전이 이벤트 유실은 매도 미발동=실손실로 직결되므로, tsl 라이브 선행 조건으로 발행 신뢰성을 보강한다. + +# 2. 요구사항 판정 결과 +이번 요청이 기존 요구사항과 비교해 신규인지, 유사한지, 중복인지 판단하고 그 근거를 남긴다. + +- 판정: 신규 +- 근거 + - 기존 dcy-006(RabbitMQ transport 도입)·dcy-009(멀티 인스턴스 토폴로지)는 전송 계약 수립이 주제였고, 발행 연결의 idle 수명/재시도 신뢰성은 다루지 않았다. + - OPS-005는 운영 환류로 발견된 신규 신뢰성 결함이며 독립 주기로 추적하는 것이 명확하다. + +# 3. 문제점의 요약 +문제의 현재 상태, 기대 상태, 관련 이해관계자, 제약사항을 정리한다. + +- 현재 상태 + - 발행자는 단일 공유 인스턴스다(`runtime/daemon.py:206` `self.event_publisher = _build_transport`). 슬롯 전이 이벤트는 `_make_event_emitter`(`daemon.py:238-248`)가 `publish_event`로 topic exchange `trex.events`에 발행한다. + - 발행 전용 연결은 publish 사이에 `process_data_events`를 돌리지 않는다(consumer 루프와 달리 — `rabbitmq.py:337/379`는 0.2초마다 호출). `params.heartbeat = 30`(`rabbitmq.py:83`)이나 idle 구간엔 heartbeat가 나가지 않아 ~60초 후 RabbitMQ 서버가 연결을 종료한다. + - 다음 발행 시 `_ensure_connected`(`rabbitmq.py:94-103`)가 stale `is_open=True`를 신뢰해 재연결을 건너뛰고, `basic_publish`(confirm_delivery 대기, `rabbitmq.py:88/180`)가 실패한다 → `AMQPError`(`rabbitmq.py:193`) → `_mark_disconnected` + `TransportUnavailableError`. + - 이 예외가 `emit()`(`daemon.py:245`)로 올라가 `:246`에서 잡혀 warning 로그만 남기고 삼켜진다("publish failure must not roll back"). 재시도·DLQ 없이 **그 이벤트는 유실**된다. 다음 발행은 재연결돼 정상 → 증상 = 한산 구간 직후 첫 이벤트 간헐 유실. + - 한편 unroutable(구독자 없음)은 `mandatory=True` → `UnroutableError`(`rabbitmq.py:190`) → `_publish_dlq`로 `trex.events.dlq`(durable)에 보존된다 → 유실 아님. 단 DLQ를 소비/알림하는 주체가 없어 조용히 누적되는 가시성 공백이 있고, `_publish_dlq`는 best-effort(`rabbitmq.py:215` 실패 시 로그만)다. +- 기대 상태 + - 발행 연결의 idle 사망을 예방하고, 사망/half-open 시에도 첫 발행이 투명 재시도로 복구되어 이벤트가 유실되지 않는다. + - DLQ(unroutable + 발행 실패 양쪽)의 유입/깊이가 운영에서 관측·경보 가능하다. +- 이해관계자 + - 작성/검수: 팀장(개발 거버넌스) + - 구현: trex-core-dev + - 영향 소비자: trex-orb-v1(라이브), trex-tsl-v1(M2 라이브 예정) + - 운영: TREX 운영팀(DLQ 가시성/경보 소비) +- 제약사항 + - trex-core는 M1 운영 배포본(`9e35467`)이다. wire 계약·기존 consume/RPC 경로 무회귀가 필수다. + - pika BlockingConnection은 스레드 비안전 — keepalive는 발행 전용 연결을 소유한 스레드에서만 I/O를 돌려야 한다. + +## 주요 사용자 시나리오 +- 점심·저활동·개장 직후 등 한산한 구간 뒤 슬롯이 HOLDING으로 전이될 때, 첫 전이 이벤트가 유실되지 않고 tsl가 수신해 트레일링을 시작한다. +- RabbitMQ 일시 단절/재시작 후에도 발행이 재연결·재시도로 복구되어 청산 전이 이벤트가 누락되지 않는다. +- 운영자가 DLQ 유입(구독자 없음 또는 발행 실패)을 대시보드/로그/경보로 인지하고 환류한다. + +# 4. 이번에 정의할 변경 +이번 Discovery에서 다루는 핵심 변경 항목을 목록으로 적는다. + +- (B) 발행 재시도 + 강제 재연결: `publish_event`가 `is_open`을 맹신하지 않고, 발행 실패 시 강제 재연결 후 1회 재시도한다. 재시도까지 실패한 경우에만 실패로 처리한다. +- (A) idle keepalive: 발행 전용 연결에 heartbeat 주기 내 `process_data_events` 틱을 보장해 서버 측 연결 종료를 예방한다. +- (D) DLQ 가시성/경보: `trex.events.dlq` 유입·깊이를 관측 가능하게 한다(unroutable + 발행 실패 양쪽 추적). 운영 관측성(dcy-029) 자산 재사용 여부 검토. + +## 영향 범위 +- 코드 영향: + - `trex_core/adapters/messaging/rabbitmq.py`(`publish_event`/`_ensure_connected`/`_publish_dlq` + keepalive 추가) + - `trex_core/runtime/daemon.py`(`_make_event_emitter` 실패 처리 강화, keepalive 틱 연동) +- 문서 영향: + - `docs/operations.md`(DLQ 가시성/경보 운영 절차), `docs/runtime-flows.md`(발행 재시도/keepalive 흐름) 갱신 검토 +- 운영 영향: + - DLQ 모니터링/경보 신규 절차. 배포는 trex-core 재배포 필요(M1 배포본 갱신) — 사장님 release 결재 대상. + +# 5. 요구사항 목록 + +## 기능 요구사항 +- FR-1 (B): `publish_event`는 연결 종료/half-open으로 발행 실패 시 강제 재연결 후 1회 재시도한다. 재시도까지 실패한 경우에만 `TransportUnavailableError`를 올린다. `is_open` 기반 스킵에 의존하지 않는다. +- FR-2 (A): 발행 전용 연결에 idle keepalive를 도입해 heartbeat 주기 내 I/O가 보장되도록 한다(서버 측 idle 종료 예방). pika 스레드 안전성을 위반하지 않는다. +- FR-3 (D): `trex.events.dlq` 유입/깊이를 운영에서 관측·경보 가능하게 한다(메트릭/로그/소비자 중 택1). unroutable과 발행 실패 양쪽이 추적되어야 한다. +- FR-4: `_make_event_emitter`의 발행 실패 처리를 보강한다 — 재시도 후에도 실패 시 단순 warning 삼킴이 아니라 식별 가능한 경보 레벨 로그/메트릭을 남긴다. + +## 비기능 요구사항 +- NFR-1: M1 운영 배포본(`9e35467`) 무회귀 — 기존 consume/RPC/wire 계약과 토폴로지(`declare_topology`)는 불변. +- NFR-2: 재시도/keepalive가 execution-loop를 장시간 블록하지 않는다(매도 타이밍 보호) — 재시도 1회 + 명시적 타임아웃 상한. +- NFR-3: 변경은 transport 어댑터·발행 경로에 국소화하고, 슬롯 도메인/broker 집행 로직은 건드리지 않는다. + +## 범위 경계 +- In Scope + - 발행 전용 연결의 재시도·재연결·keepalive(B+A) + - DLQ 유입/깊이 가시성·경보(D) + - emit 실패 처리 강화(FR-4) +- Out of Scope + - 이벤트 영속 outbox(옵션 C) — 본 주기 제외, 필요 시 후속(M3) Discovery. (사장님 스코프 결정 B+A+D) + - consumer(orb/tsl) 측 idempotency 구현 — 본 주기는 검증/확인까지, 구현은 해당 repo 별도 추적 + - RPC client(`call_rpc`) 연결 정책 — 본 주기는 이벤트 발행 경로 한정 + +# 6. 리스크/가정 목록 + +## 리스크 +- R-1 (High): trex-core는 M1 라이브 배포본이고 발행 경로는 orb(라이브)·tsl(M2)에 동시 영향 → 회귀 시 사고 반경 큼. + - 완화 방안: opus QC 강제, 기존 회귀 스위트 + wire 계약 무회귀 검증, transport 어댑터로 변경 국소화. +- R-2 (Medium): keepalive가 pika 스레드 비안전성과 충돌 가능(다른 스레드에서 I/O 호출 시 손상). + - 완화 방안: 발행 전용 연결을 소유한 스레드에서만 keepalive I/O 실행하도록 설계 단계에서 확정(OQ-1). +- R-3 (Medium): 재시도는 at-least-once 의미 → 동일 이벤트 중복 발행 가능. + - 완화 방안: 소비자(orb/tsl) idempotency 검증(OQ-2). 슬롯 전이 이벤트는 멱등 적용 가능 추정. + +## 가정 +- A-1: 본 변경은 transport 어댑터·발행 경로에 한정되며 trex-core dcy-028(strategy-state, BAT-042 released) 등 기존 계약은 불변이다. +- A-2: 슬롯 전이 이벤트 소비자(orb/tsl)는 중복 수신에 견고하거나 견고화 가능하다. +- A-3: DLQ 가시성은 dcy-029(운영 관측성) 로깅/뷰 자산을 일부 재사용할 수 있다. + +## 오픈 질문 +- OQ-1: keepalive를 별도 스레드로 둘지, execution-loop 틱에 발행 연결 `process_data_events`를 삽입할지 (pika 스레드 안전성 제약). + - 상태: Deferred + - 처리 방안: draft-batch-design에서 trex-core-dev가 스레드 소유 모델 기준으로 결정. REQ에 설계 결정 추적 인계. + - 종료 조건: batch design 문서에 keepalive 실행 위치·스레드 소유가 확정되면 Answered 처리. +- OQ-2: 재시도 at-least-once로 인한 이벤트 중복 시 소비자(orb/tsl)가 멱등한가. + - 상태: Deferred + - 처리 방안: delivery 단계에서 trex-orb-v1/trex-tsl-v1 이벤트 핸들러의 멱등성 확인(§10 DATA로 추적). 비멱등이면 해당 repo 후속 티켓. confirm-discovery를 막지 않음(전이 이벤트는 멱등 적용 가능 추정). + - 종료 조건: 두 소비자의 중복 수신 동작이 확인되면 Answered. +- OQ-3: DLQ 가시성을 메트릭/로그/소비자 데몬 중 무엇으로 구현할지. + - 상태: Deferred + - 처리 방안: draft-batch-design에서 dcy-029 관측성 자산 재사용 가능성과 함께 결정. + - 종료 조건: batch design에 DLQ 가시성 구현 방식이 확정되면 Answered. + +# 7. 초기 성공 기준 +- S-1: idle(>heartbeat×2) 구간 직후 발행이 유실 없이 성공한다(재연결·재시도로 복구). +- S-2: RabbitMQ 강제 단절/재시작 시나리오에서 발행 이벤트 누락 0건. +- S-3: DLQ 유입(unroutable + 발행 실패)이 관측 지표로 노출되고 경보 가능하다. + +## 측정 방식 및 데이터 출처 +- S-1 측정 방식: idle 주입 후 발행 통합 테스트(연결 강제 종료 후 publish) / 데이터 출처: trex-core 테스트 스위트(신규 케이스). +- S-2 측정 방식: broker 재시작 fault-injection 테스트에서 발행 카운트 vs 수신 카운트 대조 / 데이터 출처: 통합 테스트 + trex-sim e2e(M2 트랙C 연계). +- S-3 측정 방식: DLQ 깊이/유입 메트릭 노출 확인 / 데이터 출처: 관측 지표(로그/메트릭), `docs/operations.md`. + +# 8. REQ로 넘기기 전 확인 체크 +- [x] 범위(In Scope/Out of Scope) 확정 + - 기준: 사장님 스코프 B+A+D 확정 반영 + outbox(C) 제외 합의 명시 (2026-06-19 확정) +- [x] 성공 지표의 측정 방식/데이터 출처 확정 + - 기준: S-1~3의 테스트/메트릭 출처가 trex-core 스위트·trex-sim e2e로 연결됨 +- [x] High 영향 리스크 우선순위 및 해소 책임자 지정 + - 기준: R-1(회귀) 해소 책임 = trex-core-dev 구현 + opus qc-reviewer (CONFIRM① 확정) +- [x] 모든 오픈 질문(OQ) `Open` 상태 없음 확인 + - 기준: OQ-1/3 Deferred(design 인계), OQ-2 Deferred(delivery 검증) 처리 완료 — Open 0개 +- [x] Freeze 확정 담당자(CONFIRMED_BY) 지정 + - 기준: 사장님(CEO) via 팀장 + +# 9. 파일 처리 결과 +- 처리 결과: 생성 +- 생성 경로: docs/discovery/dcy-031_20260619_event-publisher-connection-reliability.md +- 참조 문서: + - trex_core/adapters/messaging/rabbitmq.py + - trex_core/runtime/daemon.py + - docs/discovery/dcy-006_20260605_strategy-module-rabbitmq-transport.md + +# 10. 사용자 결정 필요 항목 요약 + +## DECIDE +- (해소됨) 수정 스코프 = B(재시도)+A(keepalive)+D(DLQ 가시성). outbox(C)는 제외→M3 후보. — 사장님 결정 2026-06-19, §5에 반영. + +## CONFIRM +- (확정) opus QC: 본 batch는 고위험(M1 라이브 trex-core)이라 qc-reviewer를 opus로 승격 운용. — 사장님 확정 2026-06-19. +- (확정) M2 게이트: tsl(M2) 라이브 전 선행 완료 — 6/28 릴리즈 창 내 배포·검증. — 사장님 확정 2026-06-19. + +## DATA +- {{DATA_CONSUMER_IDEMPOTENCY}}: orb/tsl 이벤트 핸들러의 중복 수신 멱등성 확인 결과(OQ-2 종료용). + +# 11. Discovery Freeze +REQ 초안 작성 인계 시점의 확정 플래그와 섹션 참조만 기록한다. + +## 섹션 참조 +- 범위: [# 5. 요구사항 목록 > 범위 경계](#5-요구사항-목록) +- 리스크/오픈 질문: [# 6. 리스크/가정 목록](#6-리스크가정-목록) +- 성공 기준: [# 7. 초기 성공 기준](#7-초기-성공-기준) +- 인계 전 확인: [# 8. REQ로 넘기기 전 확인 체크](#8-req로-넘기기-전-확인-체크) +- 사용자 결정 필요: [# 10. 사용자 결정 필요 항목 요약](#10-사용자-결정-필요-항목-요약) + +## Freeze 플래그 +- Handoff Decision: REQ Drafting 진행 가능 +- Handoff Rationale: confirm-discovery 완료. 스코프 B+A+D 확정, CONFIRM 2건(opus QC·M2 게이트) 확정, OQ 전부 Deferred 처리(Open 0). DATA 1건(소비자 멱등성)은 delivery 검증으로 추적. trex-core-dev에 draft-req 인계. +- Ready for REQ Drafting: true +- Confirmed By: 사장님(CEO) via 팀장 +- Confirmed At (KST): 2026-06-19 23:20 diff --git a/docs/discovery/index.md b/docs/discovery/index.md index 9f5b9b5..b107bbf 100644 --- a/docs/discovery/index.md +++ b/docs/discovery/index.md @@ -36,3 +36,4 @@ | dcy-028_20260615_slot-strategy-state-store | 20260615 | Slot Strategy-State 저장소 — per-strategy opaque state store (read/write CAS/close 삭제, issue #26 C-1/C-2/C-3) | [Discovery](./dcy-028_20260615_slot-strategy-state-store.md) | confirmed | | dcy-029_20260616_operational-observability-logging-and-pnl-view | 20260616 | 운영 관측성 — 주문 결과 로깅 + 로그 타임스탬프(KST) + 청산사유 분류 + 슬롯/PnL 조회 뷰 + 운영자 런북 (issue #29) | [Discovery](./dcy-029_20260616_operational-observability-logging-and-pnl-view.md) | confirmed | | dcy-030_20260616_slippage-limit-price-tick-size-snapping | 20260616 | slippage 지정가 KRX 호가단위 미반영 → KIS 거부 (tick-size 스냅, 상한 준수 방향, issue #28) | [Discovery](./dcy-030_20260616_slippage-limit-price-tick-size-snapping.md) | confirmed | +| dcy-031_20260619_event-publisher-connection-reliability | 20260619 | 이벤트 발행 연결 신뢰성 — idle heartbeat 사망 후 첫 발행 유실 + DLQ 가시성 (OPS-005) | [Discovery](./dcy-031_20260619_event-publisher-connection-reliability.md) | confirmed | diff --git a/docs/srs/Documentation/req-094_event-publisher-reliability-baseline-alignment.md b/docs/srs/Documentation/req-094_event-publisher-reliability-baseline-alignment.md new file mode 100644 index 0000000..c178e29 --- /dev/null +++ b/docs/srs/Documentation/req-094_event-publisher-reliability-baseline-alignment.md @@ -0,0 +1,101 @@ +# req-094: 이벤트 발행 신뢰성 baseline 문서 정합 (runtime-flows / project-structure / operations 갱신) + +- Status: Approved +- Type: Documentation +- Priority: Medium +- Owner: project maintainers + +## Intent +- REQ-091(발행 재시도+재연결), REQ-092(idle keepalive), REQ-093(DLQ 가시성/emit 경보) 구현 후 `docs/runtime-flows.md`와 `docs/project-structure.md`의 `슬롯 이벤트 발행/구독` 및 `데몬 lifecycle` 흐름 기술이 구현과 어긋나게 된다. 또한 `docs/operations.md`에 DLQ 관측/경보 운영 절차가 신규 필요하다. 구현 완료 시점에 세 baseline 문서를 실제 구현과 정합시킨다. + +## Requirement +- REQ-091~093 구현 완료 후, 아래 baseline 문서를 실제 구현 결과와 정합시킨다. + - `docs/runtime-flows.md`: `슬롯 이벤트 발행/구독` 흐름에 재시도/강제 재연결 경로, idle keepalive 동작, DLQ 유입 관측성을 반영한다. + - `docs/runtime-flows.md`: `데몬 lifecycle` 흐름에 keepalive 틱 연동(OQ-1 결정 결과)을 반영한다. + - `docs/project-structure.md`: keepalive 컴포넌트 또는 관측성 훅이 새 모듈/함수로 추가된 경우 구조 기술을 갱신한다. + - `docs/operations.md`: DLQ 가시성/경보(OQ-3 결정 결과 반영), 운영자 조치 절차, 경보 임계값 기준을 신규 기재한다. +- 문서 갱신은 배포 wire 계약(`docs/interface-contract.md`)을 변경하지 않는다. AMQP 토폴로지·큐·exchange·라우팅 키 계약은 불변이며 본 REQ 범위 밖이다. + +## Acceptance Criteria +1. Given REQ-091~093 구현이 완료된 상태에서, When `docs/runtime-flows.md`를 확인하면, Then `슬롯 이벤트 발행/구독` 항목이 재시도/강제 재연결 경로와 idle keepalive를 포함하여 실제 구현과 일치한다. +2. Given OQ-1(keepalive 실행 위치)이 batch design에서 확정된 후, When `docs/runtime-flows.md`의 `데몬 lifecycle` 흐름을 확인하면, Then keepalive 틱 연동이 결정된 구현 방식대로 기술되어 있다. +3. Given OQ-3(DLQ 가시성 구현 방식)이 batch design에서 확정된 후, When `docs/operations.md`를 확인하면, Then DLQ 유입/깊이 관측 방법과 운영자 조치 절차가 신규 기재되어 있다. +4. Given `docs/project-structure.md`를 확인할 때, When keepalive 또는 관측성 훅이 신규 모듈로 추가된 경우, Then 해당 모듈이 구조 문서에 누락 없이 반영되어 있다. +5. Given `docs/interface-contract.md`를 확인할 때, When 본 REQ 작업이 완료되면, Then AMQP 토폴로지·큐·exchange·라우팅 키 기술이 변경되지 않았다(wire 계약 무회귀). + +## Impacted Area +- Docs: docs/runtime-flows.md(발행/keepalive 흐름 갱신), docs/project-structure.md(신규 컴포넌트 반영), docs/operations.md(DLQ 운영 절차 신규). docs/interface-contract.md는 불변. +- Modules: 없음 (문서 전용 REQ). +- Tests: 없음. 단, batch verification 시 문서-구현 정합 체크리스트 수동 검증. + +## Notes +- 출처: dcy-031 §4(영향 범위 — 문서 영향), NFR-1(wire 계약 무회귀). +- 이 REQ는 문서 전용이며 코드 변경 없이 구현된다. batch 편성 시 REQ-091~093와 같은 batch에 묶어 구현 완료 후 문서 갱신 단계로 처리한다. +- OQ-1·OQ-3 결정 전에는 해당 항목(AC-2, AC-3)을 확정할 수 없다. batch design 완료 후 갱신 내용이 확정된다. +- `docs/interface-contract.md`는 AMQP wire 계약 문서이며 trex-orb-v1·trex-tsl-v1에 파급 영향이 있다. 본 주기에서 wire 계약을 변경하지 않으므로 이 파일은 본 REQ 범위 밖이다(SDLC 교차 영향 사안 해당 없음). + +## Change Log + +### CHG-20260619-01 +- Date: 2026-06-19 +- Author: trex-core-dev + +#### Change Summary +- dcy-031 §4(문서 영향 범위)로부터 최초 생성(Proposed). REQ-091~093 구현 후 baseline 정합 문서 REQ. + +#### Intent +- 이벤트 발행 신뢰성 구현 후 runtime-flows / project-structure / operations baseline 문서 갱신을 backlog에 정규화한다. + +#### Acceptance Criteria Delta +- Added: + - AC-1: runtime-flows 발행/keepalive 경로 정합 + - AC-2: OQ-1 결정 결과 데몬 lifecycle 반영 + - AC-3: OQ-3 결정 결과 operations.md DLQ 절차 신규 + - AC-4: project-structure 신규 컴포넌트 반영 + - AC-5: interface-contract.md 불변 확인 +- Updated: 없음 +- Removed: 없음 + +#### Impacted Area Delta +- Docs: + - docs/runtime-flows.md, docs/project-structure.md, docs/operations.md +- Modules: + - 없음 +- Tests: + - 없음 (수동 검증) + +#### Delivery Trace Delta +- Batches: + - none (draft-req 단계, batch 미편성) +- Releases: + - none + +#### Revalidation Impact +- Reverification Needed: no +- Existing Implementation Invalidated: no +- Status Recommendation: keep-current +- Follow-up Action: confirm-req 후 REQ-091~093과 함께 단일 batch 편성 예정 + +#### Validation Plan +- batch verification 단계에서 docs/runtime-flows.md · docs/project-structure.md · docs/operations.md의 변경 내용이 구현과 일치하는지 수동 체크리스트로 확인. docs/interface-contract.md 불변 diff 확인. + +### CHG-20260619-02 +- Date: 2026-06-19 +- Author: 팀장 (confirm-req) + +#### Change Summary +- Proposed → **Approved**. 문서 전용 REQ. 필수 필드 충족, source dcy-031 정합. AC-2/AC-3은 OQ-1/OQ-3 design 확정 후 충족(구현 후 정합 단계라 승인 비차단). + +#### Intent +- delivery input 승인. REQ-091~093 구현 후 baseline 문서 정합을 같은 batch에서 처리. + +#### Acceptance Criteria Delta +- Added: 없음 +- Updated: 없음 +- Removed: 없음 + +#### Revalidation Impact +- Reverification Needed: no +- Existing Implementation Invalidated: no +- Status Recommendation: keep-current +- Follow-up Action: REQ-091~093과 함께 단일 batch 편성 예정 diff --git a/docs/srs/Exception/req-091_publish-retry-and-force-reconnect.md b/docs/srs/Exception/req-091_publish-retry-and-force-reconnect.md new file mode 100644 index 0000000..ca5c6ca --- /dev/null +++ b/docs/srs/Exception/req-091_publish-retry-and-force-reconnect.md @@ -0,0 +1,105 @@ +# req-091: 이벤트 발행 재시도 + 강제 재연결 (idle 후 첫 발행 무유실) + +- Status: Approved +- Type: Exception +- Priority: High +- Owner: project maintainers + +## Intent +- 발행 전용 RabbitMQ 연결이 idle heartbeat 사망(half-open) 상태에서 `is_open=True`를 신뢰한 채 `basic_publish`가 실패하면 이벤트가 재시도·DLQ 없이 조용히 유실된다. 이 결함을 제거해, idle 후 첫 발행 시도에서 연결이 죽어 있어도 강제 재연결 후 1회 재시도로 이벤트가 유실되지 않도록 한다. tsl(M2) 라이브 선행 조건인 슬롯 전이 이벤트 신뢰성을 보장한다. + +## Requirement +- `publish_event`(`adapters/messaging/rabbitmq.py`)는 아래 재시도 정책을 구현한다. + - 발행(`basic_publish` + publisher confirms 대기) 실패 시 — 예외 종류와 무관하게 — 강제 재연결(`_ensure_connected`가 아니라 명시적 `close` 후 재연결)을 수행한 뒤 동일 메시지를 1회 재시도한다. + - 재시도까지 실패한 경우에만 `TransportUnavailableError`를 호출자에게 올린다. + - `is_open` 플래그 기반 조기 반환(`_ensure_connected` 단독 판단)에 의존하지 않는다 — `is_open=True`라도 발행이 실패하면 강제 재연결 경로를 탄다. +- 재시도 1회에는 명시적 타임아웃 상한을 적용해 execution-loop를 과도하게 블록하지 않는다(NFR-2 흡수). +- 이 변경은 `adapters/messaging/rabbitmq.py`의 `publish_event` 경로에 국소화하며, consume/RPC 경로(`call_rpc`, consumer 스레드), `declare_topology`, 슬롯 도메인/broker 집행 로직을 건드리지 않는다(NFR-3 흡수). +- M1 운영 배포본(`9e35467`)의 wire 계약 — `trex.events` exchange 토폴로지, routing key 형식, 페이로드 스키마, consumer RPC 큐(intent/slots/control) — 은 불변이다(NFR-1 흡수). + +## Acceptance Criteria +1. Given 발행 전용 연결이 idle 60초 이상 경과하여 서버 측 종료(half-open) 상태이고, When `publish_event`가 호출되면, Then 강제 재연결 후 동일 이벤트를 1회 재시도해 발행에 성공하며 `TransportUnavailableError`가 발생하지 않는다(S-1 연결). +2. Given 발행 전용 연결이 half-open이고 재시도 1회도 실패하는 조건에서, When `publish_event`가 호출되면, Then `TransportUnavailableError`가 호출자에 전파되고, 이벤트 유실은 인지된 실패로 처리된다(재시도 없이 삼키던 기존 동작 제거). +3. Given 발행 재시도 타임아웃 상한이 설정되어 있고, When 재연결 또는 재시도 중 타임아웃이 초과하면, Then 해당 publish 시도는 실패로 처리되며 execution-loop 블록이 명시적 상한을 초과하지 않는다(NFR-2 — 타임아웃 값은 design 단계에서 확정). +4. Given 정상 발행(연결 살아 있음) 조건에서, When `publish_event`가 호출되면, Then 기존과 동일하게 publisher confirms + `trex.events` exchange에 발행되며, 재시도 경로가 추가 레이턴시를 유발하지 않는다(회귀 무). +5. Given `call_rpc` / intent consumer / slots-rpc consumer / control-rpc consumer 경로에서, When 연결 상태를 확인하면, Then 이 REQ의 변경이 해당 경로의 동작에 영향을 주지 않는다(consume/RPC 경로 무회귀, NFR-1). +6. Given RabbitMQ 브로커가 재시작된 후 발행 전용 연결이 끊긴 상태에서, When 슬롯 전이 이벤트가 발행 시도되면, Then 강제 재연결 후 재시도로 이벤트가 성공적으로 전달된다(S-2 연결). + +## Impacted Area +- Docs: docs/runtime-flows.md(`슬롯 이벤트 발행/구독` 흐름 — 재시도/재연결 경로 추가 기재, REQ-094에서 갱신) +- Modules: `trex_core/adapters/messaging/rabbitmq.py`(`publish_event` L168-197, `_ensure_connected` L94-103). 슬롯 도메인/broker 집행/consume 경로 불변. +- Tests: `tests/adapters/messaging/` — half-open 연결 simulate 후 publish 성공 확인(AC-1), 재시도 소진 후 실패 확인(AC-2), 타임아웃 상한 확인(AC-3), 정상 발행 회귀(AC-4), consume/RPC 경로 회귀(AC-5), broker 재시작 시나리오(AC-6). `tests/application/test_messaging_event_publisher.py` 기존 회귀 유지. + +## Notes +- 출처: dcy-031 FR-1(B), NFR-1, NFR-2, NFR-3, R-1, S-1, S-2. +- NFR-2(타임아웃 상한 값)와 강제 재연결 구체 구현 방식(close-and-reconnect vs. selective channel reopen)은 draft-batch-design에서 결정한다. 본 REQ는 "1회 재시도 + 타임아웃 상한" 정책만 확정하며 수치 기준은 AC-3 Notes에서 "design 단계 결정"으로 추적한다. +- OQ-1(keepalive 스레드 위치) 결정은 REQ-092에 귀속하며 본 REQ와 독립이다. +- 재시도 at-least-once 의미(중복 발행 가능성)는 OQ-2로 추적 중 — 소비자(orb/tsl) 멱등성은 delivery 검증(DATA) 단계에서 확인한다. +- R-1(M1 라이브 회귀): 본 batch는 opus qc-reviewer 강제(CONFIRM ①, dcy-031 §10). + +## Change Log + +### CHG-20260619-01 +- Date: 2026-06-19 +- Author: trex-core-dev + +#### Change Summary +- dcy-031 FR-1(B) 로부터 최초 생성(Proposed). NFR-1·NFR-2·NFR-3 흡수. S-1·S-2 연결. + +#### Intent +- 이벤트 발행 재시도 + 강제 재연결 정책을 backlog에 정규화한다. + +#### Acceptance Criteria Delta +- Added: + - AC-1: idle 후 첫 발행 재연결+재시도 성공 + - AC-2: 재시도 소진 후 TransportUnavailableError 전파 + - AC-3: 타임아웃 상한으로 execution-loop 과블록 방지 + - AC-4: 정상 발행 경로 회귀 무 + - AC-5: consume/RPC 경로 회귀 무 + - AC-6: broker 재시작 후 발행 복구 +- Updated: 없음 +- Removed: 없음 + +#### Impacted Area Delta +- Docs: + - docs/runtime-flows.md (재시도/재연결 경로) +- Modules: + - trex_core/adapters/messaging/rabbitmq.py +- Tests: + - tests/adapters/messaging/ (half-open/재시도/타임아웃/회귀) + +#### Delivery Trace Delta +- Batches: + - none (draft-req 단계, batch 미편성) +- Releases: + - none + +#### Revalidation Impact +- Reverification Needed: no +- Existing Implementation Invalidated: no +- Status Recommendation: keep-current +- Follow-up Action: confirm-req 후 suggest-batch-reqs + draft-batch 편성 + +#### Validation Plan +- stub 또는 mock 기반 half-open 시뮬레이션 후 publish 재시도 성공/실패 시나리오 검증. 기존 messaging 회귀 스위트 전체 통과 확인. + +### CHG-20260619-02 +- Date: 2026-06-19 +- Author: 팀장 (confirm-req) + +#### Change Summary +- Proposed → **Approved**. 필수 필드(Status/Type/Priority/Owner/Intent/Requirement/AC/Impacted Area/Change Log) 충족, AC 구체·측정 가능, source dcy-031(confirmed, Open OQ 0) 정합, blocker 없음. + +#### Intent +- delivery input 승인. NFR-2 타임아웃 수치는 비핵심 운영 메모로 draft-batch-design 확정(승인 비차단). + +#### Acceptance Criteria Delta +- Added: 없음 +- Updated: 없음 +- Removed: 없음 + +#### Revalidation Impact +- Reverification Needed: no +- Existing Implementation Invalidated: no +- Status Recommendation: keep-current +- Follow-up Action: suggest-batch-reqs + draft-batch (REQ-091~094 단일 batch 편성 예정) diff --git a/docs/srs/Exception/req-092_publish-idle-keepalive.md b/docs/srs/Exception/req-092_publish-idle-keepalive.md new file mode 100644 index 0000000..ae15fd3 --- /dev/null +++ b/docs/srs/Exception/req-092_publish-idle-keepalive.md @@ -0,0 +1,102 @@ +# req-092: 발행 전용 연결 idle keepalive (서버 측 heartbeat 사망 예방) + +- Status: Approved +- Type: Exception +- Priority: High +- Owner: project maintainers + +## Intent +- 발행 전용 RabbitMQ 연결은 consumer 루프(`process_data_events` 0.2s 틱)와 달리 publish 사이에 I/O가 전혀 없어 `heartbeat=30` 설정에도 불구하고 ~60초 idle 후 서버가 연결을 종료한다. keepalive로 idle 구간에도 heartbeat I/O가 나가도록 해 서버 측 연결 종료를 사전 예방하고, REQ-091 재시도 경로를 보완하는 2차 방어선을 구성한다. + +## Requirement +- 발행 전용 연결에 idle keepalive를 도입해, heartbeat 주기(`heartbeat=30`) 내에 `process_data_events`가 최소 1회 이상 호출되도록 보장한다. +- keepalive는 pika `BlockingConnection`의 스레드 안전성 제약을 위반하지 않는다. 즉, 발행 전용 연결을 소유한 스레드에서만 keepalive I/O를 실행한다. +- keepalive의 구체 구현 방식(별도 keepalive 스레드 vs. execution-loop 틱에 삽입)은 draft-batch-design에서 OQ-1 기준으로 확정한다. 본 REQ는 "발행 연결 소유 스레드 내 I/O 보장" 정책만 확정하며, 구현 위치는 설계 단계 결정으로 추적한다. +- 이 변경은 `adapters/messaging/rabbitmq.py` 및 필요 최소 범위의 `runtime/daemon.py`에 국소화하며, consume/RPC 경로·슬롯 도메인·broker 집행 로직을 건드리지 않는다(NFR-3 흡수). +- M1 운영 배포본(`9e35467`)의 wire 계약은 불변이다(NFR-1 흡수). + +## Acceptance Criteria +1. Given idle 구간이 `heartbeat × 2`(60초) 이상 지속될 때, When keepalive가 활성화되어 있으면, Then 발행 전용 연결이 서버에 의해 종료되지 않고 살아 있는 상태를 유지한다(S-1 1차 방어선). +2. Given keepalive 구현이 별도 스레드를 사용하거나 루프 틱에 삽입되는 경우, When 해당 I/O가 실행될 때, Then 발행 전용 연결을 소유한 스레드에서만 `process_data_events`가 호출되며, 다른 스레드가 동일 연결에 직접 I/O를 호출하지 않는다(pika 스레드 안전성 불변식). +3. Given keepalive가 동작 중이고 동시에 발행이 발생할 때, When 두 작업이 겹치면, Then 연결 상태 손상 또는 예외 없이 정상 완료된다(pika 스레드 안전성 불변식 보강). +4. Given keepalive 설정이 적용된 상태에서, When execution-loop의 매도 트리거 처리 타이밍을 확인하면, Then keepalive 틱이 execution-loop를 과도하게 블록하지 않는다(NFR-2 흡수 — 허용 블록 시간 상한은 design 단계 확정). +5. Given consumer 스레드(intent/slots/control RPC)를 확인할 때, When keepalive 변경이 적용된 코드베이스를 검사하면, Then consumer 경로의 동작·큐 바인딩·토폴로지에 변화가 없다(consume/RPC 무회귀, NFR-1). +6. Given keepalive 구현 방식이 설계 단계에서 확정된 후, When batch design 문서를 확인하면, Then OQ-1(keepalive 실행 위치·스레드 소유 모델)이 명시적으로 결정되어 있다(OQ-1 종료 조건 추적). + +## Impacted Area +- Docs: docs/runtime-flows.md(`데몬 lifecycle` 흐름 — keepalive 틱 추가 기재, REQ-094에서 갱신). docs/project-structure.md(keepalive 컴포넌트 추가 시 반영, REQ-094에서 갱신). +- Modules: `trex_core/adapters/messaging/rabbitmq.py`(keepalive 메커니즘 추가). `trex_core/runtime/daemon.py`(keepalive 연동, 최소 변경). 슬롯 도메인/broker 집행/consume 경로 불변. +- Tests: `tests/adapters/messaging/` — idle 60초 후 연결 생존 확인(AC-1), 스레드 소유 검증(AC-2), 동시 발행+keepalive 충돌 없음(AC-3), execution-loop 블록 상한(AC-4), consume 경로 회귀(AC-5). + +## Notes +- 출처: dcy-031 FR-2(A), NFR-1, NFR-2, NFR-3, R-2(keepalive pika 스레드 충돌), S-1. +- OQ-1(keepalive 구현 위치 — 별도 스레드 vs. execution-loop 삽입): 설계 단계 결정. AC-6에서 추적. 종료 조건: batch design 문서에 명시되면 Answered. +- R-2(pika 스레드 비안전성): 이 위험은 AC-2/AC-3으로 검증하며, 설계 단계(OQ-1)에서 구현 방식을 확정해 완화한다. +- keepalive는 REQ-091 재시도와 독립 계층이다. keepalive는 1차 예방(연결 사망 방지), REQ-091은 2차 복구(사망 후 재연결+재시도). 두 REQ는 함께 배치될 예정이지만 독립 구현·독립 검증 가능하다. + +## Change Log + +### CHG-20260619-01 +- Date: 2026-06-19 +- Author: trex-core-dev + +#### Change Summary +- dcy-031 FR-2(A) 로부터 최초 생성(Proposed). NFR-1·NFR-2·NFR-3 흡수. R-2(pika 스레드) OQ-1 인계. S-1 연결. + +#### Intent +- 발행 전용 연결 idle keepalive 정책을 backlog에 정규화하고, OQ-1 설계 결정 추적을 AC-6으로 고정한다. + +#### Acceptance Criteria Delta +- Added: + - AC-1: idle 60초 후 연결 생존 + - AC-2: 발행 소유 스레드에서만 I/O (스레드 안전성) + - AC-3: 동시 발행+keepalive 충돌 없음 + - AC-4: execution-loop 블록 상한 + - AC-5: consume/RPC 경로 무회귀 + - AC-6: OQ-1 종료 조건 추적 +- Updated: 없음 +- Removed: 없음 + +#### Impacted Area Delta +- Docs: + - docs/runtime-flows.md, docs/project-structure.md +- Modules: + - trex_core/adapters/messaging/rabbitmq.py, trex_core/runtime/daemon.py +- Tests: + - tests/adapters/messaging/ (idle/스레드/동시성/블록/회귀) + +#### Delivery Trace Delta +- Batches: + - none (draft-req 단계, batch 미편성) +- Releases: + - none + +#### Revalidation Impact +- Reverification Needed: no +- Existing Implementation Invalidated: no +- Status Recommendation: keep-current +- Follow-up Action: confirm-req 후 REQ-091과 함께 단일 batch 편성 예정 + +#### Validation Plan +- time-freeze 또는 sleep mock으로 idle 구간을 시뮬레이션한 후 연결 생존 + 스레드 안전성 검증. 기존 messaging 회귀 스위트 전체 통과 확인. + +### CHG-20260619-02 +- Date: 2026-06-19 +- Author: 팀장 (confirm-req) + +#### Change Summary +- Proposed → **Approved**. 필수 필드 충족, AC 구체, source dcy-031(confirmed, Open OQ 0) 정합. OQ-1(keepalive 실행 위치)은 AC-6에서 design 인계 추적 — 승인 비차단. + +#### Intent +- delivery input 승인. keepalive 구현 위치(별도 스레드 vs execution-loop 틱)는 draft-batch-design에서 확정. + +#### Acceptance Criteria Delta +- Added: 없음 +- Updated: 없음 +- Removed: 없음 + +#### Revalidation Impact +- Reverification Needed: no +- Existing Implementation Invalidated: no +- Status Recommendation: keep-current +- Follow-up Action: REQ-091과 함께 단일 batch 편성 예정 diff --git a/docs/srs/Exception/req-093_dlq-visibility-and-emit-error-alert.md b/docs/srs/Exception/req-093_dlq-visibility-and-emit-error-alert.md new file mode 100644 index 0000000..46330ec --- /dev/null +++ b/docs/srs/Exception/req-093_dlq-visibility-and-emit-error-alert.md @@ -0,0 +1,104 @@ +# req-093: DLQ 가시성/경보 + emit 실패 경보 (trex.events.dlq 관측성) + +- Status: Approved +- Type: Exception +- Priority: High +- Owner: project maintainers + +## Intent +- `trex.events.dlq`(unroutable + 발행 실패 양쪽)는 현재 durable 큐로 보존되지만 소비/알림 주체가 없어 운영에서 조용히 누적된다. 또한 `_make_event_emitter`의 발행 실패는 warning 로그로 삼켜져 운영자가 인지하지 못한다. DLQ 유입/깊이를 관측 가능하게 만들고, 최종 실패 시 식별 가능한 경보 레벨 로그를 남겨 운영 피드백 루프를 닫는다. + +## Requirement +- `trex.events.dlq` 유입(unroutable + 발행 실패 양쪽)과 깊이(queue depth)를 운영에서 관측·경보 가능하게 한다. 구현 방식(메트릭 노출 / 로그 기반 / 소비자 데몬)은 draft-batch-design에서 OQ-3 기준으로 결정한다. +- unroutable 이벤트: `mandatory=True` → `UnroutableError` → `_publish_dlq`로 `trex.events.dlq`에 보존하는 기존 경로는 유지하되, DLQ 유입이 관측 가능해야 한다. +- 발행 실패 이벤트: REQ-091의 재시도까지 실패한 경우 `TransportUnavailableError`가 올라오면, 해당 실패를 DLQ(또는 별도 실패 경보 채널)에 추적 가능한 형태로 기록하거나 메트릭을 카운트한다. 구체 경로는 OQ-3 결정에 따른다. +- `_make_event_emitter`(`runtime/daemon.py`)의 발행 실패 처리를 보강한다: REQ-091 재시도 후에도 실패 시 단순 `WARNING` 삼킴이 아니라 `ERROR` 또는 그에 준하는 경보 레벨 로그를 남긴다. 로그에는 routing key, 슬롯 ID(또는 correlation 정보), 실패 사유가 포함되어 식별 가능해야 한다. +- `_publish_dlq`의 best-effort 실패(DLQ 자체 발행 실패)도 `ERROR` 수준 로그를 남긴다. +- 이 변경은 `adapters/messaging/rabbitmq.py`(`_publish_dlq`)와 `runtime/daemon.py`(`_make_event_emitter`) 및 관측성 메커니즘 추가 범위에 국소화한다. wire 계약·consume/RPC 경로·슬롯 도메인 불변(NFR-1·NFR-3 흡수). + +## Acceptance Criteria +1. Given unroutable 이벤트가 `trex.events.dlq`에 유입될 때, When 관측 지표(로그/메트릭/소비자)를 확인하면, Then DLQ 유입이 운영자가 인지 가능한 형태로 노출된다(S-3 연결). +2. Given `trex.events.dlq` 큐의 깊이(depth)를 확인할 때, When 유입 이벤트가 누적되면, Then 운영자가 접근 가능한 지표(로그/메트릭/RabbitMQ 관리 API 등)로 깊이를 파악할 수 있다(S-3 연결). +3. Given REQ-091 재시도 후에도 발행이 실패한 경우, When `_make_event_emitter`에서 `TransportUnavailableError`를 처리할 때, Then `WARNING` 수준이 아닌 `ERROR`(또는 이상) 수준 로그가 남고, routing key와 슬롯 식별 정보가 포함된다(FR-4 연결). +4. Given `_publish_dlq`의 DLQ 재발행 자체가 실패한 경우, When 예외가 발생하면, Then `ERROR` 수준 로그가 남고 메시지 유실이 silent하게 처리되지 않는다. +5. Given DLQ 가시성 구현 방식이 설계 단계에서 확정된 후, When batch design 문서를 확인하면, Then OQ-3(DLQ 가시성 구현 방식 — 메트릭/로그/소비자 데몬 중 택1)이 명시적으로 결정되어 있다(OQ-3 종료 조건 추적). +6. Given DLQ 유입/경보 메커니즘이 추가된 상태에서, When consumer 경로(intent/slots/control RPC)를 확인하면, Then 해당 경로의 동작·큐 바인딩·토폴로지에 변화가 없다(consume/RPC 무회귀, NFR-1). + +## Impacted Area +- Docs: docs/operations.md(DLQ 가시성/경보 운영 절차 신규 기재, REQ-094에서 함께 갱신 검토). docs/runtime-flows.md(`슬롯 이벤트 발행/구독` DLQ 경로 설명 보강, REQ-094에서 갱신). +- Modules: `trex_core/adapters/messaging/rabbitmq.py`(`_publish_dlq` L199-216 — DLQ best-effort 실패 ERROR 로그 + 가시성 훅). `trex_core/runtime/daemon.py`(`_make_event_emitter` L238-248 — 발행 실패 ERROR 경보). 관측성 구현(OQ-3 결정 후 추가). wire 계약·consume/RPC 경로·슬롯 도메인 불변. +- Tests: `tests/adapters/messaging/` — DLQ 유입 관측 지표 확인(AC-1), DLQ 깊이 노출(AC-2), emit 실패 ERROR 로그(AC-3), DLQ 재발행 실패 ERROR(AC-4), consume 경로 회귀(AC-6). `tests/application/test_messaging_event_publisher.py` 기존 회귀 유지. + +## Notes +- 출처: dcy-031 FR-3(D), FR-4, A-3(dcy-029 관측성 자산 재사용 여부 검토), S-3. +- OQ-3(DLQ 가시성 구현 방식 — 메트릭/로그/소비자 중 택1): 설계 단계 결정. AC-5에서 추적. 종료 조건: batch design에 명시되면 Answered. dcy-029(REQ-084~090) 관측성 자산(구조화 로깅 인프라) 재사용 가능성을 설계 단계에서 검토한다. +- FR-4(emit 실패 경보)는 독립 REQ로 승격하지 않고 본 REQ에 흡수한다 — FR-3(DLQ 가시성)과 같은 코드 경로(`_make_event_emitter`, `_publish_dlq`)를 공유하며 독립 구현·검증 시 오버헤드 대비 가치가 낮다. +- `_publish_dlq` best-effort 정책(실패 시 silent log → ERROR로 강화)은 기존 wire 계약 변경 없이 로그 레벨 조정만으로 구현 가능해 설계 결정 없이도 확정 가능하다. +- 본 변경은 DLQ 소비자 데몬(별도 상시 프로세스)을 강제하지 않는다 — 관측 방식은 OQ-3에서 결정. + +## Change Log + +### CHG-20260619-01 +- Date: 2026-06-19 +- Author: trex-core-dev + +#### Change Summary +- dcy-031 FR-3(D) + FR-4로부터 최초 생성(Proposed). NFR-1·NFR-3 흡수. FR-4를 본 REQ에 흡수. OQ-3 설계 인계. A-3(dcy-029 자산 재사용) 인계. S-3 연결. + +#### Intent +- DLQ 가시성/경보 + emit 실패 경보 정책을 backlog에 정규화하고, OQ-3 설계 결정 추적을 AC-5로 고정한다. + +#### Acceptance Criteria Delta +- Added: + - AC-1: DLQ 유입 관측 가능 + - AC-2: DLQ 깊이 파악 가능 + - AC-3: emit 실패 ERROR 수준 경보 + - AC-4: DLQ 재발행 실패 ERROR + - AC-5: OQ-3 종료 조건 추적 + - AC-6: consume/RPC 경로 무회귀 +- Updated: 없음 +- Removed: 없음 + +#### Impacted Area Delta +- Docs: + - docs/operations.md, docs/runtime-flows.md +- Modules: + - trex_core/adapters/messaging/rabbitmq.py, trex_core/runtime/daemon.py +- Tests: + - tests/adapters/messaging/ (DLQ/emit 경보/회귀) + +#### Delivery Trace Delta +- Batches: + - none (draft-req 단계, batch 미편성) +- Releases: + - none + +#### Revalidation Impact +- Reverification Needed: no +- Existing Implementation Invalidated: no +- Status Recommendation: keep-current +- Follow-up Action: confirm-req 후 REQ-091·092와 함께 단일 batch 편성 예정 + +#### Validation Plan +- 로그 캡처 기반으로 DLQ 유입 로그/ERROR 레벨 확인. mock `_publish_dlq` 실패 시나리오로 AC-4 검증. OQ-3 결정 후 구현 방식에 맞는 통합 검증 추가. + +### CHG-20260619-02 +- Date: 2026-06-19 +- Author: 팀장 (confirm-req) + +#### Change Summary +- Proposed → **Approved**. 필수 필드 충족, AC 구체, source dcy-031(confirmed, Open OQ 0) 정합. FR-4 흡수 승인(동일 emit 경로). OQ-3(DLQ 가시성 구현 방식)은 AC-5에서 design 인계 추적 — 승인 비차단. + +#### Intent +- delivery input 승인. DLQ 가시성 구현 방식(메트릭/로그/소비자)은 draft-batch-design에서 dcy-029 관측성 자산 재사용성과 함께 확정. + +#### Acceptance Criteria Delta +- Added: 없음 +- Updated: 없음 +- Removed: 없음 + +#### Revalidation Impact +- Reverification Needed: no +- Existing Implementation Invalidated: no +- Status Recommendation: keep-current +- Follow-up Action: REQ-091·092와 함께 단일 batch 편성 예정 diff --git a/docs/srs/index.md b/docs/srs/index.md index e14cef0..24d995e 100644 --- a/docs/srs/index.md +++ b/docs/srs/index.md @@ -74,7 +74,7 @@ ## Next Requirement ID -- Current: `REQ-091` +- Current: `REQ-095` - Rule: `Requirement Register`에 있는 가장 큰 REQ 번호보다 정확히 1 커야 한다. ## Requirement Register @@ -171,9 +171,16 @@ | REQ-088 | 운영 조회 CLI — 보유·오늘 체결·실현 PnL·대기 주문 | Interface | Implemented | High | project maintainers | [req-088](./Interface/req-088_operator-pnl-query-cli.md) | | REQ-089 | 운영자 런북 — docs/operations.md 신설 | Documentation | Implemented | Medium | project maintainers | [req-089](./Documentation/req-089_operator-runbook.md) | | REQ-090 | 관측성 baseline 문서 정합 — 로깅/관측성·PnL 조회 흐름 | Documentation | Implemented | Medium | project maintainers | [req-090](./Documentation/req-090_observability-baseline-alignment.md) | +| REQ-091 | 이벤트 발행 재시도 + 강제 재연결 (idle 후 첫 발행 무유실) | Exception | Approved | High | project maintainers | [req-091](./Exception/req-091_publish-retry-and-force-reconnect.md) | +| REQ-092 | 발행 전용 연결 idle keepalive (서버 측 heartbeat 사망 예방) | Exception | Approved | High | project maintainers | [req-092](./Exception/req-092_publish-idle-keepalive.md) | +| REQ-093 | DLQ 가시성/경보 + emit 실패 경보 (trex.events.dlq 관측성) | Exception | Approved | High | project maintainers | [req-093](./Exception/req-093_dlq-visibility-and-emit-error-alert.md) | +| REQ-094 | 이벤트 발행 신뢰성 baseline 문서 정합 (runtime-flows / project-structure / operations 갱신) | Documentation | Approved | Medium | project maintainers | [req-094](./Documentation/req-094_event-publisher-reliability-baseline-alignment.md) | ## Recent Change Log Summary +- 2026-06-19 :: REQ-091·092·093·094 Proposed → **Approved** (confirm-req, 팀장): 필수 필드·구체 AC 충족, source dcy-031(confirmed, Open OQ 0) 정합, blocker 없음. 팀장 판정 — FR-4의 REQ-093 흡수 동의, OQ-1/OQ-3 design 인계 확정, NFR-2 타임아웃 수치는 draft-batch-design 확정(비차단). 다음=suggest-batch-reqs + draft-batch(REQ-091~094 단일 batch 편성 예정, 고위험 opus QC). +- 2026-06-19 :: REQ-091·092·093·094 신규 생성(Proposed, draft-req dcy-031): 이벤트 발행 연결 신뢰성 — REQ-091 발행 재시도+강제 재연결(FR-1/B, S-1·S-2, NFR-1~3 흡수), REQ-092 idle keepalive(FR-2/A, OQ-1 design 인계), REQ-093 DLQ 가시성+emit 경보(FR-3/D + FR-4, OQ-3 design 인계, S-3), REQ-094 baseline 문서 정합(runtime-flows/project-structure/operations, docs전용). NFR-2·NFR-3은 REQ-091~093 AC에 흡수. 고위험(M1 라이브) batch — opus QC 강제(CONFIRM①). + - 2026-06-16 :: REQ-086·088·089·090 Approved → **Implemented** (confirm-req-implemented, BAT-046 release-candidate, dcy-029 P1/issue #29): 청산 reason+actor 노출 + 운영 조회 CLI(report, 보유·오늘 체결·실현 PnL net·대기) + 운영자 런북 docs/operations.md + baseline 정합. 14 passed(신규 operator_report+safety_sell), 전체 537 passed/47 skipped 회귀 0, ruff clean. - 2026-06-16 :: REQ-084·085·087 Approved → **Implemented** (confirm-req-implemented, BAT-045 release-candidate, dcy-029 P0/issue #29): 구조화 로깅 기반(ISO8601 KST·레벨·Loki JSON·correlation id) + 주문 결과 로깅(event=order_submit|fill|reject) + 거부 ERROR 가시화 구현·검증. 35 passed(신규 logging+주문결과), 전체 529 passed/47 skipped 회귀 0, ruff clean.