You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
test: RejectedAttemptRaceTest 가 추가된 날부터 실패 — avg_sync_rate 기대 70.67, 실제 42.0 (main CI 빨간불) #846
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 가 빨간불로 고정돼 있다.
언제부터
이 테스트 파일은 f9523507(2026-10-21, «feat: add exercise analysis, reporting, and AI-server streaming integration features», V35 exercise_rejected_attempts 와 함께)에서 새로 생겼다
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 로 변환
완료 집계의 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 로 정상 저장된다.
Activity
Khyojae commentedon Nov 8, 2026
원인 확인 (2026-11-08, 로컬 재현)
JVM 시간대와 JDBC
serverTimezone이 다르면, 같은 «세션 시작 시각» 이 두 테이블에 서로 다른 값으로 저장된다. 완료 집계(PoseDataRepository.findRepSummaries)는pose_data.created_at = 세션 시작 시각으로 rep 을 찾는다. 두 값이 어긋나면 rep 이 0개가 되고, 평균에 미인정 시도(42)만 남는다.재현
JAVA_TOOL_OPTIONS=-Duser.timezone=UTC로 돌리면 CI 와 똑같이expected 70.67 but was 42.0으로 실패한다. CI 러너의 JVM 은 UTC 다UTC JVM 에서 찍은 값(테스트에 진단 쿼리를 임시로 넣어 확인, 커밋 안 함):
exercise_sessions.start_time14:09:42— JVM 값05:09:42를serverTimezone=Asia/Seoul로 변환pose_data.created_atJdbcTemplateps.setObject(8, sessionAnchor)(PoseDataService)05:09:42— 변환 없음완료 집계의 JPQL 은
05:09:42를 다시14:09:42로 바꿔 비교하므로 pose 행과 맞지 않는다.영향 범위
TZ: Asia/Seoul(docker-compose.prod.yml:91, dev 도 같음)이라 JVM 시간대 =serverTimezone이고, 두 경로가 같은 값을 쓴다(로컬 compose 컨테이너에서TZ=Asia/Seoul확인)TZ를 빼고 띄우면(다른 런타임·관리형 배포) 완료 통계·세트·리포트가 조용히 빈다LocalDateTime을 쓰는 곳)은PoseDataService·RejectedAttemptService·FeedbackLogService·PoseDataOrphanMonitor에 있다. 각각 Hibernate 가 쓴 값과 비교되는지는 미확인원인 후보 정리
본문의 후보 1(repetition_only)·2(집계 정의)·3(기대값)은 모두 아니다. pose 행은 sync 82·88 로 정상 저장된다.
fix: JVM 시간대 ≠ JDBC 연결 시간대면 기동 거부 + 테스트 JVM 을 Asia/Seoul 로 (#846) (#847)