# req-019: ledger outbox durability + correlation_id 멱등 - Status: Implemented - Type: Exception - Priority: High - Owner: project maintainers ## Intent - portfolio-hub 일시 장애에도 원장 기록이 유실되거나 중복되지 않도록, 체결 시 원장 기록을 ledger outbox(work item)에 적재하고 재시도 루프가 portfolio-hub write 를 수행하며 correlation_id 로 멱등을 보장한다. ## Requirement - 체결 시 원장 기록을 ledger outbox(work item)에 적재하고, 재시도 루프가 portfolio-hub write 를 시도한다(trex-orbit `LedgerWriter`/recovery work item 패턴). 원장 기록은 슬롯 전이와 분리되어(전이 우선, 기록 eventually-consistent) 일시 장애에도 유실 없이 결국 기록된다. - 멱등 키 `correlation_id = {asset_id}:{order_id}:{execution_id}` 로 로컬 dedup 하고, portfolio-hub 중복/409 를 안전 처리해 동일 체결의 중복 원장이 0 이 되게 한다. - write 결과는 outcome 어휘 `completed` / `duplicate_completed` / `retryable_failure` / `conflict_blocked` 로 분류한다. `retryable_failure` 는 재시도하고, `conflict_blocked`(409)는 운영자 해소 대상으로 보류한다. ## Acceptance Criteria 1. Given 동일 execution_id 의 원장 기록을 2회 수행, When outbox 처리, Then 원장에 중복 거래가 생기지 않는다(correlation_id dedup, 중복 0). (NFR-2) 2. Given portfolio-hub write 가 일시 실패(retryable), When 재시도 루프, Then 결국 1건이 기록되고(`completed`) 슬롯 전이 일관성이 유지된다(무유실). (NFR-2) 3. Given portfolio-hub 가 409 충돌을 반환, When outbox 처리, Then outcome 이 `conflict_blocked` 로 표시되고 운영자 해소 전까지 자동 재시도로 무한 반복하지 않는다. 4. Given 이미 기록된 work item 재처리, When outbox 처리, Then outcome 이 `duplicate_completed` 로 분류되고 원장은 변하지 않는다. 5. Given 체결 직후 portfolio-hub 불가, When 슬롯 전이, Then 슬롯 전이는 기록 성공 여부와 무관하게 진행된다(분리). ## Impacted Area - Docs: docs/runtime-flows.md(ledger outbox/재시도 흐름), docs/data-model.md(outbox work item/멱등 키) — REQ-020 에서 정합 - Modules: 신규 ledger outbox(work item store) + 재시도 워커, REQ-018 의 fill 기록 경로가 직접 write 대신 outbox 적재로 연결 - Tests: 동일 execution_id 중복 0, 실패 주입 후 재시도로 결국 기록, 409→conflict_blocked, duplicate_completed, 전이/기록 분리 ## Notes - 출처: dcy-004 FR-3, FR-4, NFR-2, R-1(High, 원장 유실/중복). - BAT-004 의 broker handoff durability(REQ-014, 옵션 A)와 유사 구조이나 ledger 전용 outbox 다. 부분체결/정밀 세금 모델은 본 REQ 범위 밖(후속). - 멱등/무유실(NFR-2)을 별도 REQ 로 승격하지 않고 본 REQ 의 AC-1/AC-2 로 흡수한다. - conflict_blocked 운영자 해소 절차의 상세(알림/대시보드)는 운영 단계 후속. ## Change Log ### CHG-20260604-01 - Date: 2026-06-04 - Author: Justin Ko #### Change Summary - dcy-004 FR-3/FR-4 로부터 최초 생성(Proposed). NFR-2(멱등/무유실) 흡수. #### Intent - 원장 기록 무유실+멱등(outbox 재시도, correlation_id dedup, outcome 어휘)을 backlog 로 정규화. #### Acceptance Criteria Delta - Added: - 중복 0, 실패 재시도 결국 기록, conflict_blocked, duplicate_completed, 전이/기록 분리 5개 기준 - Updated: - 없음 - Removed: - 없음 #### Impacted Area Delta - Docs: - docs/runtime-flows.md, docs/data-model.md - Modules: - ledger outbox store + 재시도 워커 - Tests: - 멱등/무유실/conflict 주입 테스트 #### Delivery Trace Delta - Batches: - none - Releases: - none #### Revalidation Impact - Reverification Needed: no - Existing Implementation Invalidated: no - Status Recommendation: keep-current - Follow-up Action: confirm-req 후 batch 편성 #### Validation Plan - 동일 execution_id 중복 0 + 원장 실패 주입 후 재시도 결국 기록 + 409 conflict_blocked 테스트로 검증. ### CHG-20260604-02 - Date: 2026-06-04 - Author: Justin Ko #### Change Summary - Status 변경: Proposed -> Approved. confirm-req 승인 게이트 통과. #### Intent - ledger outbox durability 와 correlation_id 멱등 요구사항을 delivery input 으로 승인. #### Acceptance Criteria Delta - Added: - 없음 - Updated: - 없음 - Removed: - 없음 #### Impacted Area Delta - Docs: - 없음 - Modules: - 없음 - Tests: - 없음 #### Delivery Trace Delta - Batches: - none - Releases: - none #### Revalidation Impact - Reverification Needed: no - Existing Implementation Invalidated: no - Status Recommendation: Approved - Follow-up Action: batch 편성 #### Validation Plan - 기존 Acceptance Criteria 로 batch delivery 에서 검증. ### CHG-20260604-03 - Date: 2026-06-04 - Author: Justin Ko #### Change Summary - Status 변경: Approved -> Implemented. BAT-006 release-candidate verification evidence 기반. #### Intent - ledger outbox durability + correlation_id 멱등 + outcome 분류 구현과 검증 evidence 확보 후 구현 완료 상태로 동기화. #### Acceptance Criteria Delta - Added/Updated/Removed: 없음 #### Impacted Area Delta - Docs/Modules/Tests: 없음 (변경은 상태 전이만) #### Delivery Trace Delta - Batches: - BAT-006 (release-candidate) - Releases: - none #### Revalidation Impact - Reverification Needed: no - Existing Implementation Invalidated: no - Status Recommendation: Implemented - Follow-up Action: release 단계는 별도 진행 #### Validation Plan - AC-1: tests/application/test_execution_ledger.py::test_duplicate_execution_id_results_in_zero_duplicate_in_ledger. - AC-2: tests/application/test_execution_ledger.py::test_retryable_failure_then_eventually_completes + tests/application/test_ledger_outbox.py::test_writer_retries_until_success. - AC-3: tests/application/test_execution_ledger.py::test_conflict_blocks_without_auto_retry + tests/application/test_ledger_outbox.py::test_writer_marks_conflict_blocked_and_does_not_retry. - AC-4: tests/application/test_execution_ledger.py::test_duplicate_completed_when_reprocessing_terminal_item + test_duplicate_execution_id_results_in_zero_duplicate_in_ledger. - AC-5: tests/application/test_execution_ledger.py::test_slot_transition_proceeds_when_ledger_is_down. ### CHG-20260608-01 - Date: 2026-06-08 - Author: Justin Ko #### Change Summary - (change-req, dcy-015/issue #13) ledger outbox 에 영속/백오프/dead-letter 도입: 영속 오류(`LedgerPermanentError`)→터미널 `delivery_failed` 즉시; retryable→**지수 백오프+jitter**(`next_retry_at`)·**max_attempts** 초과 시 `delivery_failed`(dead-letter). `list_pending` 이 `next_retry_at > now` 행을 건너뜀(head-of-line 보호). status enum 에 `delivery_failed`·`next_retry_at` 컬럼 마이그레이션(007). 백오프 env 설정(base/cap/max-attempts). BAT-019 재구현·재검증 완료 → Implemented 유지. #### Intent - 미등록 포트폴리오 404 의 초당 무한 재시도(실측 930회) 제거 + 일시 오류의 절제된 백오프 + FIFO 잠식 방지. #### Acceptance Criteria Delta - Added: - 영속→`delivery_failed`(재시도 0), retryable→백오프+max_attempts→`delivery_failed`(dead-letter), `list_pending` 백오프 스킵, status enum/`next_retry_at` 마이그레이션, 백오프 env. #### Impacted Area Delta - Modules: `trex_core/application/ledger_outbox.py`(LedgerWriter 백오프/dead-letter, InMemory outbox), `trex_core/adapters/persistence/postgresql.py`(next_retry_at/status/list_pending skip), `trex_core/adapters/persistence/sql/007_*.sql`(신규), `trex_core/runtime/config.py`(env)+daemon 배선 - Tests: tests/application/test_ledger_outbox_backoff.py, tests/adapters/persistence/test_postgresql_ledger_outbox.py(delivery_failed/backoff skip), test_ledger_outbox.py/test_execution_ledger.py(백오프 정합) #### Delivery Trace Delta - Batches: BAT-019 - Releases: none #### Revalidation Impact - Reverification Needed: yes (BAT-019 에서 완료) - Existing Implementation Invalidated: yes (무한 재시도·백오프 부재 경로) - Status Recommendation: Implemented (change-req → BAT-019 재검증) - Follow-up Action: confirm-req-implemented(BAT-019). #### Validation Plan - BAT-019: permanent 즉시 터미널, backoff 스킵, max_attempts dead-letter, PG 마이그레이션 round-trip 단위/인프라 게이트. ### CHG-20260608-02 - Date: 2026-06-08 - Author: Justin Ko #### Change Summary - (change-req, dcy-018) `LedgerWriter` 에 `SnapshotStore` 주입 + option D write-back: `new`·`duplicate` outcome 의 `*_after`(절대 권위값)를 `update_cash_position` 으로 반영(OQ-2 b, best-effort — 실패해도 ledger 멱등·청산 무차단). 추정 현금 오버레이용 `LedgerOutbox.pending_cash_delta()`(미정산 행 cash delta 합) 추가(InMemory + PG). BAT-023 재구현·재검증 완료 → Implemented 유지. #### Intent - 체결 → portfolio-hub 권위 cash/quantity 를 스냅샷에 write-back(option D 활성화) + 공백 동안 추정 현금 산출 근거 제공. #### Acceptance Criteria Delta - Added: - LedgerWriter 가 new+duplicate 에 best-effort write-back(*_after None 이면 skip). `pending_cash_delta()` 로 미정산 추정 합 제공. #### Impacted Area Delta - Modules: `trex_core/application/ledger_outbox.py`(LedgerWriter write-back/`_writeback`/`trade_cash_delta`/`pending_cash_delta`), `trex_core/adapters/persistence/postgresql.py`(`pending_cash_delta` SQL), `trex_core/runtime/daemon.py`(SnapshotStore 주입) - Tests: tests/application/test_ledger_option_d.py, tests/adapters/persistence/test_postgresql_ledger_outbox.py::test_pending_cash_delta_on_pg #### Delivery Trace Delta - Batches: BAT-023 - Releases: none #### Revalidation Impact - Reverification Needed: yes (BAT-023 에서 완료) - Existing Implementation Invalidated: no (write-back/추정은 additive, 기존 outbox 멱등 불변) - Status Recommendation: Implemented (change-req → BAT-023 재검증) - Follow-up Action: confirm-req-implemented(BAT-023). #### Validation Plan - BAT-023: write-back new/duplicate/skip/best-effort + pending_cash_delta 단위 + PG.