Files
trex-core/docs/discovery/dcy-031_20260619_event-publisher-connection-reliability.md
T
genichinandClaude Opus 4.8 bc79f60274 docs(dcy-031): OPS-005 이벤트 발행 신뢰성 Discovery + REQ-091~094
운영팀 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 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VHtM5Ws1HKCKQ4JGi6GRb5
2026-06-19 21:21:19 +09:00

14 KiB
Raw Blame History

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로 넘기기 전 확인 체크

  • 범위(In Scope/Out of Scope) 확정
    • 기준: 사장님 스코프 B+A+D 확정 반영 + outbox(C) 제외 합의 명시 (2026-06-19 확정)
  • 성공 지표의 측정 방식/데이터 출처 확정
    • 기준: S-1~3의 테스트/메트릭 출처가 trex-core 스위트·trex-sim e2e로 연결됨
  • High 영향 리스크 우선순위 및 해소 책임자 지정
    • 기준: R-1(회귀) 해소 책임 = trex-core-dev 구현 + opus qc-reviewer (CONFIRM① 확정)
  • 모든 오픈 질문(OQ) Open 상태 없음 확인
    • 기준: OQ-1/3 Deferred(design 인계), OQ-2 Deferred(delivery 검증) 처리 완료 — Open 0개
  • 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 초안 작성 인계 시점의 확정 플래그와 섹션 참조만 기록한다.

섹션 참조

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