공유 가상 호가를 소비하는 지정가 부분 체결 엔진과 3초 주기 워커. 접수 경로와 계층 규약은 develop에 이미 있었고, 이번 PR은 비어 있던 “접수 이후” 구간을 채운다.
종목 조회 · 상태 갱신 · 세션 · 환율 준비 후 정책 검증. 두 경로가 여기서 갈린다.
호가를 읽어 Planner를 그대로 호출하되 아무것도 저장하지 않는다. 화면 숫자와 실제 체결 숫자가 어긋나지 않는 이유.
→ 계산 계층으로 직행 (워커 · 확정 거치지 않음)
주문 INSERT + 현금·수량 동결. 커밋 후
LimitOrderAcceptedEvent ★ 발행.
누구를 먼저 체결할지 정한다. 아래 계층은 “주문 하나”만 알고, 공정성은 전부 여기 책임.
@Scheduled(fixedDelay 3s) · 그룹 라운드로빈 ·
tick당 100건 / 그룹당 10건 / 시작 예산 5초
SET LOCAL lock_timeoutReason 반환 → 워커가 판단↑ 미리보기도 확정 계층도 같은 이 함수를 호출한다.
가상 호가 생성·갱신 → order_book. 워커와 같은 테이블을 두고 경합한다.
만료 주문 정리 + 동결 해제. 체결과 동결 자원을 나눠 쓴다.
계층 패턴 자체는 develop에 이미 있다(Service/TransactionService 쌍,
Propagation.NEVER 10곳). 그래서 리뷰의 질문은 “계층을 잘 나눴나”가 아니라
“기존 규약을 제대로 지켰나”이고, 위험은 아래 세 접점에 몰려 있다.
접수 트랜잭션이 워커에게 말을 거는 유일한 통로. AFTER_COMMIT으로 발행하고, 워커는 즉시 반영하지 않고 보류했다가 다음 후보 선정 경계에서 커서를 되돌린다.
호가 갱신 스케줄러와 체결 워커가 같은 테이블을 본다. bookVersion + revision을 근거 토큰에 담아 락 이후 재검증으로 방어.
접수가 잠근 현금·수량을 체결과 만료가 각각 푼다. 이번에 Account.settleReservedBuy, Holding.settleReservedSell가 신설됐다.
| 이전 (develop) | 이후 | |
|---|---|---|
| 접수 | 주문 INSERT + 동결 | 동일 + 이벤트 발행 ◇ |
| 체결 | 없음. 영원히 PENDING → 만료 | 워커가 호가 잔량을 소비 ★ |
| 미리보기 | UNSUPPORTED 고정 문구 |
Planner 재사용 ★ |
| 주문 종료 | 만료뿐 | 만료 또는 체결 |