Skip to content

fix: 조회수 반영 시 유실 및 재처리 중복 방지 - #854

Merged
lsy1307 merged 4 commits into
developfrom
fix/853-prevent-view-count-loss-and-duplication
Oct 28, 2026
Merged

lsy1307 merged 4 commits into
developfrom
fix/853-prevent-view-count-loss-and-duplication

Conversation

@lsy1307

@lsy1307 lsy1307 commented Oct 26, 2026

Copy link
Copy Markdown
Contributor

관련 이슈

작업 내용

Redis에서 조회수를 가져와 삭제한 뒤 DB 반영에 실패하면 증가분이 유실됐습니다. DB 커밋 후 완료 확인 전에 작업이 중단된 경우에도 안전하게 재처리할 수 있도록, 반영할 이벤트와 DB 처리 기록을 각각 남깁니다.

  • 활성 조회수 Hash와 이벤트 Stream을 같은 Redis Cluster Hash Slot에 배치했습니다. Lua 스크립트로 증가분 조회, 이벤트 저장, 활성 카운터 제거를 원자적으로 처리합니다.
  • 이벤트에 flushId, 게시글 ID, 증가분을 기록합니다. DB의 flush_id UNIQUE 제약과 트랜잭션으로 처리 기록 저장 및 조회수 증가를 묶어 신규 이벤트만 반영합니다.
  • DB 커밋 후에만 XACK하고, 완료된 이벤트를 Stream에서 제거합니다. 미완료 이벤트는 XAUTOCLAIM으로 인계받아 같은 처리 번호로 재처리합니다.
  • 잘못된 이벤트가 정상 이벤트 처리를 막지 않도록 개별 처리하고, 조회수 내보내기에 실패해도 미완료 이벤트 소비는 실행합니다.
  • 기존 테스트를 새 저장 방식에 맞추고, 장애 재현 및 복구 검증 테스트를 추가했습니다.

검증

게시글 150개 중 50개에 각 100회씩 총 5,000회를 증가시키고, 반영 이벤트 50개 중 5개에 DB 업데이트 실패를 주입했습니다.

지표 기존 방식 변경 후
DB 실패 후 재처리 시 유실된 조회수 500회 0회
DB 커밋 후 ACK 누락 이벤트 재처리 시 중복 반영 발생 가능 0회
  • 커밋 후 ACK 누락 상황은 실제 DB 커밋 후 ACK를 생략하는 방식으로 재현했습니다. 운영체제에서 프로세스를 강제 종료한 테스트는 아닙니다.
  • 동시 증가와 내보내기, 동일 이벤트 동시 처리, ACK 실패 후 재처리, 삭제된 게시글, 잘못된 이벤트 및 pending 페이지 순회를 검증했습니다.
  • 전체 빌드 검증: 테스트 642개 중 640개 통과, 기존 연결 확인 테스트 2개 건너뜀. 전체 실행 시 테스트 MySQL 데이터 디렉터리만 임시 tmpfs로 사용했고, 공통 컨테이너 설정은 원복했습니다.
  • 컨벤션 정리 후 관련 테스트 17개 통과. 마지막 메서드 순서 및 테스트 변수 정리 후 compileTestJava flywayValidate 통과.
  • 커밋 분할 전후 최종 파일 내용이 동일함을 확인했습니다.

특이 사항

  • V63__create_view_count_flush.sql로 처리 기록 테이블을 추가합니다.
  • HRANDFIELD, XAUTOCLAIM을 사용하므로 Redis 6.2 이상이 필요합니다.
  • 배포 전에 기존 post:view:<postId> 카운터를 DB에 반영해야 합니다. 새 Hash로 자동 이관하지 않으므로 기존 버전과 새 버전의 조회수 처리 작업이 겹치지 않도록 전환해야 합니다.
  • 완료 이벤트 제거는 전용 소비자 그룹 하나를 사용하는 구조를 전제로 합니다.
  • DB 처리 기록은 중복 반영 방지를 위해 보존하며, 정리 정책은 포함하지 않았습니다.
  • Redis 자체 데이터 유실 및 실제 Cluster 장애 전환은 이번 검증 범위에 포함하지 않았습니다.

리뷰 요구사항 (선택)

  • 처리 기록 저장과 조회수 증가의 트랜잭션 경계, 커밋 후 ACK 순서를 확인해 주세요.
  • 배포 시 기존 카운터 반영 및 버전 전환 순서를 확인해 주세요.

