발행 전용 연결의 idle heartbeat 사망 후 첫 발행 유실 결함 제거. - REQ-091 발행 재시도 + 강제 재연결(_force_reconnect, is_open 불신) - REQ-092 idle keepalive(execution-loop 단일스레드 틱, process_data_events time_limit=0) - REQ-093 DLQ 가시성 + emit 실패 ERROR 경보(구조화 로그, dcy-029 재사용) - REQ-094 baseline 문서 정합(runtime-flows/operations/project-structure) 독립 opus QC PASS-WITH-NOTES → 시정 2건(ruff F401, AC-3 프로덕션 경로) 완료. 554 passed/47 skipped/회귀 0/ruff clean. NFR-1 wire 무회귀(interface-contract diff 0). BAT-047 release-candidate, REQ-091~094 Implemented. dcy-031. M2 트랙B. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VHtM5Ws1HKCKQ4JGi6GRb5
7.0 KiB
7.0 KiB
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.eventsexchange 토폴로지, routing key 형식, 페이로드 스키마, consumer RPC 큐(intent/slots/control) — 은 불변이다(NFR-1 흡수).
Acceptance Criteria
- Given 발행 전용 연결이 idle 60초 이상 경과하여 서버 측 종료(half-open) 상태이고, When
publish_event가 호출되면, Then 강제 재연결 후 동일 이벤트를 1회 재시도해 발행에 성공하며TransportUnavailableError가 발생하지 않는다(S-1 연결). - Given 발행 전용 연결이 half-open이고 재시도 1회도 실패하는 조건에서, When
publish_event가 호출되면, ThenTransportUnavailableError가 호출자에 전파되고, 이벤트 유실은 인지된 실패로 처리된다(재시도 없이 삼키던 기존 동작 제거). - Given 발행 재시도 타임아웃 상한이 설정되어 있고, When 재연결 또는 재시도 중 타임아웃이 초과하면, Then 해당 publish 시도는 실패로 처리되며 execution-loop 블록이 명시적 상한을 초과하지 않는다(NFR-2 — 타임아웃 값은 design 단계에서 확정).
- Given 정상 발행(연결 살아 있음) 조건에서, When
publish_event가 호출되면, Then 기존과 동일하게 publisher confirms +trex.eventsexchange에 발행되며, 재시도 경로가 추가 레이턴시를 유발하지 않는다(회귀 무). - Given
call_rpc/ intent consumer / slots-rpc consumer / control-rpc consumer 경로에서, When 연결 상태를 확인하면, Then 이 REQ의 변경이 해당 경로의 동작에 영향을 주지 않는다(consume/RPC 경로 무회귀, NFR-1). - Given RabbitMQ 브로커가 재시작된 후 발행 전용 연결이 끊긴 상태에서, When 슬롯 전이 이벤트가 발행 시도되면, Then 강제 재연결 후 재시도로 이벤트가 성공적으로 전달된다(S-2 연결).
Impacted Area
- Docs: docs/runtime-flows.md(
슬롯 이벤트 발행/구독흐름 — 재시도/재연결 경로 추가 기재, REQ-094에서 갱신) - Modules:
trex_core/adapters/messaging/rabbitmq.py(publish_eventL168-197,_ensure_connectedL94-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 편성 예정)