From 3aa61c94b832bdaa7c5b12051720083e14265f64 Mon Sep 17 00:00:00 2001 From: Klaus Date: Fri, 10 Jul 2026 06:05:21 +0900 Subject: [PATCH] =?UTF-8?q?docs(home):=20AI=20=EC=8A=A4=EB=83=85=EC=83=B7?= =?UTF-8?q?=20fallback=20=EB=AC=B8=EC=84=9C=EB=A5=BC=20=EC=A0=95=EB=A6=AC?= =?UTF-8?q?=ED=95=9C=EB=8B=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../plan-task.md | 27 ++++++++++--------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/docs/20260709_메인_홈_추천_AI캐릭터_스냅샷/plan-task.md b/docs/20260709_메인_홈_추천_AI캐릭터_스냅샷/plan-task.md index ded04b7e..e060c54d 100644 --- a/docs/20260709_메인_홈_추천_AI캐릭터_스냅샷/plan-task.md +++ b/docs/20260709_메인_홈_추천_AI캐릭터_스냅샷/plan-task.md @@ -158,21 +158,21 @@ - [x] **Task 5.1: AI 캐릭터 fallback refresh service 추가** - 파일 경로: - - Create: `src/main/kotlin/kr/co/vividnext/sodalive/v2/recommendation/application/AiCharacterSnapshotFallbackService.kt` + - Create: `src/main/kotlin/kr/co/vividnext/sodalive/v2/recommendation/application/AiCharacterSnapshotFallbackPort.kt` - Modify: `src/main/kotlin/kr/co/vividnext/sodalive/v2/recommendation/application/RecommendationSnapshotRefreshService.kt` - - Test: `src/test/kotlin/kr/co/vividnext/sodalive/v2/recommendation/application/AiCharacterSnapshotFallbackServiceTest.kt` + - Test: `src/test/kotlin/kr/co/vividnext/sodalive/v2/recommendation/application/RecommendationSnapshotFallbackServiceTest.kt` - RED: 최신 `AI_CHARACTER` 스냅샷이 없을 때 fallback service가 AI refresh를 요청하고, refresh 완료 후 저장된 스냅샷을 다시 조회하도록 실패 테스트를 작성한다. 이미 스냅샷이 있으면 refresh를 요청하지 않는 테스트도 추가한다. - - 실패 확인: `./gradlew test --tests kr.co.vividnext.sodalive.v2.recommendation.application.AiCharacterSnapshotFallbackServiceTest` + - 실패 확인: `./gradlew test --tests kr.co.vividnext.sodalive.v2.recommendation.application.RecommendationSnapshotFallbackServiceTest` - GREEN: fallback service를 추가해 double-check 조회, AI 단일 refresh 호출, refresh 후 재조회 흐름을 구현한다. - REFACTOR: fallback service는 점수 계산/상세 조립을 직접 하지 않고 snapshot 조회와 refresh orchestration만 담당한다. - 기대 결과: 스냅샷 없음 fallback이 scheduler와 같은 저장 로직을 재사용한다. - [x] **Task 5.2: fallback lock, timeout, 백그라운드 완료 정책 구현** - 파일 경로: - - Modify: `src/main/kotlin/kr/co/vividnext/sodalive/v2/recommendation/application/AiCharacterSnapshotFallbackService.kt` - - Test: `src/test/kotlin/kr/co/vividnext/sodalive/v2/recommendation/application/AiCharacterSnapshotFallbackServiceTest.kt` + - Modify: `src/main/kotlin/kr/co/vividnext/sodalive/v2/recommendation/application/RecommendationSnapshotFallbackService.kt` + - Test: `src/test/kotlin/kr/co/vividnext/sodalive/v2/recommendation/application/RecommendationSnapshotFallbackServiceTest.kt` - RED: lock 대기 최대 300ms, 홈 API refresh 완료 대기 최대 1,500ms, timeout 시 현재 요청 빈 결과 반환, timeout 후 background refresh 계속 진행, lock이 잡혀 있는 동안 후속 요청은 중복 refresh를 시작하지 않는 테스트를 작성한다. - - 실패 확인: `./gradlew test --tests kr.co.vividnext.sodalive.v2.recommendation.application.AiCharacterSnapshotFallbackServiceTest` + - 실패 확인: `./gradlew test --tests kr.co.vividnext.sodalive.v2.recommendation.application.RecommendationSnapshotFallbackServiceTest` - GREEN: refresh 작업을 worker thread에서 실행하고 해당 thread 안에서 Redisson lock을 획득/해제한다. 홈 요청 thread는 future 완료를 최대 1,500ms만 기다리고, timeout 시 future를 cancel하지 않는다. - REFACTOR: 실제 구현에서 `TaskExecutor`, `CompletableFuture`, 또는 기존 async 인프라 중 하나만 사용한다. 테스트에서는 deterministic executor/fake clock을 사용해 sleep 기반 테스트를 피한다. - 기대 결과: 첫 요청 timeout 후에도 refresh가 완료되면 다음 요청은 저장된 스냅샷을 사용하고, 동시 요청은 중복 refresh를 만들지 않는다. @@ -203,12 +203,12 @@ - [x] **Task 6.2: fallback/refresh 관측 로그 추가** - 파일 경로: - - Modify: `src/main/kotlin/kr/co/vividnext/sodalive/v2/recommendation/application/AiCharacterSnapshotFallbackService.kt` + - Modify: `src/main/kotlin/kr/co/vividnext/sodalive/v2/recommendation/application/RecommendationSnapshotFallbackService.kt` - Modify: `src/main/kotlin/kr/co/vividnext/sodalive/v2/recommendation/application/RecommendationSnapshotRefreshService.kt` - - Test: `src/test/kotlin/kr/co/vividnext/sodalive/v2/recommendation/application/AiCharacterSnapshotFallbackServiceTest.kt` + - Test: `src/test/kotlin/kr/co/vividnext/sodalive/v2/recommendation/application/RecommendationSnapshotFallbackServiceTest.kt` - Test: `src/test/kotlin/kr/co/vividnext/sodalive/v2/recommendation/application/RecommendationSnapshotRefreshServiceTest.kt` - RED: fallback refresh 실행/성공/실패/timeout, lock 획득 성공/실패, AI 스냅샷 저장 수 로그 이벤트가 남는지 실패 테스트를 작성한다. - - 실패 확인: `./gradlew test --tests kr.co.vividnext.sodalive.v2.recommendation.application.AiCharacterSnapshotFallbackServiceTest --tests kr.co.vividnext.sodalive.v2.recommendation.application.RecommendationSnapshotRefreshServiceTest` + - 실패 확인: `./gradlew test --tests kr.co.vividnext.sodalive.v2.recommendation.application.RecommendationSnapshotFallbackServiceTest --tests kr.co.vividnext.sodalive.v2.recommendation.application.RecommendationSnapshotRefreshServiceTest` - GREEN: 기존 프로젝트 관례대로 신규 metric dependency 없이 구조화 로그를 남긴다. - REFACTOR: 로그 event key는 검색 가능한 snake_case로 고정하고, 예외 로그는 홈 전체 실패를 유발하지 않는 fallback 실패와 실제 refresh 실패를 구분한다. - 기대 결과: 운영에서 fallback 빈 배열 원인을 timeout/lock/실패/데이터 없음으로 구분할 수 있다. @@ -228,7 +228,7 @@ - 실행 명령: - `./gradlew test --tests kr.co.vividnext.sodalive.v2.recommendation.domain.RecommendationScorePolicyTest --tests kr.co.vividnext.sodalive.v2.recommendation.domain.RecommendationSnapshotWindowPolicyTest` - `./gradlew test --tests kr.co.vividnext.sodalive.v2.recommendation.adapter.out.persistence.DefaultHomeRecommendationQueryRepositoryTest` - - `./gradlew test --tests kr.co.vividnext.sodalive.v2.recommendation.application.RecommendationSnapshotRefreshServiceTest --tests kr.co.vividnext.sodalive.v2.recommendation.application.AiCharacterSnapshotFallbackServiceTest --tests kr.co.vividnext.sodalive.v2.recommendation.application.HomeRecommendationQueryServiceTest` + - `./gradlew test --tests kr.co.vividnext.sodalive.v2.recommendation.application.RecommendationSnapshotRefreshServiceTest --tests kr.co.vividnext.sodalive.v2.recommendation.application.RecommendationSnapshotFallbackServiceTest --tests kr.co.vividnext.sodalive.v2.recommendation.application.HomeRecommendationQueryServiceTest` - `./gradlew test --tests kr.co.vividnext.sodalive.v2.api.home.HomeRecommendationControllerTest` - [x] **Task 7.2: 전체 회귀와 문서 검증** @@ -257,11 +257,11 @@ - 2026-07-10: 구현 검증으로 산식/윈도우 focused 테스트 `RecommendationScorePolicyTest`, `RecommendationSnapshotWindowPolicyTest`를 실행해 `BUILD SUCCESSFUL`을 확인했다. - 2026-07-10: AI 캐릭터 스냅샷 집계 focused 테스트 `DefaultHomeRecommendationQueryRepositoryTest`를 실행해 전날 UTC window, 팔로우 증가량, 후보 제외, top 20/동점 정렬이 `BUILD SUCCESSFUL`임을 확인했다. -- 2026-07-10: refresh/fallback/query focused 테스트 `RecommendationSnapshotRefreshServiceTest`, `AiCharacterSnapshotFallbackServiceTest`, `HomeRecommendationQueryServiceTest`를 실행해 section lock, 300ms lock wait, 1,500ms home wait, timeout 후 background 완료, double-check, fallback 연결이 `BUILD SUCCESSFUL`임을 확인했다. +- 2026-07-10: refresh/fallback/query focused 테스트 `RecommendationSnapshotRefreshServiceTest`, `RecommendationSnapshotFallbackServiceTest`, `HomeRecommendationQueryServiceTest`를 실행해 section lock, 300ms lock wait, 1,500ms home wait, timeout 후 background 완료, double-check, fallback 연결이 `BUILD SUCCESSFUL`임을 확인했다. - 2026-07-10: API 회귀 focused 테스트 `HomeRecommendationControllerTest`, `HomeRecommendationResponseTest`를 실행해 공개 응답 스키마 유지가 `BUILD SUCCESSFUL`임을 확인했다. - 2026-07-10: 전체 검증으로 `./gradlew ktlintCheck`, `./gradlew test`, `./gradlew tasks --all`, `git diff --check`를 실행했다. `./gradlew test`는 120초 제한에서 1회 timeout되어 600초 제한으로 재실행했고, 모든 명령이 최종 `BUILD SUCCESSFUL` 또는 무출력 통과했다. - 2026-07-10: 리뷰 게이트에서 발견된 fallback 조건/단일 실행/section lock 해제 시점 이슈를 수정했다. 최신 스냅샷 존재 여부는 요청 page가 아니라 `offset=0, limit=1` 존재 확인으로 분리했고, fallback refresh는 단일 in-flight future로 제한했으며, scheduler AI section lock은 트랜잭션 완료 후 해제되도록 보강했다. -- 2026-07-10: 수정 후 `HomeRecommendationQueryServiceTest`, `AiCharacterSnapshotFallbackServiceTest`, `RecommendationSnapshotRefreshServiceTest` focused 테스트를 실행해 `BUILD SUCCESSFUL`을 확인했다. 이어서 domain/repository/API focused 테스트, `./gradlew ktlintCheck`, `git diff --check`, `./gradlew test`, `./gradlew tasks --all`을 재실행했고 모두 최종 통과했다. +- 2026-07-10: 수정 후 `HomeRecommendationQueryServiceTest`, `RecommendationSnapshotFallbackServiceTest`, `RecommendationSnapshotRefreshServiceTest` focused 테스트를 실행해 `BUILD SUCCESSFUL`을 확인했다. 이어서 domain/repository/API focused 테스트, `./gradlew ktlintCheck`, `git diff --check`, `./gradlew test`, `./gradlew tasks --all`을 재실행했고 모두 최종 통과했다. - 2026-07-10: 후속 리뷰 응답으로 AI 캐릭터 팔로우 증가량 집계에서 비활성 follower 계정을 제외하도록 `creator_following.member_id`의 `member.is_active = true` 조건을 추가했다. `DefaultHomeRecommendationQueryRepositoryTest`에 활성 follow row이지만 follower 계정이 비활성인 fixture를 추가했고, `./gradlew test --tests kr.co.vividnext.sodalive.v2.recommendation.adapter.out.persistence.DefaultHomeRecommendationQueryRepositoryTest`, `./gradlew ktlintCheck`, `git diff --check`가 모두 통과했다. - 2026-07-10: 후속 리뷰 응답으로 `HomeRecommendationQueryService.findAiCharacterRecommendations`의 `Propagation.NOT_SUPPORTED`를 제거해 홈 API 트랜잭션 경계를 유지하도록 되돌렸다. 또한 AI 캐릭터 스냅샷 window를 `endExclusiveUtc` 기반 half-open 범위로 정리해 `cm.created_at < :windowEndExclusive`, `cf.created_at < :windowEndExclusive`, `cc.created_at < :windowEndExclusive`로 집계되도록 수정했다. @@ -270,4 +270,5 @@ - 2026-07-10: 전체 회귀 재검증 중 `HomeRecommendationControllerTest.shouldExposeCreatorIdOnAiCharacterRecommendations`가 전체 실행에서 최신 AI 스냅샷 충돌로 1회 실패했다. 테스트 fixture의 AI 스냅샷 시각을 fallback window보다 최신인 `2026-12-31T23:59:59`로 고정해 테스트 독립성을 보강했고, `./gradlew test --tests 'kr.co.vividnext.sodalive.v2.api.home.HomeRecommendationControllerTest.shouldExposeCreatorIdOnAiCharacterRecommendations' --tests 'kr.co.vividnext.sodalive.v2.api.home.HomeRecommendationControllerTest.shouldReturnTwentyAiCharactersOnHomeRecommendations'`가 `BUILD SUCCESSFUL`임을 확인했다. - 2026-07-10: 최종 검증으로 `./gradlew test`, `./gradlew ktlintCheck`, `git diff --check`를 재실행했다. 전체 테스트 1125건 포함 모든 명령이 최종 통과했다. -- 2026-07-10: 리뷰 게이트에서 PRD inclusive window 문구와 fallback post-refresh 재조회 트랜잭션 가시성 이슈가 차단으로 지적됐다. PRD/plan-task의 기간 표현을 `windowStartUtc <= t < windowEndExclusiveUtc` half-open 기준으로 정렬했고, `AiCharacterSnapshotFallbackService`의 snapshot read를 `PROPAGATION_REQUIRES_NEW` read-only `TransactionTemplate`으로 분리해 caller read transaction과 독립된 DB read가 되도록 보강했다. +- 2026-07-10: 리뷰 게이트에서 PRD inclusive window 문구와 fallback post-refresh 재조회 트랜잭션 가시성 이슈가 차단으로 지적됐다. PRD/plan-task의 기간 표현을 `windowStartUtc <= t < windowEndExclusiveUtc` half-open 기준으로 정렬했고, active 공통 `RecommendationSnapshotFallbackService`의 snapshot read를 `PROPAGATION_REQUIRES_NEW` read-only `TransactionTemplate`으로 분리해 caller read transaction과 독립된 DB read가 되도록 보강했다. +- 2026-07-10: 후속 정리로 삭제된 `AiCharacterSnapshotFallbackServiceTest` 참조를 active `RecommendationSnapshotFallbackServiceTest`로 갱신했고, 비활성 legacy `AiCharacterSnapshotFallbackService` 구현을 제거한 뒤 `AiCharacterSnapshotFallbackPort`만 별도 파일로 유지했다.