# REL-009: OPS-005 이벤트 발행 신뢰성 + 체결가 가드 + 알람 emitter + 무데이터 런북 (M2 통합 릴리즈) - Status: confirmed - Profile: app-service - Included Batch: BAT-047, BAT-048, BAT-049 ## Scope - Included: - **BAT-047** (dcy-031 / OPS-005): 이벤트 발행 연결 신뢰성 — `publish_event` 1회 실패 시 강제 재연결+재시도(REQ-091), 발행 전용 연결 idle keepalive `process_data_events` 틱(REQ-092), DLQ 유입/emit 최종 실패 ERROR 경보(REQ-093), baseline 문서 정합(REQ-094). **M1 프로덕션(9e35467) 미반영 결함 해소 — idle 60초 후 첫 전이 이벤트 유실 제거.** - **BAT-048** (dcy-032): 체결가 0/None 백필 가드 + WARN 로깅 — `_on_filled`/`_on_partial_cancel`에서 avg_price=0/None 시 지정가 백필(REQ-095), 백필/unresolvable WARN 로그(reason 필드 포함)(REQ-096), 알람 능력 스파이크 산출물·후보 A/B/C 분석(REQ-097 — 알람 실제 구현은 dcy-033 이월). **슬롯 avg_price·원장 cost basis의 무경고 0오염 제거.** - **BAT-049** (dcy-033): 알람 시스템 1단계 — 알람 envelope 표준 JSON 스키마·`trex.alerts`(topic, durable=True) exchange 신규 선언(REQ-098/099), application `AlarmEmitter` + CRITICAL code 8종 배선(REQ-100), `runtime-flows.md` 알람 발신 흐름 기술(REQ-101). **기존 wire 계약(exchange/큐/라우팅) 불변 — 가산·격리 추가.** - **dcy-017 후속 운영자 런북** (docs-only, 2026-06-22): 지속 무데이터/죽은 종목 위생 절차 `docs/operations.md §7` 신규 기재 — pause/resume 시맨틱 검증·Step 1~3b 절차·고아 방지 순서 명시. 코드 변경 없음. - Excluded: - 이벤트 발행 영속 outbox(옵션 C) — M3 후보 - consumer(orb/tsl) idempotency 구현 — OQ-2(각 repo 추적) - 알람 수신·텔레그램 전달(trex-notifier) — **배포 의존성(아래 참조)** - INFO·WARN 알람 code 배선 — M3 이후 - 과거 0 기록 원장/슬롯 소급 정정 - Scope Note: - BAT-047은 origin/main(`b2165ea`) 기준 이미 푸시됨 — 이번 배포는 현재 local main HEAD(`5bdd720`) 전체를 싣는 체크아웃 갱신이다. - dcy-017 런북은 별도 Discovery/Batch를 생성하지 않은 docs-only 패치(커밋 `5bdd720`)이며 코드 변경 없음. ## 배포 의존성 및 순서 (필독) ### trex-notifier 선행 조건 `trex.alerts` exchange 선언은 데몬 기동 시 `declare_topology()`가 자동 수행한다. 알람 발행(`mandatory=False`)은 notifier 미연결 시 silent drop — **core 기동 자체는 notifier 없이도 안전**하다. 그러나 알람 수신·텔레그램 전달(trex-notifier)이 연결되지 않은 상태에서 CRITICAL 이벤트가 발생하면 알람이 유실(drop)된다. 따라서: 1. **trex-notifier 배포·바인딩 선행**: `trex.alerts`에 바인딩된 notifier 큐가 없으면 알람이 RabbitMQ에서 곧바로 drop된다. trex-notifier를 먼저 띄우고 `trex.alerts` 바인딩을 확인한 뒤 trex-core를 재시작한다. 2. **trex-core는 orb/tsl 이벤트 발행 경로의 중심**이다. trex-core 재시작 시간 동안 orb/tsl이 AMQP를 통해 trex-core로 전송하는 slot 의도(intent)가 일시 지연될 수 있다. 가능하면 거래 비활성 시간대(장 외)에 배포한다. ### 배포 순서 요약 ``` 1. (장 외 시간) trex-notifier 배포 및 trex.alerts 바인딩 확인 2. trex-core main 갱신 → pip install -e . (의존성 변경 없음 — 확인 후 생략 가능) → 데몬 재시작 3. (선택) trex-orb-v1 / trex-tsl-v1 재시작 — 코드 변경 없어 강제 아님, keepalive/재연결은 core 측 개선임 ``` ## Rollout Plan 1. **배포 전 준비** - 전체 테스트 green 확인: `python3 -m pytest -q` → **608 passed, 47 skipped** (현재 HEAD `5bdd720` 기준 검증 완료). - ruff clean 확인: `ruff check trex_core/ tests/` → `All checks passed!`. - BAT-047/048/049 `verification.md` 상태: BAT-047 approved, BAT-048 verified(팀장 confirm 완료), BAT-049 release-candidate(팀장 실인프라 e2e 포함 confirmed). - DB 스키마 변경 없음 — 신규 마이그레이션 없음. `apply_migrations` 기존 001~015 idempotent 재실행. - trex-notifier 배포·`trex.alerts` 바인딩 완료 확인 (선행 조건). 2. **배포 실행** (운영 환경 — bare .venv, instance_key=`trex-admin-daily-trading`) - `git pull` (또는 `git fetch && git checkout 5bdd720`) — local main을 프로덕션 체크아웃 경로에 반영. - 의존성 변경 여부 확인: `git diff 9e35467 HEAD -- pyproject.toml` → 변경 없으면 `pip install -e .` 생략 가능. - `trex-core-daemon` 재시작 (기존 PID 종료 후 신규 기동). - 기동 로그에서 `trex.alerts` exchange 선언 성공(`declare_topology` 완료) 확인. 3. **배포 후 검증** (아래 Verification Checklist 참조) ## Rollback Plan - **트리거**: 데몬 기동 실패 / `trex.alerts` exchange 선언 오류 / 이벤트 발행 경로 이상(재시도 루프 과다·DLQ 급증) / 체결 처리 이상(백필 WARN 폭증·슬롯 PENDING 고착) / 기존 서비스 연동 장애. - **절차**: 1. 데몬 정지. 2. 이전 커밋 체크아웃: `git checkout 9e35467` (REL-008 배포본 = M1 프로덕션 라인). 3. `trex-core-daemon` 재기동. 4. **DB 스키마 변경 없음** — 마이그레이션 없으므로 롤백 시 데이터 무손실. 5. `trex.alerts` exchange는 롤백 후에도 RabbitMQ에 남아있으나 발행자가 없어 무해. 필요 시 관리 UI에서 수동 삭제 가능(비필수). - **부분 완화**: `AlarmEmitter`는 best-effort 삼킴 패턴으로 기동·발행 경로가 알람 실패에 비차단. 알람 발행 오류만 발생하면 롤백 대신 notifier 재바인딩을 먼저 시도한다. ## Verification Checklist ### 배포 전 - [ ] 전체 테스트 green: `python3 -m pytest -q` → 608 passed, 47 skipped, 0 fail (HEAD `5bdd720`). - [ ] ruff clean: `ruff check trex_core/ tests/` → `All checks passed!`. - [ ] BAT-047 verification: Status=approved (confirmed 2026-06-19). - [ ] BAT-048 verification: Status=verified (confirmed 2026-06-21). - [ ] BAT-049 verification: Status=release-candidate (팀장 실인프라 e2e confirmed 2026-06-21). - [ ] trex-notifier 배포·`trex.alerts` 바인딩 완료 (선행). - [ ] DB 스키마 변경 없음 재확인 (마이그레이션 없음). - [ ] `docs/interface-contract.md` 변경 범위 확인: BAT-049 `trex.alerts` 섹션 추가(기존 섹션 diff=0). - [ ] **OPS-005 배포갭 인지**: 현재 프로덕션 M1(`9e35467`)은 idle keepalive·재시도 미포함 — 이번 배포가 해소. ### 배포 후 - [ ] **OPS-005 해소 확인 (BAT-047 핵심)**: 장 시작 후 orb/tsl → core idle 60초 이상 경과 후 첫 slot 전이 이벤트가 정상 수신되는지 확인. `ConnectionReset` / `StreamLostError` 로그 무재발 확인. `slot_filled` / `slot_entered` 이벤트가 orb/tsl에 도달하는지 확인. - [ ] 데몬 기동 로그: `declare_topology` 정상 완료, `trex.alerts` exchange 선언 포함, ERROR/CRITICAL/Traceback 0. - [ ] keepalive 동작 확인: 기동 후 60초 경과 시 `keepalive_tick` 호출 로그(또는 무오류) 확인. - [ ] 알람 발행 경로 확인: CRITICAL 이벤트 발생 시 `alarm.critical.trex-core` routing key로 `trex.alerts`에 발행되고 trex-notifier가 텔레그램 전달하는지 확인 (e2e 경로). - [ ] 체결 처리: 정상 체결 시 avg_price 백필 WARN 미발생 확인. KIS avg_price 이상 케이스 발생 시 WARN 로그 필드(slot_id/symbol/backfill_price/reason) 확인. - [ ] **AMQP wire 무회귀**: `trex.events`, `trex.intent.req`, `trex.slots.req`, `trex.control.req` exchange/큐/라우팅 형식 불변 확인. orb/tsl 연동 장애 없음 확인. - [ ] `trex-core report` 정상 동작 (REL-008 기능 회귀 없음). ## AMQP Wire 계약 무회귀 체크포인트 이번 릴리즈의 AMQP 변경 범위는 아래로 한정된다. orb/tsl 연동에 영향 없음을 명시한다. | 항목 | 변경 여부 | 근거 | |---|---|---| | `trex.events` exchange/토폴로지 | 불변 | BAT-047 Out of Scope 명시, verification NFR-1 PASS | | `trex.intent.req` 큐/라우팅 | 불변 | BAT-047/048/049 모두 불변 확인 | | `trex.slots.req` 큐/스키마 | 불변 | BAT-047/048/049 모두 불변 확인 | | `trex.control.req` 큐 | 불변 | 변경 없음 | | 슬롯 이벤트 페이로드 스키마 | 불변 | BAT-047 wire 무회귀 PASS | | `trex.alerts` exchange | **신규 추가** (가산·격리) | BAT-049 REQ-099. 기존 exchange/큐에 무영향. mandatory=False — unroutable silent drop | | `docs/interface-contract.md` | **trex.alerts 섹션 신규** (기존 섹션 diff=0) | BAT-049 REQ-098 AC-3 PASS | ## Release Execution Evidence - Release Status Transition Target: released ### Deployment Facts - Executed At: - 2026-06-25 - Completed At: - 2026-06-25 PASS (34700f3 on 5bdd720, PID 849009) - Environment: - 운영 (`/root/release` 또는 해당 경로, instance_key=`trex-admin-daily-trading`, 실거래 KIS 계좌 91254), bare .venv - Operator: - 사장님 - Revision or Artifact: - `9e35467`(REL-008 M1 현재 프로덕션) → **`34700f3` on `5bdd720`(REL-009 배포본)**. - BAT-047: `d182464`, BAT-048: `f1b649c`, BAT-049: `d4c5fcb`, dcy-017 런북: `5bdd720`. - Commands or Procedure: - `git pull` (또는 체크아웃 `5bdd720`) → `pip install -e .` (의존성 변경 시) → `trex-core-daemon` 재시작. - 마이그레이션: 기동 시 `apply_migrations` 자동 실행 (001~015 idempotent, 신규 없음). ### Profile-Specific Checks - app-service: - Health Endpoint: - N/A (데몬은 HTTP 표면 없음). "ready" = 워커 스레드 + RabbitMQ 토폴로지 선언(`trex.alerts` 포함) 완료, 로그 ERROR/Traceback 0. - Startup Log Fields: - PASS. `declare_topology` 완료 로그, `trex.alerts` exchange 선언 확인(AMQP 5연결 정상), ERROR/CRITICAL/Traceback 0. - Request Log Fields: - PASS. `slot_filled` 이벤트 정상 발행 확인. OPS-005 ConnectionReset 무재발 확인. 12:02 sell_filled 집행 정상. - Smoke or Manual Verification: - PASS. idle keepalive 동작, `slot_filled` 이벤트 발행 경로 정상. `trex.alerts` exchange 선언 5연결 확인. ### Rollback Outcome - Rollback Required: - No - Rollback Trigger: - none - Rollback Action: - none ### Release Decision Evidence - Released Decision: - released - Rationale: - 2026-06-25 배포 PASS. 2026-06-26 첫 거래일 OPS-005(RabbitMQ ConnectionReset) 무재발(0건) 확인. slot_filled 이벤트 정상 발행. 12:02 sell_filled 집행 정상. AMQP 5연결 정상. 롤백 없음. - Recorded By: - 팀장 - Recorded At: - 2026-06-26 ## Feedback Handoff ### M2 배포 + 첫 거래일 환류 (2026-06-26) - Observation Summary: - **배포**: 2026-06-25 PASS (34700f3 on 5bdd720, PID 849009). trex.alerts 선언, AMQP 5연결 정상. - **첫 거래일 검증**: OPS-005(RabbitMQ ConnectionReset) 무재발(0건) — 해소 확인. slot_filled 이벤트 정상 발행. 12:02 sell_filled 집행 정상. - **신규 발견**: slot_filled 이벤트 NO_ROUTE → DLQ 유입 확인. 라우팅키 `slot_filled_*._.{symbol}`(strategy_id 자리 `_`). 원인: `_emit_event`(execution.py:345/398)가 자산중심 슬롯(dcy-020, portfolio_id 단위, strategy_id 미보유) 기반 발행 → orb/tsl의 `trex.events.strategy.{strategy_id}` 바인딩과 불일치. 체결이벤트 채널(REQ-003) 미전달(전략은 slots RPC 폴링 주력이라 비치명적이나 채널 깨짐+DLQ 누적). - Discovery Input: - **★ slot_filled NO_ROUTE / fill→strategy 라우팅 재설계** (trex-core/P1/M3): 자산중심 모델에서 fill 이벤트를 strategy 바인딩으로 전달하는 라우팅 매핑이 없음. `_emit_event`의 라우팅키 생성 로직이 strategy_id를 참조하지 않아 orb/tsl의 `trex.events.strategy.{strategy_id}` 바인딩과 불일치 → NO_ROUTE → DLQ 누적. 자산중심 슬롯(portfolio_id 단위)에서 fill→strategy 라우팅 재설계 필요. 보드 등록됨. **교차 영향 사안**: wire 계약(exchange/라우팅키/바인딩) 변경 — 팀장 검토 후 Discovery 개시 필요. - **이벤트 발행 영속 outbox**: BAT-047 Out of Scope으로 이월됨(M3 후보). at-least-once 중복 이벤트 소비자 멱등성도 함께 추적. - **실 broker 통합 CI**: half-open/idle 60초/broker 재시작 시나리오는 현재 mock unit으로만 검증. `TREX_RABBITMQ_TEST_URL` 포함 통합 환경 구성(M3 후보). - **INFO·WARN 알람 code 배선**: CRITICAL 8종 이후 후속 범위(dcy-033 범위 경계 밖, M3 후보). - REQ Input: - none - Change Request Input: - none ## 미해결 / 잔여 위험 | 항목 | 수준 | 설명 | 해소 방안 | |---|---|---|---| | trex-notifier 미선행 시 알람 유실 | Medium | notifier 없이 core 배포 시 CRITICAL 알람 silent drop | 배포 순서 준수: notifier → core | | 지정가 백필 추정치 오차 | Medium | BAT-048 R-1: 실제 체결가 ≠ 지정가 가능. reason=avg_price_backfill WARN으로 추적 가능 | 운영자 WARN 발생 시 operations.md §5 절차 참조 | | fill_price_unresolvable 슬롯 고착 | Low | BAT-048 R-2: limit_price=None 시 PENDING 고착 → watchdog 탐지 필요 | 기존 stale_pending watchdog 동작, 운영자 개입 | | half-open/idle 실인프라 검증 | Low | BAT-047 mock unit 검증 — 실 broker 통합 skip (환경 미구성) | 배포 후 OPS-005 재발 모니터링으로 실증, M3 통합 CI | | NFR-2 publish 블록 최악 케이스 | Low | BAT-047: 1차 실패 시 원 socket_timeout(5.0s) 초과 가능 | 배포 후 모니터링, 필요 시 후속 REQ(publish timeout 강화) | | BAT-049 통합(실 RabbitMQ) 3건 | Low | declare_topology/mandatory=False/기존 topology 불변은 팀장 실인프라 e2e에서 확인됨(2026-06-21) | 배포 후 Smoke 체크포인트로 재확인 |