Skip to content

feat(session): 시간초과 세션 자동 종료 (좀비 세션 방지)#124

Merged
i3months merged 1 commit into
devfrom
feature/session-sweeper
Jun 25, 2026
Merged

feat(session): 시간초과 세션 자동 종료 (좀비 세션 방지)#124
i3months merged 1 commit into
devfrom
feature/session-sweeper

Conversation

@i3months

Copy link
Copy Markdown
Member

변경 사항

스켑틱 리뷰에서 가장 큰 남은 갭이었던 세션 좀비화를 해소합니다.

면접을 중간에 버리면(자기소개 미답변 / STT 실패 / 탭 종료) 세션이 IN_PROGRESS영원히 남았습니다 — 피드백도 못 받고, 히스토리·중복질문 회피·통계를 오염시켰습니다. maxDurationMinutes 자동 종료가 원래 "미구현"이었던 게 원인.

이제 @Scheduled 스위퍼가 시간 초과된 진행 중 세션을 자동 종료합니다.

동작

  • SchedulingConfig (@EnableScheduling) + SessionTimeoutSweeper (@Scheduled, 기본 5분 주기): maxDurationMinutes(세션별, startedAt 기준) 초과한 IN_PROGRESS 세션을 찾아 SessionTimeoutService.endTimedOut(id) 호출. 진행 세션은 소수라 전체 조회 후 메모리 필터.
  • SessionTimeoutService (세션마다 독립 트랜잭션):
    • 답변이 하나라도 있으면 → COMPLETED + SessionEndedEvent(DURATION_EXCEEDED)피드백 생성
    • 답변이 없으면(자기소개도 안 함) → INTERRUPTED (피드백 없음, 정리만)
    • IN_PROGRESS 재확인 → 멀티 인스턴스 중복 실행에도 안전(+ 피드백 멱등)
  • repo: findByStatusAndDeletedFalse, existsBySession_IdAndRole 추가
  • 설정: interview.session.sweep-interval-ms(기본 300000) · sweep-initial-delay-ms(기본 60000)

테스트

  • SessionTimeoutServiceTest: 답변O→COMPLETED+EndedEvent / 답변X→INTERRUPTED+EndedEvent 미발행 / 이미 종료→no-op
  • SessionTimeoutSweeperTest: 시간초과 세션만 종료, 신규 세션은 제외
  • 전체 백엔드 스위트 통과(스케줄링 활성화된 컨텍스트 부팅 포함, ArchUnit·Flyway 포함)

비고

시간 기준은 maxDurationMinutes(기본 60분) — 버려진 세션은 최대 그 시간 내 정리됩니다. 더 빠른 유휴(idle) 기반 정리는 마지막 메시지 시각 추적이 필요해 후속으로 둡니다.

🤖 Generated with Claude Code

면접을 중간에 버리면(자기소개 미답변·STT 실패·탭 종료) 세션이 IN_PROGRESS 로
영원히 남아 피드백도 못 받고 히스토리·중복회피를 오염시키던 갭을 해소.

- @EnableScheduling(SchedulingConfig) + SessionTimeoutSweeper(@scheduled 5분 주기)가
  maxDurationMinutes 초과한 IN_PROGRESS 세션을 찾아 SessionTimeoutService.endTimedOut 호출.
  진행 세션은 소수라 전체 조회 후 메모리 필터(startedAt + maxDurationMinutes).
- SessionTimeoutService: 답변 있으면 COMPLETED(→ SessionEndedEvent(DURATION_EXCEEDED)
  → 피드백 생성), 없으면 INTERRUPTED(피드백 없음). IN_PROGRESS 재확인으로 멀티 인스턴스 안전.
- repo: findByStatusAndDeletedFalse, existsBySession_IdAndRole 추가.
- 주기 설정: interview.session.sweep-interval-ms(기본 300000)·sweep-initial-delay-ms(기본 60000).
- 테스트(서비스 분기 + 스위퍼 필터) + CLAUDE 갱신.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@vercel

vercel Bot commented Jun 25, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
stackup Ready Ready Preview, Comment Jun 25, 2026 11:47am

@i3months i3months merged commit 274044b into dev Jun 25, 2026
3 checks passed
i3months added a commit that referenced this pull request Jun 28, 2026
…-hardening

fix(session): 세션 종료 동시성 하드닝 (#124 후속)
i3months added a commit to i3months/stackup that referenced this pull request Jun 30, 2026
회의적 멀티에이전트 리뷰(3/3 검증)에서 발견한 Team-StackUp#124 스위퍼 동시성 결함 3건.

1) [HIGH] 종료 세션 사후 변조: QuestionsCallbackService.apply()가 status 미확인 →
   스위퍼/수동 종료 뒤 늦게 도착한 POOL/FOLLOWUP 콜백이 종료 세션에 질문·placeholder
   INSERT. apply()에 isTerminal() 가드 추가(멱등 마킹 후 드롭).
2) [MEDIUM] generate.feedback 이중 발행: 락 없는 COMPLETED 전이로 동시 종료 시
   SessionEndedEvent 가 두 번 발행(멱등 가드가 비동기 생성 피드백 행에 의존해 무력화).
3) [MEDIUM] 막판 답변 발화가 종료 세션에 꼬리질문 누적.

수정:
- InterviewSessionRepository.finishIfInProgress: 조건부 UPDATE(WHERE status=IN_PROGRESS)로
  종료 전이를 원자화. 영향 행 1인 트랜잭션만 종료 부수효과 발행 → DB 행 락 직렬화로
  동시 종료(스위퍼·수동·콜백) 시 정확히 한 번만 SessionEndedEvent 발행.
- SessionTimeoutService/SessionService.end/QuestionsCallbackService.endSession 모두
  finishIfInProgress 게이트 사용.
- SessionFollowupRequester.onAnswerSubmitted 에 isTerminal() 가드.
- SessionStatus.isTerminal() 헬퍼 추가.
- 테스트: 종료 경쟁 패배(lost-race) 미발행 검증 추가, 기존 종료 테스트 보강. 전체 통과.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant