fix(backend): 이어하기가 실제로는 아무것도 하지 않던 문제 - #181
Merged
Merged
Conversation
직전 PR(#180)에서 재개 후 엔티티를 `findById` 로 다시 읽었다. **벌크 UPDATE 는 영속성 컨텍스트를 갱신하지 않으므로** 같은 트랜잭션의 `findById` 는 1차 캐시에 있는 낡은 엔티티(status=INTERRUPTED)를 그대로 돌려준다. 결과적으로: - `advanceToNextGeneral` 이 `getStatus() != IN_PROGRESS` 에서 조용히 되돌아가 **턴 복구가 통째로 죽었다** — 생성 중 placeholder도, 다음 질문 발행도 일어나지 않는다 - 응답 `status` 가 INTERRUPTED 로 나가 프론트가 라이브 화면으로 전환하지도 않는다 즉 이어하기 기능 전체가 동작하지 않았다. `SessionService.start` 가 `startIfReady` 뒤에 `session.start()` 로 인메모리를 맞추는 것과 같은 방식으로 `InterviewSession.resume(now)` 을 추가해 맞춘다. UPDATE 와 같은 `now` 를 넘겨 DB 와 값이 어긋나지 않게 했다. ## 테스트가 왜 못 잡았나 `sessionRepository.findById` 를 **별도의 IN_PROGRESS 인스턴스**를 돌려주도록 목을 걸어서, 운영에서 낡은 엔티티가 오는 상황이 재현되지 않았다. 픽스처를 인스턴스 하나만 쓰도록 바꾸고 `resume_syncsInMemoryStateAfterBulkUpdate` 를 추가했다 — 상태·resumedAt·endedAt· durationAnchor·응답 status 를 한 번에 못 박는다. 수정 전 코드로 되돌리면 이 테스트가 실패하는 것을 확인했다. `backend/CLAUDE.md` 에 "조건부 UPDATE 뒤 인메모리 동기화" 를 규약으로 적었다.
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.
변경 사항
직전 PR(#180)의 후속. 머지 직후 교차 검토하다 발견했다 — 이어하기 기능 전체가 동작하지 않는 상태였다.
무엇이 잘못됐나
재개 전이 후 엔티티를 다시 읽었다:
JPQL 벌크 UPDATE 는 영속성 컨텍스트를 갱신하지 않는다. 세션은 바로 위에서
findByIdAndUser_IdAndDeletedFalse로 이미 로드돼 1차 캐시에 있으므로,findById는 DB 를 다시 읽지 않고 낡은 인스턴스(status=INTERRUPTED) 를 그대로 돌려준다.flush()는 변경을 DB 로 밀 뿐 엔티티를 새로 읽지 않는다.결과:
advanceToNextGeneral이 첫 줄에서getStatus() != IN_PROGRESS로 조용히 되돌아간다 → 생성 중 placeholder 복구도, 다음 질문 발행도 일어나지 않는다. feat: 중단된 면접 이어하기 (B-5) #180 이 자랑한 "끊긴 턴 복구"가 통째로 죽어 있었다status가 INTERRUPTED 로 나가 프론트가 라이브 화면으로 전환하지도 않는다사용자 입장에선 '이어서 진행하기' 를 눌러도 아무 일도 일어나지 않는다.
수정
이 저장소에는 이미 정답이 있었다 —
SessionService.start는startIfReady뒤에session.start()로 인메모리를 맞춘다:같은 방식으로
InterviewSession.resume(now)를 추가했다. UPDATE 와 같은now를 넘겨 DB 와 인메모리 값이 어긋나지 않게 했다(start()는 각자Instant.now()를 써서 미세하게 다를 수 있는데, 여기선 피했다).테스트가 왜 못 잡았나
sessionRepository.findById를 별도의 IN_PROGRESS 인스턴스를 돌려주도록 목을 걸어서, 운영에서 낡은 엔티티가 오는 상황 자체가 재현되지 않았다. 목이 실제 동작을 가린 전형적인 경우다.픽스처를 인스턴스 하나만 쓰도록 바꾸고
resume_syncsInMemoryStateAfterBulkUpdate를 추가했다 — 상태 ·resumedAt·endedAt소거 ·durationAnchor()· 응답 status 를 한 번에 못 박는다.수정 전 코드로 되돌려 이 테스트가 실패하는 것을 확인했다 (
resume_leavesAnsweredQuestionAlone,resume_syncsInMemoryStateAfterBulkUpdate2건 FAILED).참고: 복구 분기 테스트들은
QuestionsCallbackService를 목으로 두기 때문에 이 버그를 잡지 못한다(목에는 상태 검사가 없다). 그래서 인메모리 동기화 자체를 검증 대상으로 삼았다.영향 범위
backend/CLAUDE.md에 "조건부 UPDATE 뒤에는 인메모리 상태를 반드시 맞춘다" 를 규약으로 적었다 — 같은 실수가 다음 전이에서 반복되지 않게.리뷰어 체크포인트
end,interrupt,cancel)는 전이 후 엔티티 상태를 읽지 않거나 이미 동기화하고 있어 같은 문제가 없다.start는session.start()로 맞추고,end/interrupt는 이후 엔티티 상태에 의존하지 않는다