REQ-011 change-req(CHG-20260612-01)→구현→confirm-req-implemented(CHG-20260612-02)
전체 delivery. paper 한정 결정성 버그(프로덕션 무관).
- seq_store.py 신규: SeqStore 포트 + RedisSeqStore(원자 INCR, 재시작 단조·영속)
+ InMemorySeqStore(폴백/테스트). REQ-016 token_cache 패턴 재사용.
- paper.py: optional seq_store 주입. 미주입 시 기존 in-memory 동작 byte-for-byte 유지.
- config.py/daemon.py: TREX_PAPER_REDIS_URL → RedisSeqStore wiring
(key=trex:broker:paper:{instance_key}:order-seq).
- 런타임 Redis 오류는 전파(silent in-memory 폴백 금지 — 버그 재현 방지).
- tests: 재시작 단조성/correlation_id 비충돌/in-memory 폴백/fake redis INCR (5).
434 passed (+5), ruff clean.
REQ-011 Approved→Implemented, BAT-039 release-candidate. issue #4 close.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
3.5 KiB
3.5 KiB
BAT-039 Design
- Status: confirmed
- Batch: BAT-039
Architecture Summary
- paper 어댑터의 order seq 발번을 optional
SeqStore위로 옮긴다. 주입되면 영속 단조 카운터(RedisINCR)를, 미주입이면 기존 in-memory_seq를 사용한다(byte-for-byte 기존 동작 유지). SeqStore는 adapters/broker 레이어의 작은 포트로, REQ-016token_cache.py(Redis best-effort)와 같은 패턴·같은 레이어에 둔다. 도메인은 의존하지 않는다(NFR-1 불변).- 데몬은
TREX_PAPER_REDIS_URL이 설정되면RedisSeqStore를 만들어 paper 브로커에 주입한다.
Changed Areas
- Modules:
trex_core/adapters/broker/seq_store.py(신규):SeqStoreProtocol —next_seq() -> intRedisSeqStore(redis_url, *, key, redis_module=None)—INCR key로 원자·영속 단조 정수 반환.from_url(socket_connect_timeout=2)(token_cache 정합).InMemorySeqStore— thread-safe 카운터(테스트/명시 폴백)
trex_core/adapters/broker/paper.py—__init__(..., seq_store: Optional[SeqStore] = None);submit_order에서seq = self._seq_store.next_seq()(주입 시) /self._seq += 1(미주입)trex_core/runtime/config.py—PaperBrokerConfig.redis_url: str = ""+from_configTREX_PAPER_REDIS_URLtrex_core/runtime/daemon.py—_build_broker: paper +config.paper.redis_url→RedisSeqStore(url, key=f"trex:broker:paper:{config.instance_key}:order-seq")주입
- Tests:
tests/adapters/broker/test_paper_seq_store.py(신규)
- Docs: 없음(runtime-flows 흐름 불변 — 발번 영속화는 내부 결정성, 외부 흐름 동일)
Key Decisions
SeqStore.next_seq() -> int단일 메서드: paper 가 필요로 하는 것은 단조 정수뿐. order_id 포맷(PAPER-{seq:06d})은 paper 책임으로 유지. 포트는 최소.- Redis
INCR(원자·영속): GET/SET race 없이 단일 명령으로 단조 보장. 키 부재 시 0→1 로 시작해 첫 order_id 가PAPER-000001(기존과 동일 시작값). - 키 스코프 = instance_key:
trex:broker:paper:{instance_key}:order-seq. 인스턴스별 독립 카운터(다중 인스턴스 교차 방지). 단일 인스턴스(default)는 단일 키. - 폴백 의미 = 설정 부재만:
TREX_PAPER_REDIS_URL미설정 → seq store 미주입 → in-memory_seq(기존 동작). 런타임 Redis 오류는 전파(silent in-memory 폴백 금지 — 그 폴백이 곧 버그 재현). paper 는 테스트 하네스이므로 오류 전파가 안전. (R-1) - token_cache best-effort 와의 차이: 토큰 캐시는 miss/error → 재발급(무해)이라 best-effort 폴백. seq 는 error → 폴백이 곧 멱등 키 충돌이므로 best-effort 폴백을 적용하지 않는다.
Edge Cases
- seq store 미주입: 기존
self._seq += 1,PAPER-000001부터. AC-1~3 불변. - INCR 결과 > 999999:
f"{seq:06d}"는 최소 패딩 →PAPER-1000000정상 표기, 고유성 유지. - 재시작 모사(같은 key 공유 RedisSeqStore/InMemorySeqStore 인스턴스): 두 번째 PaperBroker 가 첫 인스턴스 이후 seq 로 시작 → 매도 order_id ≠ 직전 매수 → correlation_id 비충돌.
- 동시 submit(스레드):
INCR원자 /InMemorySeqStore는 lock 으로 보호.
Architecture Impact
- none (runtime-flows 흐름 불변. 발번 경로 내부 결정성 보강만, 외부 계약/흐름 동일)
Reference Doc Update Plan
- project-structure.md: none (adapters/broker 에 파일 1개 추가 — 구조 패턴 불변, token_cache 와 동급)
- runtime-flows.md: none