feat(messaging): OPS-005 이벤트 발행 연결 신뢰성 (BAT-047)
발행 전용 연결의 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
This commit is contained in:
+58
-1
@@ -94,7 +94,62 @@ grep "event=order_reject" /tmp/trex-core.log # 거부 — ERROR 레벨
|
||||
|
||||
---
|
||||
|
||||
## 4. 디버깅 / 트러블슈팅
|
||||
## 4. DLQ 가시성 / 이벤트 발행 경보 (REQ-093 / dcy-031 / BAT-047)
|
||||
|
||||
trex-core는 `trex.events.dlq`(durable) 큐와 구조화 로그를 통해 이벤트 발행 실패를 운영자에게 노출한다.
|
||||
|
||||
### DLQ 유입 원인
|
||||
|
||||
| 원인 | 경로 | 로그 |
|
||||
|---|---|---|
|
||||
| 구독자 없는 이벤트(unroutable) | `UnroutableError` → `_publish_dlq(reason="unroutable")` | ERROR `DLQ entry: reason=unroutable rk=...` |
|
||||
| DLQ 재발행 자체 실패 | `_publish_dlq` 내 exception | ERROR `DLQ publish failed (rk=...)` |
|
||||
| 발행 최종 실패(재연결+재시도 후) | `_make_event_emitter` → `TransportUnavailableError` | ERROR `event publish failed (rk=... slot_id=... strategy=...)` |
|
||||
|
||||
### 로그 기반 경보 (Loki / grep)
|
||||
|
||||
```bash
|
||||
# DLQ 유입 확인
|
||||
grep '"level":"error"' /tmp/trex-core.log | grep "DLQ"
|
||||
# 또는 text 포맷
|
||||
grep "ERROR.*DLQ" /tmp/trex-core.log
|
||||
|
||||
# 발행 최종 실패 확인
|
||||
grep "event publish failed" /tmp/trex-core.log
|
||||
```
|
||||
|
||||
Loki LogQL:
|
||||
```logql
|
||||
{job="trex-core"} | json | level="error" | line_contains "DLQ"
|
||||
{job="trex-core"} | json | level="error" | line_contains "event publish failed"
|
||||
```
|
||||
|
||||
### RabbitMQ queue depth 확인
|
||||
|
||||
```bash
|
||||
# rabbitmqctl로 DLQ 깊이 조회
|
||||
rabbitmqctl list_queues name messages | grep dlq
|
||||
|
||||
# RabbitMQ 관리 UI: http://<broker>:15672 → Queues → trex.events.dlq
|
||||
```
|
||||
|
||||
### 운영자 조치 절차
|
||||
|
||||
1. **DLQ 유입 경보 수신**: 로그 `ERROR ... DLQ entry` 발견.
|
||||
2. **원인 분류**:
|
||||
- `reason=unroutable`: 해당 strategy_id의 전략 데몬(orb/tsl)이 꺼져 있거나 큐 바인딩이 없는 경우. 전략 데몬 기동 상태 확인.
|
||||
- `DLQ publish failed`: RabbitMQ 자체 연결 문제. broker 상태 확인.
|
||||
- `event publish failed`: 발행 연결 재연결+재시도 후에도 실패. broker 재시작 또는 네트워크 장애. trex-core 재기동 검토.
|
||||
3. **DLQ 내용 소비**(선택): 적체된 메시지는 수동 drain 또는 strategy 재기동 후 catch-up.
|
||||
4. **임계값 기준**: DLQ depth > 0 = 경보 대상. depth > 100 = 즉시 조치 권고.
|
||||
|
||||
### idle heartbeat 예방 (REQ-092)
|
||||
|
||||
trex-core는 발행 연결의 idle 종료를 예방하기 위해 `trex-execution` 루프 각 틱(기본 1초)마다 `event_publisher.keepalive_tick()`(`process_data_events(time_limit=0)`)을 호출한다. `heartbeat=30s` 설정 대비 충분한 I/O 주기를 보장한다.
|
||||
|
||||
---
|
||||
|
||||
## 5. 디버깅 / 트러블슈팅
|
||||
|
||||
| 증상 | 원인 | 조치 |
|
||||
|---|---|---|
|
||||
@@ -118,6 +173,8 @@ psql "$TREX_POSTGRESQL_DSN" -c "SELECT slot_id, from_state, to_state, reason, ac
|
||||
|
||||
---
|
||||
|
||||
---
|
||||
|
||||
## 관련 문서
|
||||
- `docs/interface-contract.md` — RabbitMQ 토폴로지 + 메시지 payload + reason/actor 어휘
|
||||
- `docs/runtime-flows.md` — 흐름 다이어그램(스냅샷/intent RPC/이벤트/집행 루프/로깅)
|
||||
|
||||
Reference in New Issue
Block a user