fix(queue): enqueue 경로 중복 방어 — partial unique index + 중앙 enqueue_stage 함수

기존 UNIQUE(document_id, stage, status)는 pending+processing 동시 존재를
허용해서 stale 복구 시 충돌 발생. 2-layer 방어로 근본 차단:

1) DB: partial unique index uq_queue_active — 활성 행(pending/processing)은
   (document_id, stage)당 최대 1개만 허용
2) App: enqueue_stage() 중앙 함수 — INSERT ON CONFLICT DO NOTHING으로
   모든 9개 경로의 check-then-insert TOCTOU race 제거

migration 117은 guard check 포함 — 활성 중복이 남아있으면 RAISE EXCEPTION
으로 중단, 수동 정리 유도.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Hyungi Ahn
2026-04-15 08:37:32 +09:00
parent 8ec1e53ca4
commit 751cdc5be8
9 changed files with 77 additions and 65 deletions
+2 -6
View File
@@ -17,7 +17,7 @@ from core.config import settings
from core.database import get_session
from core.utils import file_hash
from models.document import Document
from models.queue import ProcessingQueue
from models.queue import ProcessingQueue, enqueue_stage
from models.user import User
router = APIRouter()
@@ -473,11 +473,7 @@ async def upload_document(
await session.flush()
# 처리 큐 등록
session.add(ProcessingQueue(
document_id=doc.id,
stage="extract",
status="pending",
))
await enqueue_stage(session, doc.id, "extract")
await session.commit()
return DocumentResponse.model_validate(doc)