Skip to content

[fix] 썸네일 큐 초과 시 성공한 업로드가 500으로 응답될 위험 #86

Description

@hamtorygoals

📄 Description

이슈 #70(PHOTO-02/03 파일 수·용량 제한) 검증 과정에서 발견한 별도 문제입니다.

PhotoUploadService.submitThumbnailJobsAfterCommit은 PHOTO-03 트랜잭션 커밋 직후, 완료 등록을 처리하던 바로 그 요청 스레드에서 각 사진의 썸네일 생성 작업을 thumbnailExecutor(ThreadPoolTaskExecutor, core=4/max=6/큐 100 — 전부 미검증 하드코딩 기본값, application.yaml 등 어디에도 오버라이드나 근거 없음)에 동기적으로 제출합니다.

ThreadPoolTaskExecutor의 기본 RejectedExecutionHandlerAbortPolicy라, 스레드 6개가 모두 사용 중이고 큐(100개)도 가득 차면 제출 시 RejectedExecutionException을 던집니다. 문제는 이 시점에 사진은 이미 DB에 정상적으로 생성·커밋된 뒤라는 점입니다. 즉 실제로는 업로드가 성공했는데도, 썸네일 작업 제출이 큐 초과로 예외를 던지면 그 예외가 그대로 전파되어 HTTP 응답이 500으로 나갈 위험이 있습니다(사진 row는 이미 커밋되어 살아있는데 응답만 실패로 보이는 상태 불일치).

실측 결과 (조사용 벤치마크, PhotoUploadBatchLoadBenchmarkTest)

Object Storage 지연 80ms를 흉내 낸 환경에서 동시 사용자 3명이 각자 20개 배치를 완료 등록했을 때:

  • 완료 직후 thumbnailExecutor 활성 스레드 4, 대기 큐 56/100
  • 이 서비스는 같은 모임·행사에서 여러 명이 비슷한 시점에 몰아서 업로드하는 패턴이 자연스러운데, 사용자 56명만 겹쳐도 (56 × 20 = 100~120) 큐가 가득 차는 시나리오가 드문 예외가 아니라 흔한 정상 케이스일 수 있습니다.

✅ Tasks

  • 큐 초과(RejectedExecutionException) 시 응답이 깨지지 않도록 안전하게 처리 — 로그만 남기고 thumbnailStatusPENDING으로 남겨 기존 PhotoSweepScheduler 복구 경로(재시작으로 작업 유실 시와 동일한 경로)를 타도록 수정. 리뷰 과정에서 PhotoSweepScheduler.resubmitStaleThumbnailJobs()(재수거 스윕 자체)에도 같은 결함이 있는 걸 발견해 함께 수정했습니다.
  • 위 수정 이후, 동시 사용자 6~10명 × 20개 배치 등 현실적인 부하로 재측정해 큐가 실제로 어느 시점에 얼마나 차는지, 그 상태에서 썸네일 준비 지연이 체감상 허용 가능한지 확인 → #107로 분리
  • 측정 근거로 thumbnail.executor.queue-capacity(및 필요 시 core/max-pool-size)를 application.yaml에 명시적으로 설정하고 근거를 의사결정 로그에 기록 → #107로 분리

📎 ETC

  • 대기 중인 큐 항목은 이미지 바이트가 아니라 photoId(UUID) 참조만 들고 있어 큐 용량 자체를 늘리는 메모리 비용은 크지 않음 — 실행 중인 작업(최대 4~6개)의 메모리 비용(파일 크기가 아니라 해상도에 비례, 관련 조사는 이슈 [Feat] 사진 업로드 파일 수 및 용량 제한 조정 #70 참고)과는 별개 문제.
  • 재배포 시 큐에 남은 작업은 유실되고 스윕이 나중에 복구하는 구조라(docs/architecture/backend-architecture.md 4.3), 큐를 넉넉히 잡아도 치명적이진 않으나 유실 폭이 커지는 트레이드오프는 있음.

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions