Skip to content

fix: 릴리즈 리뷰 반영 (대화 메시지 시각을 DB 시계로 채번) - #273

Merged
chanwoo7 merged 3 commits into
developfrom
fix/release-review-conversation-db-clock
Sep 2, 2026
Merged

fix: 릴리즈 리뷰 반영 (대화 메시지 시각을 DB 시계로 채번)#273
chanwoo7 merged 3 commits into
developfrom
fix/release-review-conversation-db-clock

Conversation

@chanwoo7

@chanwoo7 chanwoo7 commented Sep 2, 2026

Copy link
Copy Markdown
Member

릴리즈 PR #272의 Codex P1 대응.

  • 문제: 앱 호스트 시계 채번은 다중 인스턴스에서 노드 간 오차로 잠금 순서와 어긋날 수 있음 → 시계 늦은 노드의 답장이 last_read_at보다 과거 시각을 받아 안읽음 배지에서 영구 누락 가능
  • 해결: 대화 잠금 획득 후 **DB 시계(SELECT NOW(3))**로 채번(구매자 전송·판매자 답장 공통) — DB가 단일 시계 소스이므로 인스턴스 수와 무관하게 잠금 순서 = 시각 순서 = 커밋 순서
  • API 표면(SDL) 변경 없음

릴리즈 PR #272 Codex P1 반영: 앱 호스트 시계로 채번한 created_at은
다중 인스턴스 배포에서 노드 간 시계 오차로 잠금 순서와 어긋날 수 있어,
시계가 늦은 노드의 답장이 last_read_at보다 과거 시각을 받아 안읽음
배지에서 영구 누락될 수 있다.

- 구매자 전송·판매자 답장 모두 대화 잠금 획득 후 DB 시계(SELECT NOW(3))로
  시각을 채번 — DB가 단일 시계 소스라 인스턴스 수와 무관하게
  잠금 순서 = 시각 순서 = 커밋 순서 유지
- 읽음 마커는 이미 메시지 created_at 파생이라 추가 변경 없음
@coderabbitai

coderabbitai Bot commented Sep 2, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Team

Run ID: 15ca998c-5fbe-43ee-a268-6cb92240decb

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions

github-actions Bot commented Sep 2, 2026

Copy link
Copy Markdown

🧹 knip — dead-code 리포트

Unused dependencies (1)
Duplicate exports (1)
전체 리포트
Unused dependencies (1)
graphql-ws  package.json:63:6
Duplicate exports (1)
KEYWORD_RANK_SNAPSHOT_SIZE|MAX_POPULAR_KEYWORDS_LIMIT  src/features/search/constants/search.constants.ts

청소 후보(오탐 가능) · 기준 docs/guide/architecture-conventions.md

@github-actions

github-actions Bot commented Sep 2, 2026

Copy link
Copy Markdown

🩺 NestJS Doctor — 90/100 (Excellent)

진단 302건 (error 0).

Category error warning info
architecture 0 0 9
correctness 0 153 0
performance 0 24 17
schema 0 0 86
security 0 13 0
architecture / security 상위 항목
  • info architecture/architecture/no-barrel-export-internals: Barrel file re-exports internal type 'IAuditLogRepository'.
  • warning security/security/no-exposed-env-vars: Direct 'process.env.NODE_ENV' access in 'AuthController'. Use ConfigService instead.
  • warning security/security/require-guards-on-endpoints: Endpoint 'start' has no @UseGuards() at class or method level.
  • warning security/security/require-guards-on-endpoints: Endpoint 'callback' has no @UseGuards() at class or method level.
  • warning security/security/require-guards-on-endpoints: Endpoint 'refresh' has no @UseGuards() at class or method level.
  • warning security/security/require-guards-on-endpoints: Endpoint 'logout' has no @UseGuards() at class or method level.
  • warning security/security/require-guards-on-endpoints: Endpoint 'sellerLogin' has no @UseGuards() at class or method level.
  • warning security/security/require-guards-on-endpoints: Endpoint 'sellerRefresh' has no @UseGuards() at class or method level.
  • warning security/security/require-guards-on-endpoints: Endpoint 'sellerLogout' has no @UseGuards() at class or method level.
  • warning security/security/require-guards-on-endpoints: Endpoint 'devIssueToken' has no @UseGuards() at class or method level.
  • info architecture/architecture/no-barrel-export-internals: Barrel file re-exports internal module '@/features/conversation/repositories/conversation.repository'.
  • info architecture/architecture/no-barrel-export-internals: Barrel file re-exports internal type 'ConversationRepository'.
  • info architecture/architecture/no-barrel-export-internals: Barrel file re-exports internal module '@/features/order/repositories/order.repository'.
  • info architecture/architecture/no-barrel-export-internals: Barrel file re-exports internal type 'OrderRepository'.
  • info architecture/architecture/no-barrel-export-internals: Barrel file re-exports internal module '@/features/product/repositories/product.repository'.

오탐 포함 가능 · 기준 docs/guide/architecture-conventions.md

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 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);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge 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 👍 / 👎.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

반영: 채번을 GREATEST(NOW(3), last_message_at+1ms, last_read_at+1ms)로 보정 — 잠금 아래라 대화 단위 단조성 보장, 기존 앱 시계 미래 마커·ms 동률 모두 커버. 미래 마커 회귀 spec 추가.

@github-actions

github-actions Bot commented Sep 2, 2026

Copy link
Copy Markdown

Coverage report

St.
Category Percentage Covered / Total
🟢 Statements 97.69% 5890/6029
🟢 Branches 93.16% 2315/2485
🟢 Functions 96.43% 1163/1206
🟢 Lines 98.28% 5363/5457

Test suite run success

1868 tests passing in 225 suites.

Report generated by 🧪jest coverage report action from a94857a

@codecov

codecov Bot commented Sep 2, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 71.42857% with 2 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
...nversation/repositories/conversation.repository.ts 71.42% 1 Missing and 1 partial ⚠️

📢 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 추가

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 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".

Comment on lines +487 to +488
FROM store_conversation
WHERE id = ${conversationId}`;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge 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 👍 / 👎.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

반영: 채번 SELECT에 FOR UPDATE 추가(잠금 조회로 최신 커밋 읽음, 이미 잡은 잠금이라 추가 대기 없음).

PR #273 Codex P2 반영: GREATEST 채번 SELECT가 일반(비잠금) 조회라
트랜잭션 초입 스냅샷을 읽어, 잠금 대기 중 커밋된 마커 갱신을 놓칠 수
있었다. FOR UPDATE 잠금 조회로 전환 — 최신 커밋 값을 읽고, row는 이미
본 트랜잭션이 잠근 상태라 추가 대기 없음.
@chanwoo7
chanwoo7 merged commit 15a8d5c into develop Sep 2, 2026
10 of 11 checks passed
@chanwoo7
chanwoo7 deleted the fix/release-review-conversation-db-clock branch September 2, 2026 18:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant