# req-091: 이벤트 발행 재시도 + 강제 재연결 (idle 후 첫 발행 무유실) - Status: Implemented - 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 편성 예정)