미병합 #51의 feat/50-image-cleanup-worker에서 분기하고 #39의 refactor/38-dream-image-async를 병합한다.
공유 커밋을 다시 쓰지 않고 두 부모의 커밋 이력을 보존한다. develop에 선행 기능이 들어갔다고 가정하지 않는다.
#39의 Future/슬롯 계약과 #46 생성 보호, #48 파생 그래프 삭제, #50 정리 상태/참조 보호를 함께 적용한다.
새 DB 테이블/컬럼은 추가하지 않는다. 기존 DB에는 선행 PR들의 수동 SQL이 적용되어야 한다.
실제 OpenAI/S3 어댑터·크레딧·세계관·CI 구현은 이번 범위에 없다.
ImageGenerator.generate는 CompletableFuture<byte[]>, 이미지 생성 HTTP API는 비동기 응답을 반환한다.
Provider 어댑터는 Future를 신속히 반환해야 한다. 대기를 다른 스레드에 옮겨 get/join으로 막는 구현은 사용하지 않는다.
- 사용자 잠금 트랜잭션에서 소유권/revision/입력/재생성 자격과 기존 결과 재사용을 검사한다.
- 신규 작업만 슬롯을 확보하고 이미지 예약을 커밋한다. 슬롯 부족은 IMAGE_503_2이며 예약 변경을 롤백한다.
- 트랜잭션 종료 뒤 Provider의 Future에 완료 처리를 연결한다. 응답 대기 중 요청 스레드/DB 잠금을 유지하지 않는다.
image-result-*실행기에서 바이트 검증·업로드와 짧은 완료/실패 트랜잭션을 처리한다.- 업로드·결과 기록·필요한 보상 정리가 끝나면 슬롯을 반환한다. 예약 저장 실패도 획득한 슬롯을 반환한다.
새 요청은 완료/실패 결과가 준비되면 200, 같은 입력의 진행 중 요청은 곧바로 202, 성공 결과 재사용은 200이다. 응답은 no-store이며 저장 키·서명 URL·외부 예외 본문을 메타데이터/오류 응답으로 보내지 않는다. HTTP 응답/공개 Future 취소가 내부 완료 처리·정리·슬롯 반환을 취소하지 않는다. 실제 생성 요청을 접수하는 영속 대기열이나 서버 재시작 후 Provider 자동 재호출은 구현하지 않았다.
활성 이미지의 관련 꿈 수정/삭제·충돌하는 분석/서사 재생성은 409다. 다른 꿈을 불필요하게 차단하지 않는다. 예약/변경은 기존 사용자 잠금 순서로 직렬화하며 외부 응답 대기에는 잠금을 유지하지 않는다. 기존 성공/진행 요청 재사용은 신규 실행 검사/슬롯보다 먼저 처리한다. 따라서 슬롯이 가득 차거나 분석/서사 재생성 대기 중이어도 자격을 만족하는 기존 성공 이미지는 조회/재사용할 수 있다. 명시적 이미지 재생성은 같은 입력이어도 신규 시도이며 최신 imageVersion과 생성 보호 조건을 검사한다. 실패해도 이전 성공 파일·결과 입력 정보는 유지한다. 이미지 상태 PROCESSING/FAILED만으로 이전 파일이 사라진 것은 아니다.
lease 만료 후 수정/삭제가 허용되더라도 이전 시도의 완료는 원본·입력·시도·lease를 다시 검사한다. 꿈 삭제는 관련 파생 DB 행과 기존 성공 파일 정리 요청을 함께 커밋한다. 늦은 응답으로 행을 복원하지 않는다. 삭제된 이미지의 완료/실패·단건·URL 재발급은 404다. 비동기 HTTP 오류도 IMAGE_404_1을 유지하고 외부 원인을 제거한다.
업로드 뒤 채택되지 않은 파일은 해당 새 시도의 객체만 보상 삭제한다. 보상 실패는 DISCARDED_UPLOAD 요청에 영속 기록한다. #50 정리 워커는 성공 참조·활성 시도를 보호하고, 삭제를 시작한 키는 이후 이미지 결과로 채택하지 못하도록 한다. 동일 성공의 완료 재전송을 새 미채택 업로드로 판단해 성공 파일을 지우지 않는다. 완료 후 같은 키 등록/삭제 IO 직후 재업로드는 #50 재등록 계약으로 처리한다. 삭제 성공 뒤 DB 기록 실패는 lease 만료/멱등 삭제로 복구한다. 자세한 상태 전이는 워커 계약을 따른다.
DB/외부 저장소의 비원자성, 프로세스 중단·보상과 정리 기록의 동시 실패, 과거 키 이력 부재는 여전히 운영 대조 회수 범위다. 기존에 발급한 다운로드 URL은 만료 전 유효할 수 있다. 과거 전체 파일 회수·세계관 전체 이미지 정리는 추가하지 않는다.
| 설정 | 기본값 | 적용 범위 |
|---|---|---|
| MONGLE_IMAGE_MAX_CONCURRENT_CALLS | 4 | Provider 대기부터 파일/DB/보상 처리까지 진행 이미지 시도 (#83 예산 조정) |
| MONGLE_IMAGE_RESULT_THREADS | 2 | Provider 응답 뒤 이미지 검증/업로드/DB 완료 스레드 |
| MONGLE_IMAGE_CLEANUP_ENABLED | false | 별도로 기록된 파일 정리 요청 워커 활성화 |
| MONGLE_IMAGE_CLEANUP_MAX_CONCURRENT_CALLS | 2 | 이미지 정리 워커의 외부 삭제 실행 수 |
image.execution과 image.cleanup은 같은 YAML image 아래에 공존하지만 별도 실행 자원이다. 동시 이미지 작업 4와 결과 스레드 2는 서로 다른 제한이다. 정리 워커 2도 Provider 작업 상한이 아니다. 값은 설정 가능하며 실제 Provider/스토리지 시간 제한·메모리·호출 한도 측정 뒤 조정한다. 기본 스토리지/생성기는 unavailable이고 정리 워커는 기본 비활성이다. 이번 통합만으로 실 이미지 운영 연결이 되지는 않는다.
기존 비동기·생성 보호·삭제·정리 회귀와 다음 통합 시나리오를 확인한다.
- 미완료 Provider Future 반환·전용 완료 풀·슬롯 한도/반납·예약 롤백·취소 후 내부 처리.
- 진행 중 해당 꿈 변경/재생성 차단, 다른 꿈 변경 허용, 가득 찬 슬롯과 서사 재생성 중 성공 이미지 재사용.
- 만료 후 삭제/Provider 실패/늦은 업로드의 404·원본 비복원·보상 실패 기록·정리 워커 완료.
- 업로드와 DB 완료 사이 삭제, 삭제 시작 키의 늦은 채택 차단, 동일 성공 완료 재전송 보호.
- 실제 로컬 HTTP의 진행 중 중복 202·원래 요청 완료 200/no-store·삭제 후 오류 404·비밀정보 제거.
H2/가짜 Provider/가짜 저장소와 로컬 HTTP를 사용한다. 실제 MySQL 8·OpenAI·S3·운영 파일 삭제는 실행하지 않는다. 전체 테스트/bootJar/diff 최종 결과는 PR 검증 기록과 이 프로젝트 완료 보고서에 남긴다.
#54에서 선택적 비공개 S3 저장소를 추가한다. 기본 비활성이며 실제 이미지 Provider는 여전히 미연결이다. S3 IO는 기존 제한된 완료 풀/정리 실행기에서 수행한다. 설정·시간 제한·버전 관리 전제를 참고한다.
#56에서 기본 비활성 OpenAI 어댑터를 추가한다. 모델/출력/옵션은 필수 설정이며 기존 재사용/재생성·삭제/정리 계약을 유지한다. Provider/응답 큐/저장·보상 예산과 종료 순서는 OpenAI 연동 계약을 참고한다.