feat(spec-006): 사용자 통계 캐시 stale-cache 해소 (통계 버전 기반 캐시 키)#4
Open
simhani1 wants to merge 7 commits into
Open
Conversation
통계 버전 기반 캐시 키로 stale cache 해소. 배치 완료 후에만 버전 전환(US2)해 중간 상태 캐싱 방지. 전수 evict 없이 TTL 자연 만료. 실제 구현(challenge-avg Caffeine 로컬, progressCalculation 배치)과의 차이는 Assumptions에 기록. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- Q1: 다중 인스턴스 가정, 공유 분산 캐시(Redis) + 공유 버전 소스 - Q2: 통계 버전 = 직전 완료 배치의 데이터 기준일(영업일) - Q3: 콜드스타트(완료 배치 0건)는 오늘 기준일 잠정 버전 사용 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- 버전 = 직전 완료 progressCalculation JobLog endTime의 기준일(DB 파생, 헌법 II) - 캐시 Caffeine→Redis 공유 이관, 키=memberId:version, TTL 25h - single-flight=lockingRedisCacheWriter+sync, 실패 비캐싱 - 헌법 게이트 전원 통과, 신규 의존성/테이블 없음 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
조기 최적화 회피. 버전 해석을 인덱스된 MAX(endTime) 1건 읽기로 매 요청 직접 수행. (job_type, end_time) 복합 인덱스 추가. stale 창·동시조회 레이스 경계가 메모 만료 instant → DB 커밋 instant(마이크로초)로 축소. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Setup→Foundational(버전해석+Redis 버전키 캐시)→US1(SC-001 MVP) →US2(SC-002)→US3(SC-003/005)→Polish(SC-004/FR-007/콜드스타트/게이트). 신규 의존성 없음. 통합테스트는 IntegrationTest(MySQL+Redis) 확장. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Foundational+US1(MVP): JobLog (job_type,end_time) 인덱스 + QueryDSL MAX(endTime) 버전 쿼리, StatisticsVersionProvider(매 요청 DB 파생, Asia/Seoul yyyy-MM-dd, 콜드스타트=오늘), CacheConfig Caffeine→Redis 이관(TTL 25h, GenericJackson2Json, lockingRedisCacheWriter), MemberStatisticsCacheService(@Cacheable key=memberId:version, sync), MemberServiceImpl 버전 위임(자기호출 회피). 테스트: SC-001(버전 전환 재계산) + FR-008(결정성) 통합테스트, StatisticsCacheTestSupport 헬퍼. MemberIntegrationTest 회귀 2건 새 설계(버전키/분산캐시)에 맞게 갱신. verify: ./gradlew check GREEN 173 tests, verifySecretLogScan clean. review: 헌법 7원칙 PASS, P1=0. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ng 단일화 US2/US3/Polish 인수 기준 박제(6개 통합 테스트): - SC-002 진행중(완료 row 부재) 중간상태 미캐싱 - SC-003 버전 전환 시 구 키 잔존(evict 0회) - SC-004 single-flight Flask 1회 수렴 - SC-005 캐시 키 TTL 25h 근사 - FR-007 실패 비캐싱·재시도 가능 - 콜드스타트 오늘 기준일 잠정 버전 P3-2: PlanetrushApplication 중복 @EnableCaching 제거(RedisConfig 단일화). verify: ./gradlew check GREEN 179 tests(인수 8/8), verifySecretLogScan clean. review: 전체 헌법 7원칙 PASS, P1=0/P2=0. claude-review.md(헌법 VI) 생성. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
개요
마이페이지 통계(
GET /api/v1/members/mypage) 캐시가 24h TTL 동안 stale 해지는 문제를 캐시 키에 "통계 버전"을 포함해 해소한다. 버전은 직전에 완료된progressCalculation배치의 기준일(yyyy-MM-dd, Asia/Seoul)이며, 배치가 완료된 후에만 전환되어 자정 직후 중간 상태가 새 버전으로 캐싱되지 않는다.Spec:
specs/006-statistics-cache-versioning/(spec·plan·research·data-model·contracts·tasks)핵심 설계
JobLog에서MAX(endTime) where jobType='progressCalculation' and endTime is not null(QueryDSL 스칼라). 완료 0건이면 오늘(콜드스타트). 매 요청 DB 파생(버전 캐시 없음) → stale 창 0 수렴finish()(endTime set) 후에만 JobLog 저장 → 진행 중 row 미영속 → 새 날 배치 완료 전까지 버전이 어제로 유지(중간 상태 미캐싱)challenge-avg::{memberId}:{version}, TTL 25hlockingRedisCacheWriter+@Cacheable(sync=true)→ 동시 첫 조회 Flask 1회 수렴build.gradle의존성 0, 신규 테이블 0 (JobLog에(job_type, end_time)인덱스만 추가)검증 (Testcontainers MySQL+Redis)
./gradlew checkGREEN — 179 tests, 0 failverifySecretLogScanclean (헌법 V)should_recompute_on_first_read_after_version_transition_and_hit_within_same_versionshould_not_cache_intermediate_state_while_batch_in_progressshould_keep_old_version_key_without_evict_on_version_transitionshould_converge_flask_call_to_once_on_concurrent_first_readsshould_set_ttl_around_25h_on_cache_keyshould_not_cache_failure_and_allow_retry_on_next_requestshould_return_deterministic_version_for_same_db_stateshould_use_today_as_version_on_cold_start_with_no_completed_batch헌법 7원칙
IntegrationTest(MySQL+Redis) 확장, Flask만 @MockBeanMAX(endTime)스칼라 집계듀얼 AI 리뷰 (헌법 VI)
specs/006-statistics-cache-versioning/_harness/claude-review.md(최종 권고: ✅ MERGE, P1=0/P2=0)잔여 백로그 (머지 비차단 P3)
@class결속(GenericJackson2JsonRedisSerializer) — DTO 리네임/이동 시 TTL 내 잔존 캐시 역직렬화 500 위험. 후속 "역직렬화 실패→miss" 복원력 과제로 추적.🤖 Generated with Claude Code