Skip to content

[Feat] 문서 인덱싱 완료 및 검색 가능 Version 전환 #84

Description

@Gimini-3

📌 Description

현재 Chunk Embedding 생성은 Job에 고정된 Model로 Version의 전체 Vector Set을 원자 저장한 뒤에도
Document Version을 EMBEDDING, Embedding Job을 PROCESSING, Attempt를 STARTED로 유지합니다.

이번 기능은 현재 실행 Context의 마지막 짧은 Transaction에서 Embedding Set의 완전성과 실행 소유권을
재검증하고, Attempt·Job·Version·Document 및 검색 가능 Version 포인터를 함께 완료 처리합니다.
새 Version이 활성화되면 이전 current Version의 ACTIVE Embedding은 STALE로 전환합니다.

최초 완료와 응답 유실 후 같은 실행의 재요청은 중복 이벤트나 상태 변경 없이 같은 완료 결과로 수렴해야
합니다. 이미 완료된 같은 Attempt의 재생은 최종 결과 확인이므로 Lease 만료 여부와 무관하게 허용하되,
Job·Attempt·Worker·Claim Token이 모두 기존 완료 실행과 일치해야 합니다.

✅ To-do

완료 API 계약

  • POST /admin/indexing-jobs/{jobId}/attempts/{attemptId}/complete 추가
  • 요청 Body는 Worker ID와 canonical UUID Claim Token만 입력
  • 응답은 Job·Attempt·Document·Version 식별자, 완료 상태와 완료 시각만 반환
  • Claim Token, Chunk 본문, Vector와 내부 오류 상세는 응답·이벤트·일반 로그에 노출하지 않음
  • 최초 완료와 같은 실행의 멱등 재생 모두 200 OK
  • 기존 /admin/** ADMIN 정책 재사용 및 Swagger 성공·오류 계약 문서화

잠금과 실행 Context 검증

  • 모든 완료 요청은 Embedding Job을 가장 먼저 쓰기 잠금으로 조회
  • 최초 완료는 PROCESSING Job에 대해 Worker, Claim Token과 유효 Lease 검증
  • Path Attempt ID가 현재 Claim의 STARTED Attempt 및 Worker와 일치하는지 검증
  • Job이 직접 참조하는 Version을 다음 순서로 쓰기 잠금
  • Version이 참조하는 Document를 마지막으로 쓰기 잠금
  • 잠금 순서를 Job → Version → Document로 고정하고 향후 완료·실패·복구 흐름도 동일하게 유지
  • Document 잠금 뒤 대상 Version이 해당 Document의 최신 Version인지 다시 검증
  • Version·Document 관계와 현재 Version 포인터가 같은 Document 범위인지 검증

Embedding Set 완료 조건

  • 대상 Version이 EMBEDDING 상태인지 검증
  • Job에 고정된 Embedding Model ID가 유효한지 검증
  • 완료 시점에도 Job 고정 Model이 active + searchable인지 검증해 검색 불가능한 Version 활성화 차단
  • 대상 Version의 Chunk가 한 건 이상 존재하는지 검증
  • Version·Job Model 기준 전체 Embedding 수가 Chunk 수와 정확히 같은지 검증
  • 완료 대상 Embedding 전체가 ACTIVE 상태인지 검증
  • 부분 Set, 다른 Model, Version·Document 역정규화 불일치는 내부 데이터 오류로 완료 차단
  • 검증 실패 시 Attempt·Job·Version·Document·Embedding·이벤트를 모두 변경하지 않음

원자적 상태 전환

  • 하나의 Transaction에서 Attempt를 SUCCESS로 종료
  • Attempt의 ended_atduration_ms 기록
  • Job을 INDEXED로 전환하고 completed_at 기록
  • Version을 INDEXED로 전환하고 indexed_at 기록
  • Document의 current_version_id를 완료 Version으로 교체
  • Document를 INDEXED로 전환
  • 이전 current Version이 다른 Version이면 해당 Version의 ACTIVE Embedding을 STALE로 일괄 전환
  • 최초 Version처럼 current Version이 이미 완료 대상이면 자기 Embedding을 STALE로 바꾸지 않음
  • INDEXED 이벤트를 정확히 한 건 기록
  • 같은 완료 시각을 Attempt·Job·Version·이벤트에 사용
  • 상태 변경이나 이벤트 저장 중 하나라도 실패하면 전체 Rollback

멱등 재생과 오래된 완료 차단

  • Job 행 잠금 후 이미 INDEXED이면 완료 재생 경로로 분기
  • 재생은 같은 Job·Attempt ID·Worker ID·Claim Token과 SUCCESS Attempt만 허용
  • 완료 재생은 현재 Lease가 만료됐더라도 기존 완료 시각과 상태를 반환
  • 재생에서 Embedding 상태, current Version, 이벤트와 완료 시각을 다시 변경하지 않음
  • 다른 Claim Token, 다른 Worker, 다른 Attempt의 완료 가장은 409로 거부
  • 최초 완료 시 대상보다 최신 Version이 존재하면 stale 완료로 보고 409로 거부
  • FAILED, CANCELED 등 종료 상태 Job은 완료 처리하지 않음

Repository·Domain·오류 계약

  • Version·Model·상태별 Embedding 개수 조회 계약 추가
  • 이전 Version의 ACTIVE → STALE 일괄 갱신 쿼리 추가
  • 최신 Document Version 판정과 필요한 잠금 조회 계약 정리
  • Job·Attempt·Version의 성공 전이 메서드에 허용 상태 Guard 반영
  • 완료 불가, stale 완료와 내부 데이터 불일치 ErrorCode 구분
  • 새 Class·Record의 역할·책임·경계를 class-level comment로 문서화
  • 순차 실행 흐름에 번호 주석을 유지하고 변경된 주석을 실제 동작과 일치시킴

검증

  • Entity 상태 전이와 잘못된 전이 단위 테스트
  • Service 최초 완료·완료 재생·응답 동일성 단위 테스트
  • 잘못된 Worker·Token·Attempt, 만료 Lease와 종료 Job 거부 테스트
  • Chunk·Embedding 수 불일치, 비활성 Embedding과 최신 Version 불일치 테스트
  • Controller 입력 검증, 200, ADMIN/USER/미인증 계약 테스트
  • 실제 OpenSQL에서 최초 Version 완료 후 검색 가시성 검증
  • 실제 OpenSQL에서 새 Version 활성화, current Version 교체와 이전 Embedding STALE 검증
  • 실제 OpenSQL에서 두 Thread 동시 완료가 단일 상태 전환과 INDEXED 이벤트 한 건으로 수렴하는지 검증
  • 완료 도중 실패 시 모든 상태와 Embedding 상태가 Rollback되는지 검증
  • 완료 후 같은 요청 재생이 Row·이벤트·시각을 변경하지 않는지 검증
  • ./gradlew clean buildgit diff --check 통과
  • docs/design/에 실제 구현 기준 상세 설계 문서 작성

🔒 핵심 불변식

  • 완료 Transaction의 잠금 순서는 항상 Embedding Job → Document Version → Document입니다.
  • 최초 완료는 유효한 현재 Claim과 만료되지 않은 Lease만 수행할 수 있습니다.
  • 이미 성공한 같은 실행의 완료 재생은 Lease와 무관하지만 저장된 실행 식별자가 모두 일치해야 합니다.
  • 검색 가능한 Version 전환과 이전 Embedding 비활성화는 하나의 Transaction으로 커밋됩니다.
  • 완료되는 Version은 해당 Document의 최신 Version이어야 합니다.
  • 완료 시점의 Job 고정 Model은 Query Embedding이 사용하는 active + searchable Model이어야 합니다.
  • Chunk 수와 Job Model의 ACTIVE Embedding 수는 완료 시 정확히 같습니다.
  • 최초 Version 완료에서는 대상 Embedding을 스스로 STALE 처리하지 않습니다.
  • 새 Version 완료 후 검색은 새 current Version의 ACTIVE Embedding만 대상으로 합니다.
  • 완료 이벤트와 상태 전이는 같은 실행에서 정확히 한 번만 기록됩니다.
  • Claim Token, Chunk 본문과 Vector는 API 응답·이벤트·일반 로그에 노출하지 않습니다.

🚫 제외 범위

  • 실패 시 Attempt·Job·Version 종료와 오류 저장
  • 자동 재시도, Retry 횟수 증가와 다음 실행 시각 계산
  • Lease 연장, 만료 Job 회수와 Worker 자동 Polling
  • 완료된 과거 Version 수동 재활성화
  • 부분 Embedding Set 복구·정리 도구
  • Batch·병렬 Embedding 최적화와 Checkpoint
  • Embedding Model 교체·기존 문서 재색인과 다중 searchable Model 지원
  • 새 Message Broker, Cache 또는 Production Dependency
  • 새 Flyway Migration

✅ 완료 기준

  • 전체 Embedding Set이 준비된 유효한 현재 실행만 인덱싱을 완료할 수 있습니다.
  • Attempt·Job·Version·Document·current Version·이전 Embedding·이벤트가 한 Transaction으로 전환됩니다.
  • 최초 문서는 완료 전 검색되지 않고 완료 커밋 후 검색 대상이 됩니다.
  • 새 Version 완료 전에는 기존 Version이 검색되고, 완료 커밋 후에는 새 Version만 검색됩니다.
  • 동시·순차 재호출이 중복 이벤트나 추가 상태 변경 없이 같은 완료 결과로 수렴합니다.
  • 오래된 Token, 다른 Worker, 잘못된 Attempt, 만료 Lease와 stale Version은 검색 가시성을 바꾸지 못합니다.
  • 단위·Controller·OpenSQL 통합·동시성·전체 회귀 테스트가 통과합니다.

📒 기타

  • 선행 기능: [Feat] Chunk Embedding 생성 및 Vector 저장 #82
  • 검색 Query는 documents.status = INDEXED, documents.current_version_id = embeddings.document_version_id,
    embeddings.status = ACTIVE를 모두 요구합니다.
  • 최초 업로드는 current Version이 아직 미완료 Version을 가리키므로 Document 상태가 검색 노출을 차단합니다.
  • 새 Version 처리 중에는 기존 INDEXED current Version을 유지합니다.
  • 완료 응답 유실에 대비해 Job·Attempt의 완료 실행 식별 정보는 유지하되 외부로 노출하지 않습니다.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions