Skip to content

feat: PROCESSING 좀비 상태 복구 - 실행 소유권(analysis_run_id) 및 can_retry API 필… - #84

Open
6ye0m wants to merge 2 commits into
devfrom
feature/52-run-ownership
Open

feat: PROCESSING 좀비 상태 복구 - 실행 소유권(analysis_run_id) 및 can_retry API 필…#84
6ye0m wants to merge 2 commits into
devfrom
feature/52-run-ownership

Conversation

@6ye0m

@6ye0m 6ye0m commented Aug 7, 2026

Copy link
Copy Markdown
Collaborator

close #52

작업 내용

exams/models.py
StudyMaterial에 analysis_started_at(PROCESSING 진입 시각), analysis_run_id(실행 식별자 UUID) 필드 추가
exams/services/analysis_orchestrator.py
PROCESSING_TIMEOUT_SECONDS = 300(5분) 기준으로 좀비 PROCESSING 판정, retry_analysis()에서만 구제 허용 (정책 단순화)
재시도 횟수 제한을 FAILED/좀비 양쪽에 동일하게 적용 (좀비라고 우회 불가)
analysis_started_at이 NULL인 경우(필드 추가 이전부터 PROCESSING이었던 데이터)도 좀비로 취급
analysis_run_id로 실행 소유권 관리 — 저장 직전 소유권 재확인(StaleAnalysisRunError), _finish_success/_finish_failure도 조건부 UPDATE로 변경해 뒤늦은 실행이 최신 상태를 덮어쓰지 못하게 방지
get_analysis_status()에 is_stale/can_retry/retry_after_seconds 추가
exams/views.py
material_analysis_status 응답에 can_retry/retry_after_seconds 포함
마이그레이션: exams/migrations/0003_studymaterial_analysis_run_id_and_more.py
exams/tests.py
ProcessingTimeoutTestCase(22개), MaterialAnalysisViewTestCase에 5개 추가 (총 27개)

검증 결과

python manage.py test exams.tests.ProcessingTimeoutTestCase → Ran 22 tests, OK
python manage.py test exams.tests.MaterialAnalysisViewTestCase → Ran 25 tests, OK
python manage.py test → Ran 240 tests, OK
manage.py check / makemigrations --check → 이상 없음

@wngjs8114

Copy link
Copy Markdown
Collaborator

전체적으로 #52 의도대로 잘 구현된 것 확인했습니다.
analysis_started_at 기반 좀비 판정, retry 횟수 제한, analysis_run_id 소유권 체크,
can_retry/retry_after_seconds 응답 및 테스트까지 방향은 좋습니다.

다만 머지 전에 한 가지 동시성 케이스만 확인/수정 부탁드립니다.

현재 _save_tasks_with_estimates()에서 StudyTask 저장 트랜잭션이 먼저 commit된 뒤
_finish_success()가 별도로 실행됩니다.

분석이 이미 5분을 넘긴 상태에서는 아래 순서가 가능해 보입니다.

  1. 기존 run A가 _save_tasks_with_estimates()에서 run_id=A 확인 후 StudyTask 저장/commit
  2. 그 직후 사용자가 좀비 재시도 → run B가 run_id를 B로 takeover
  3. run A의 _finish_success(run_id=A)는 조건부 UPDATE 0건으로 무시됨
  4. 그런데 _execute_analysis()는 그대로 tasks를 반환해서 View에서는
    "AI 분석이 완료되었습니다" 후 task_review로 이동할 수 있음

즉 상태 소유권 덮어쓰기는 막았지만,
StudyTask 저장 → COMPLETED 처리 사이에 소유권이 바뀌는 경우
이전 실행이 성공 응답을 반환할 수 있습니다.

_finish_success()가 소유권 상실 시 bool 또는 StaleAnalysisRunError를 반환/발생시키게 해서
기존 실행이 성공으로 끝나지 않게 하거나,
StudyTask 저장과 최종 상태 변경까지 같은 소유권 검증/transaction 범위에서
원자적으로 처리하는 방향으로 보완하면 좋을 것 같습니다.

이 케이스 테스트도 하나 추가 부탁드립니다.

@6ye0m

6ye0m commented Aug 8, 2026

Copy link
Copy Markdown
Collaborator Author

지적하신 동시성 케이스 반영했습니다.

수정 내용

_finish_success()가 소유권을 잃었을 때(조건부 UPDATE 0건) 조용히 return하던 걸, StaleAnalysisRunError를 던지도록 변경했습니다.

python
if not updated_count:
logger.warning(...)
raise StaleAnalysisRunError(...) # 이전: return

_execute_analysis()도 이 예외를 잡아서, StudyTask 저장은 끝났더라도 최종 완료 처리 시점에 소유권을 잃었다면 tasks를 정상 반환하지 않고 예외를 그대로 전파하도록 수정했습니다.

python
try:
_finish_success(study_material, tasks, run_id)
except StaleAnalysisRunError:
logger.info(...)
raise
return tasks

테스트 추가

test_ownership_lost_between_save_and_finish_does_not_report_success — _finish_success()를 patch해서, 그 함수가 호출되는 바로 그 시점(StudyTask 저장 커밋 직후, 완료 처리 직전)에 소유권이 다른 실행으로 넘어가는 상황을 재현했습니다. analyze_and_estimate()가 StaleAnalysisRunError를 던지고, analysis_status는 COMPLETED로 갱신되지 않으며, 이미 저장된 StudyTask는 그대로 남아있는 것까지 확인했습니다.

기존 test_finish_success_ignored_when_run_superseded도 새 동작(예외 발생)에 맞게 같이 수정했습니다.

검증 결과

python manage.py test → Ran 252 tests, OK
python manage.py check / makemigrations --check → 이상 없음

참고: _finish_failure()는 이번엔 대칭적으로 고치지 않았습니다. 실패 경로에서 소유권을 잃으면 원래 예외(AIAnalysisError 등)가 그대로 "실패"로 전달되는데, 이건 이번에 고친 "거짓 성공" 케이스보다 위험도가 낮다고 판단했습니다. 필요하시면 후속으로 대칭 처리하겠습니다.

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.

refactor: 비정상 종료 시 PROCESSING 상태가 장시간 유지되는 문제 개선

2 participants