@coderabbitai

coderabbitai Bot commented Oct 26, 2026 •

Copy link
Copy Markdown

Review in Change Stack →

No actionable comments were generated in the recent review.

Recent review info
Run configuration
  • Source mode: Auto
  • Repository: Unavailable
  • Branch: Unavailable
  • File: No configuration file from this repository applied
  • Commit: Unavailable
  • Layers: Repository UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 8c474d7d-35f8-456d-b973-7e7327cbf615
Commits

Reviewing files that changed from the base of the PR and between c4fe7f6 and 9f6916d.

Files selected for processing: 20
  • src/main/java/com/example/solidconnection/community/post/domain/ViewCountEvent.java
  • src/main/java/com/example/solidconnection/community/post/domain/ViewCountFlush.java
  • src/main/java/com/example/solidconnection/community/post/repository/ViewCountFlushRepository.java
  • src/main/java/com/example/solidconnection/community/post/repository/ViewCountStreamRepository.java
  • src/main/java/com/example/solidconnection/community/post/service/PostRedisManager.java
  • src/main/java/com/example/solidconnection/community/post/service/UpdateViewCountService.java
  • src/main/java/com/example/solidconnection/community/post/service/ViewCountConsumer.java
  • src/main/java/com/example/solidconnection/scheduler/UpdateViewCountScheduler.java
  • src/main/resources/db/migration/V63__create_view_count_flush.sql
  • src/main/resources/scripts/ackViewCount.lua
  • src/main/resources/scripts/claimViewCounts.lua
  • src/main/resources/scripts/flushViewCounts.lua
  • src/test/java/com/example/solidconnection/community/post/service/LegacyViewCountFailureTest.java
  • src/test/java/com/example/solidconnection/community/post/service/PostCommandServiceTest.java
  • src/test/java/com/example/solidconnection/community/post/service/PostQueryServiceTest.java
  • src/test/java/com/example/solidconnection/community/post/service/UpdateViewCountServiceTest.java
  • src/test/java/com/example/solidconnection/community/post/service/ViewCountRecoveryTest.java
  • src/test/java/com/example/solidconnection/concurrency/PostViewCountConcurrencyTest.java
  • src/test/java/com/example/solidconnection/scheduler/UpdateViewCountSchedulerTest.java
  • src/test/resources/application.yml

Included review availability: This review used your included allowance. Your plan provides up to 1 included review per hour; 0 remain after this review.

Walkthrough

조회수 집계는 Redis 카운터를 Stream 이벤트로 전환하고, DB 처리 이력으로 재처리 중복을 막는 구조로 바뀌었습니다.

  1. 이벤트 계약과 중복 방지: 이벤트 및 처리 이력 테이블을 추가하고, 신규 이벤트만 조회수에 반영합니다.
  2. Redis 카운터와 스트림: 활성 카운터 증가, 배치 플러시, 이벤트 읽기와 재인계를 구현합니다.
  3. 소비자와 복구: 스케줄러가 플러시 후 이벤트를 처리합니다. 실패 복구, 동시 처리, 중복 방지를 검증하는 테스트를 추가합니다.

Priority: Normal

Estimated code review effort: 4 (Complex) | ~45 minutes

Severity of issue fixed: Medium

Suggested reviewers: jaehee25

Merge Risk: Minimal · up to 9f691

No actionable merge-blocking issue remains. Existing view-count counters still need the documented pre-deployment transition.

Pre-merge checks: Passed 4 · Failed 1

Failed Failed checks: 1 warning

Check name Status Explanation Resolution
Docstring Coverage Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 46 functions across 18 files. (2 skipped: … Write docstrings for the functions missing them to satisfy the coverage threshold.
Passed Passed checks: 4 passed
Check name Status Explanation
Title check Passed 제목은 조회수 반영 과정의 유실과 재처리 중복을 방지하는 PR의 주요 변경 사항을 정확하고 간결하게 설명합니다.
Description check Passed 이슈, 작업 내용, 검증 결과, 특이 사항, 리뷰 요구사항을 모두 포함합니다. 데이터 마이그레이션, Redis 버전, 검증 범위와 제한 사항도 구체적으로 설명합니다.
Linked Issues check Passed 1. 이슈 #853 요구사항 구현
- ViewCountStreamRepository는 post:view:{views}:active Hash와 post:view:{views}:events Stream을 같은 hash slot에 둡니다.
- flushViewCounts.lua는 증가분 조회, 이벤트 저장, 활성 카운터 삭제를 하나의 Lua 실…
Out of Scope Changes check Passed 1. 변경 범위
- 추가된 이벤트 도메인, 처리 기록 엔티티와 저장소, Redis Stream 저장소, 소비자, 스케줄러 변경, 마이그레이션 및 Lua 스크립트는 모두 이슈 #853의 조회수 유실 및 중복 반영 방지에 직접 연결됩니다.
- 기존 테스트 수정과 복구·경합 테스트 추가는 해당 동작을 검증하기 위한 지원 변경입니다.
- 삭제된 게시글의 무동…
Full details: Linked Issues check

Explanation

  1. 이슈 #853 요구사항 구현
    • ViewCountStreamRepository는 post:view:{views}:active Hash와 post:view:{views}:events Stream을 같은 hash slot에 둡니다.
    • flushViewCounts.lua는 증가분 조회, 이벤트 저장, 활성 카운터 삭제를 하나의 Lua 실행으로 처리합니다.
    • UpdateViewCountService.apply는 @Transactional 경계에서 flush_id 처리 기록과 조회수 증가를 수행합니다. ViewCountFlushRepository와 마이그레이션은 이벤트별 고유 제약을 제공합니다.
    • ViewCountConsumer는 트랜잭션 메서드가 반환된 뒤 acknowledge를 호출합니다. XAUTOCLAIM 기반 재인계는 동일한 flushId를 유지합니다.
    • 실패 복구, 커밋 후 ACK 누락, 동일 이벤트 동시 처리, 증가·flush 경합, 잘못된 이벤트, pending 이벤트, 삭제된 게시글에 대한 테스트가 추가되었습니다.
    • 현재 PR 설명은 기존 post:view:&lt;postId&gt; 카운터를 전환 전에 DB에 반영해야 한다는 절차와 검증 조건을 문서화합니다.

코드와 테스트 요약은 이슈 #853의 모든 코딩 요구사항을 충족하는 근거를 제공합니다. Redis 데이터 유실과 실제 프로세스 강제 종료 및 Cluster failover를 검증하지 않은 사실은 이슈가 명시한 검증 한계와 일치합니다.

Full details: Out of Scope Changes check

Explanation

  1. 변경 범위
    • 추가된 이벤트 도메인, 처리 기록 엔티티와 저장소, Redis Stream 저장소, 소비자, 스케줄러 변경, 마이그레이션 및 Lua 스크립트는 모두 이슈 #853의 조회수 유실 및 중복 반영 방지에 직접 연결됩니다.
    • 기존 테스트 수정과 복구·경합 테스트 추가는 해당 동작을 검증하기 위한 지원 변경입니다.
    • 삭제된 게시글의 무동작 처리와 잘못된 이벤트 격리는 이벤트 소비를 계속하기 위한 관련 동작입니다.

이슈 #853과 연결되지 않은 변경은 확인되지 않았습니다.

Full details: Docstring Coverage

Explanation

Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 46 functions across 18 files. (2 skipped: 2 unsupported.)

Resolution

Write docstrings for the functions missing them to satisfy the coverage threshold.

Finishing Touches: 💡 1
📝 Generate docstrings: 💡
  • Commit to this branch
  • Create a new PR
🧪 Generate unit tests (beta)
  • Commit to this branch
  • Create a new PR
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

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.

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 26, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-26T12:35:09.889043Z 9f6916d PR opened
ℹ️ 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" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@lsy1307 lsy1307 self-assigned this Oct 26, 2026
@lsy1307 lsy1307 added the 버그 Something isn't working label Oct 26, 2026

@Hexeong Hexeong left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

고생하셨습니다!

Stream 도입 과정에 명확한 이유가 있네요..! 단순 카운터 방식은 증가분을 먼저 삭제하면 DB 반영 실패 시 유실되고, DB 반영 후 삭제하면 그 사이 들어온 새 조회수까지 지울 수 있다는 점 확인했습니다.

이번 PR은 Lua로 증가분을 Stream 이벤트에 옮긴 뒤 카운터를 제거해 배치 인계 구간의 유실 가능성을 막고, 처리 ID로 재처리 시 중복 반영도 방지하는 걸로 보여요! 이 점에서 Stream 도입 이유가 납득됩니다~

@lsy1307
lsy1307 merged commit 7fcc826 into develop Oct 28, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

버그 Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

fix: 조회수 반영 시 유실 및 재처리 중복 방지

2 participants