Skip to content

test: RejectedAttemptRaceTest 가 추가된 날부터 실패 — avg_sync_rate 기대 70.67, 실제 42.0 (main CI 빨간불) #846

Closed

Description

@Khyojae

증상

RejectedAttemptRaceTest.completionAndReport_endToEnd(실 MySQL, race 프로파일)가 RejectedAttemptRaceTest.java:203 에서 실패한다.

expected: 70.67
 but was: 42.0

테스트 의도는 (82 + 42 + 88) / 3 이다. 인정된 반복 2개(pose 배치 sync 82·88)와 미인정 시도 1개(42)를 합친 평균이다. 그런데 실제 값 42.0 은 미인정 시도 한 건만 평균에 들어간 값과 같다. 이 테스트가 Backend tests 에서 유일하게 실패하는 테스트라, main CI 가 빨간불로 고정돼 있다.

언제부터

→ 테스트가 처음부터 통과한 적이 없다. #819(RC)나 #845 의 회귀가 아니다.

재현

cd backend && ./gradlew test --tests "com.shadowfit.service.exercise.RejectedAttemptRaceTest"   # Docker 필요(Testcontainers)

원인 후보 — 🔴 전부 미검증

42.0 = «pose 행의 sync_rate 가 평균에 하나도 안 들어갔다» 로 읽힌다. 그렇게 될 수 있는 경로:

  1. 시드 스쿼트(SEEDED_SQUAT_ID)가 repetition_only=true 라 PoseDataService.savePoseDataBatch 가 sync_rate 를 NULL 로 저장(ExerciseAnalysisCapabilities.isCountOnly)
  2. 완료 집계(SessionCompletionTx.applyComplete)가 평균을 pose 행이 아니라 다른 원천에서 계산 — 175행 주석이 날짜에 따라 «센 반복의 평균» 으로 정의가 갈린다고 적고 있다
  3. 테스트 쪽 기대값이 구현과 다른 정의(«모든 시도 평균»)를 가정

먼저 할 일: 테스트 안에서 SELECT sync_rate FROM pose_data WHERE session_id=? 를 찍어 pose 행의 sync_rate 가 NULL 인지부터 본다. 이걸로 1과 2·3 이 갈린다.

🤖 Generated with Claude Code

Activity

  1. Khyojae commented on Nov 8, 2026

    @Khyojae
    MemberAuthor

    원인 확인 (2026-11-08, 로컬 재현)

    JVM 시간대와 JDBC serverTimezone 이 다르면, 같은 «세션 시작 시각» 이 두 테이블에 서로 다른 값으로 저장된다. 완료 집계(PoseDataRepository.findRepSummaries)는 pose_data.created_at = 세션 시작 시각 으로 rep 을 찾는다. 두 값이 어긋나면 rep 이 0개가 되고, 평균에 미인정 시도(42)만 남는다.

    재현

    • 이 테스트만 로컬(JVM = KST)에서 돌리면 통과한다(5/5)
    • 같은 테스트를 JAVA_TOOL_OPTIONS=-Duser.timezone=UTC 로 돌리면 CI 와 똑같이 expected 70.67 but was 42.0 으로 실패한다. CI 러너의 JVM 은 UTC 다

    UTC JVM 에서 찍은 값(테스트에 진단 쿼리를 임시로 넣어 확인, 커밋 안 함):

    값 쓰는 경로 DB 에 저장된 값
    exercise_sessions.start_time Hibernate(JPA) 14:09:42 — JVM 값 05:09:42 를 serverTimezone=Asia/Seoul 로 변환
    pose_data.created_at JdbcTemplate ps.setObject(8, sessionAnchor) (PoseDataService) 05:09:42 — 변환 없음

    완료 집계의 JPQL 은 05:09:42 를 다시 14:09:42 로 바꿔 비교하므로 pose 행과 맞지 않는다.

    영향 범위

    • 운영·dev compose 는 안전하다 — 백엔드 컨테이너가 TZ: Asia/Seoul(docker-compose.prod.yml:91, dev 도 같음)이라 JVM 시간대 = serverTimezone 이고, 두 경로가 같은 값을 쓴다(로컬 compose 컨테이너에서 TZ=Asia/Seoul 확인)
    • 위험이 숨어 있는 건 «JVM 시간대 = Asia/Seoul» 이라는 전제가 코드·설정 어디에도 강제돼 있지 않다는 점이다. TZ 를 빼고 띄우면(다른 런타임·관리형 배포) 완료 통계·세트·리포트가 조용히 빈다
    • 같은 패턴(JdbcTemplate 로 LocalDateTime 을 쓰는 곳)은 PoseDataService·RejectedAttemptService·FeedbackLogService·PoseDataOrphanMonitor 에 있다. 각각 Hibernate 가 쓴 값과 비교되는지는 미확인

    원인 후보 정리

    본문의 후보 1(repetition_only)·2(집계 정의)·3(기대값)은 모두 아니다. pose 행은 sync 82·88 로 정상 저장된다.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions