fix: 릴리즈 리뷰 반영 (대화 메시지 시각을 DB 시계로 채번) - #273
Conversation
릴리즈 PR #272 Codex P1 반영: 앱 호스트 시계로 채번한 created_at은 다중 인스턴스 배포에서 노드 간 시계 오차로 잠금 순서와 어긋날 수 있어, 시계가 늦은 노드의 답장이 last_read_at보다 과거 시각을 받아 안읽음 배지에서 영구 누락될 수 있다. - 구매자 전송·판매자 답장 모두 대화 잠금 획득 후 DB 시계(SELECT NOW(3))로 시각을 채번 — DB가 단일 시계 소스라 인스턴스 수와 무관하게 잠금 순서 = 시각 순서 = 커밋 순서 유지 - 읽음 마커는 이미 메시지 created_at 파생이라 추가 변경 없음
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Team Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
🧹 knip — dead-code 리포트전체 리포트
|
🩺 NestJS Doctor — 90/100 (Excellent)진단 302건 (error 0).
architecture / security 상위 항목
|
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 4826dac0d8
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| // 정합 — 앱 호스트 시계는 다중 인스턴스 오차에 취약, 릴리즈 리뷰 반영). | ||
| await tx.$queryRaw`SELECT id FROM store_conversation WHERE id = ${args.conversationId} FOR UPDATE`; | ||
| const now = new Date(); | ||
| const now = await this.fetchDbNow(tx); |
There was a problem hiding this comment.
Keep timestamps monotonic across the clock-source cutover
When an existing conversation's last_read_at was stamped by an app node whose clock is ahead of the database, the first seller replies after this deployment receive an earlier NOW(3) value despite holding the row lock. Those replies are then permanently excluded by the repository's created_at > last_read_at unread predicates; equality can also occur because both columns have millisecond precision. Derive the new timestamp under the lock so it is strictly greater than the stored conversation watermark rather than using the database clock alone.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
반영: 채번을 GREATEST(NOW(3), last_message_at+1ms, last_read_at+1ms)로 보정 — 잠금 아래라 대화 단위 단조성 보장, 기존 앱 시계 미래 마커·ms 동률 모두 커버. 미래 마커 회귀 spec 추가.
Coverage report
Test suite run success1868 tests passing in 225 suites. Report generated by 🧪jest coverage report action from a94857a |
Codecov Report❌ Patch coverage is
📢 Thoughts on this report? Let us know! |
PR #273 Codex P1 반영: 배포 전 앱 시계가 DB보다 앞섰던 노드가 남긴 미래 last_read_at/last_message_at이 있으면, 컷오버 직후 NOW(3) 채번이 그보다 과거/동률이 되어 새 답장이 안읽음 판정(created_at > last_read_at) 에서 영구 누락될 수 있다. - 채번을 GREATEST(NOW(3), last_message_at+1ms, last_read_at+1ms)로 보정 — 잠금 아래라 대화 단위 단조성이 race 없이 보장되고, ms 동률 배제까지 해소 - 미래 마커 재현 회귀 spec 추가
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: a4a8ad2660
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| FROM store_conversation | ||
| WHERE id = ${conversationId}`; |
There was a problem hiding this comment.
Read timestamp markers from the locked row version
Although the prior future-marker issue was addressed, the new evidence is that this remains a nonlocking SELECT: in createBuyerMessages, the earlier findFirst establishes a MySQL REPEATABLE READ snapshot before lockOrCreateConversation may wait for a concurrent sender. After that sender commits, this query can still read the pre-wait last_message_at/last_read_at, so during the clock-cutover window two serialized sends can receive the same timestamp. The subscription contract discards list events by lastMessageAt (and lastReadAt for buyers), allowing the later preview to be dropped as stale. Make this a current read, such as by adding FOR UPDATE, so it uses the row version whose lock was acquired.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
반영: 채번 SELECT에 FOR UPDATE 추가(잠금 조회로 최신 커밋 읽음, 이미 잡은 잠금이라 추가 대기 없음).
PR #273 Codex P2 반영: GREATEST 채번 SELECT가 일반(비잠금) 조회라 트랜잭션 초입 스냅샷을 읽어, 잠금 대기 중 커밋된 마커 갱신을 놓칠 수 있었다. FOR UPDATE 잠금 조회로 전환 — 최신 커밋 값을 읽고, row는 이미 본 트랜잭션이 잠근 상태라 추가 대기 없음.
릴리즈 PR #272의 Codex P1 대응.
last_read_at보다 과거 시각을 받아 안읽음 배지에서 영구 누락 가능SELECT NOW(3))**로 채번(구매자 전송·판매자 답장 공통) — DB가 단일 시계 소스이므로 인스턴스 수와 무관하게 잠금 순서 = 시각 순서 = 커밋 순서