diff --git a/docs/Diagram/Admin Operations & Observability Architecture.png b/docs/Diagram/Admin Operations & Observability Architecture.png new file mode 100644 index 0000000..93d9c5c Binary files /dev/null and b/docs/Diagram/Admin Operations & Observability Architecture.png differ diff --git a/docs/Diagram/Architecture_Diagram_Feedback_loop.png b/docs/Diagram/Architecture_Diagram_Feedback_loop.png new file mode 100644 index 0000000..bc4e1eb Binary files /dev/null and b/docs/Diagram/Architecture_Diagram_Feedback_loop.png differ diff --git a/docs/Diagram/sequence_diagram/SD-04-FeedBack_loop.png b/docs/Diagram/sequence_diagram/SD-04-FeedBack_loop.png new file mode 100644 index 0000000..f14fa03 Binary files /dev/null and b/docs/Diagram/sequence_diagram/SD-04-FeedBack_loop.png differ diff --git a/docs/Diagram/sequence_diagram/SD-05-Admin_Video_Ops.png b/docs/Diagram/sequence_diagram/SD-05-Admin_Video_Ops.png new file mode 100644 index 0000000..148ae3e Binary files /dev/null and b/docs/Diagram/sequence_diagram/SD-05-Admin_Video_Ops.png differ diff --git a/docs/Diagram/sequence_diagram/SD-06-Admin_ML(feedback_loop)_Ops.png b/docs/Diagram/sequence_diagram/SD-06-Admin_ML(feedback_loop)_Ops.png new file mode 100644 index 0000000..ef42bce Binary files /dev/null and b/docs/Diagram/sequence_diagram/SD-06-Admin_ML(feedback_loop)_Ops.png differ diff --git a/docs/Diagram/sequence_diagram/SD_Mermaid.md b/docs/Diagram/sequence_diagram/SD_Mermaid.md index b9aa053..54cd08f 100644 --- a/docs/Diagram/sequence_diagram/SD_Mermaid.md +++ b/docs/Diagram/sequence_diagram/SD_Mermaid.md @@ -141,4 +141,220 @@ SS->>LLM: LLM 답변 생성 직접 요청 (Direct SDK)
generate_answer(query LLM-->>SS: answer (+ cited_chunk_ids) SS-->>C: 최종 응답 반환
{answer, timestamps, topk_chunk_ids, cited_chunk_ids} +``` + +# 4. Feedback Loop + +```mermaid + +sequenceDiagram + participant Client + participant CoreAPI as Core API Server + participant MetaDB as Metadata DB + participant FIP as Feedback Ingestion Pipeline + participant ObjStore as Object Storage + participant MLWorker as ML Lifecycle Worker + participant ModelFiles as Model Artifact Files + participant MEE as Managed Embedding Endpoint + participant VS as Vector Store + + %% === 피드백 수집 (2.6) === + rect rgb(230, 245, 255) + Note over Client, ObjStore: 피드백 수집 + Client ->> CoreAPI: 검색 응답 단위 피드백 (req_id, rating) + CoreAPI ->> CoreAPI: 사용자 권한 검증 + CoreAPI ->> MetaDB: req_id 검색 응답 스냅샷 조회 + MetaDB -->> CoreAPI: 스냅샷 + CoreAPI ->> CoreAPI: 피드백 유효성 검증
(동일 사용자, 허용 시간, 무효화 여부) + CoreAPI ->> FIP: 검증된 피드백 이벤트 전달
(질의, 응답, 활성 모델/인덱스 정보 포함) + FIP ->> ObjStore: 원본 이벤트 로그 적재 + end + + %% === 데이터셋 전처리 (2.7) === + rect rgb(245, 255, 230) + Note over MLWorker, ObjStore: 피드백 데이터셋 생성 - 정기 배치
운영자 수동 트리거도 가능 + MLWorker ->> ObjStore: 신규 피드백 원본 로그 읽기 + ObjStore -->> MLWorker: 원본 로그 + MLWorker ->> MLWorker: 서빙 맥락(모델 버전, 인덱스)이 기록된 이벤트만
학습 데이터셋으로 변환 + MLWorker ->> ObjStore: 학습용 피드백 데이터셋 저장 + + alt 실행 중인 MLPipelineRun 없음 + MLWorker ->> MLWorker: 학습 파이프라인 자동 트리거 + else 실행 중인 MLPipelineRun 존재 + MLWorker ->> MLWorker: 최신 데이터셋 기준 다음 실행 대기 등록 + Note over MLWorker: 이전 대기 실행은 SUPERSEDED 처리 + end + end + + %% === 모델 재학습 및 재색인 (2.8) === + rect rgb(255, 245, 230) + Note over MLWorker, VS: 모델 재학습 및 재색인 + + %% 학습 + MLWorker ->> ObjStore: 학습용 데이터셋 읽기 + ObjStore -->> MLWorker: 학습 데이터셋 + MLWorker ->> MLWorker: 후보 임베딩 모델 학습 + MLWorker ->> ModelFiles: 후보 모델 저장 + MLWorker ->> MetaDB: MLPipelineRun 생성 + + %% 평가 + MLWorker ->> ObjStore: 평가용 데이터셋 읽기 + ObjStore -->> MLWorker: 평가 데이터셋 (immutable versioned artifact) + MLWorker ->> MLWorker: 후보 모델 vs 기준 모델 비교 평가 + MLWorker ->> MetaDB: 평가 결과 집계 저장 + MLWorker ->> ObjStore: 평가 질의별 상세 아티팩트 저장 + + alt 평가 PASS + MLWorker ->> MEE: 후보 모델 로드 요청 + MEE ->> ModelFiles: 후보 모델 아티팩트 조회 + ModelFiles -->> MEE: 후보 모델 아티팩트 + MEE -->> MLWorker: readiness 확인 + MLWorker ->> MEE: 재색인용 임베딩 요청 (후보 모델) + MEE -->> MLWorker: 임베딩 벡터 + MLWorker ->> VS: 후보 모델 전용 인덱스 구축 + Note over MLWorker, VS: 전체 데이터의 즉시 재색인은 필수 아님 + Note over MLWorker, VS: 신규 유입 데이터와 고활성 데이터부터 우선 반영 + Note over MLWorker, VS: 이 과정에서도 사용자 검색은 기존 서빙 유지 + MLWorker ->> MetaDB: 마지막 정상 서빙 조합 스냅샷 저장 + Note over MetaDB: active/previous 조합을 롤백 복구 기준으로 보존 + MLWorker ->> MEE: 서빙 모델 전환 + MLWorker ->> MetaDB: ModelRelease 갱신
(active=candidate, previous=직전 active) + Note over MLWorker, VS: 서빙 전환 후 남은 오래된 세대 데이터는
최신 active 기준으로 점진 재임베딩 + Note over MLWorker, VS: 온라인 검색은 active와 previous의 최대 2세대까지만 병행 지원 + else 평가 FAIL 또는 시스템 ERROR + MLWorker ->> MetaDB: MLPipelineRun 실패 단계 및 유형 기록 + Note over MLWorker, MetaDB: 기존 서빙 유지,
Admin Dashboard에서 실패 상태 확인 가능 + end + end + + +``` + +# 5. Admin_Video_Ops +```mermaid +sequenceDiagram + participant Admin as Admin Dashboard + participant CoreAPI as Core API Server + participant MetaDB as Metadata DB + participant Broker as Message Broker + participant PipelineWorker as Media & AI Pipeline Worker + participant ObjStore as Object Storage + + Note over Admin, CoreAPI: 모든 Admin 요청은 JWT claim role 기반 운영자 권한 검증,
소유권(user_id) 제한 없이 모든 리소스 접근 + + %% === 파이프라인 상태 조회 === + rect rgb(230, 245, 255) + Note over Admin, MetaDB: 파이프라인 상태 조회 + Admin ->> CoreAPI: 임의 video_id 처리 현황 조회 + CoreAPI ->> MetaDB: Video 상태 및 실패 상세 조회 + MetaDB -->> CoreAPI: 처리 현황, failed_stage, error_message + CoreAPI -->> Admin: 처리 현황 및 실패 상세 응답 + end + + %% === 재처리 === + rect rgb(245, 255, 230) + Note over Admin, PipelineWorker: 비디오 재처리 + Admin ->> CoreAPI: 임의 video_id 재처리 요청 + CoreAPI ->> MetaDB: 현재 Video.status 확인 + MetaDB -->> CoreAPI: 현재 상태 + Note over CoreAPI: 재처리 가능 상태인지 검증 후 진행 + CoreAPI ->> MetaDB: Video.status를 PENDING으로 초기화 + CoreAPI ->> Broker: PREPROCESS_REQUEST 발행 + CoreAPI -->> Admin: 202 Accepted + Broker ->> PipelineWorker: PREPROCESS_REQUEST 전달 + PipelineWorker ->> MetaDB: Video 정보 로드, failed_stage 확인 + MetaDB -->> PipelineWorker: failed_stage, 보존 산출물 정보 + PipelineWorker ->> PipelineWorker: 완료된 작업 Skip, 안전한 재개 지점부터 Resume + end + + %% === 삭제 === + rect rgb(255, 245, 230) + Note over Admin, ObjStore: 비디오 삭제 + Admin ->> CoreAPI: 임의 video_id 삭제 요청 + CoreAPI ->> MetaDB: Video.status를 DELETING으로 전이 + Note over MetaDB: 이 시점부터 검색 범위에서 즉시 제외 + CoreAPI ->> Broker: DELETE_REQUEST 발행 + CoreAPI -->> Admin: 202 Accepted + Broker ->> PipelineWorker: DELETE_REQUEST 전달 + PipelineWorker ->> MetaDB: VectorIndexEntry, Chunk,
TranscriptSegment, Asset 삭제 (단일 트랜잭션) + PipelineWorker ->> MetaDB: Video 레코드 hard-delete + PipelineWorker ->> ObjStore: 원본 영상, 오디오, 키프레임 삭제 (비동기) + Note over PipelineWorker: 수집 피드백 이벤트 및 학습 데이터셋은 보존 + end +``` + +# 6. Admin_ML_ops + +```mermaid + +sequenceDiagram + participant Admin as Admin Dashboard + participant CoreAPI as Core API Server + participant MetaDB as Metadata DB + participant Broker as Message Broker + participant MLWorker as ML Lifecycle Worker + participant ModelFiles as Model Artifact Files + participant IndexSnaps as Index Snapshot Files + participant MEE as Managed Embedding Endpoint + participant VS as Vector Store + + Note over Admin, CoreAPI: 모든 Admin 요청은 JWT claim role 기반 운영자 권한 검증 + + %% === ML 파이프라인 상태 조회 === + rect rgb(230, 245, 255) + Note over Admin, MetaDB: ML 파이프라인 상태 조회 + Admin ->> CoreAPI: MLPipelineRun 상태 조회 + CoreAPI ->> MetaDB: MLPipelineRun 진행 상태 및 실패 현황 조회 + MetaDB -->> CoreAPI: 실행 상태, 실패 단계/유형,
대기 실행 유무, FAIL/ERROR 구분 + CoreAPI -->> Admin: ML 파이프라인 현황 응답 + end + + %% === ML 파이프라인 수동 재트리거 === + rect rgb(245, 255, 230) + Note over Admin, MLWorker: ML 파이프라인 수동 재트리거 + Admin ->> CoreAPI: ML 파이프라인 재트리거 요청 + CoreAPI ->> MetaDB: MLPipelineRun 상태 확인 + MetaDB -->> CoreAPI: 현재 실행 상태 + CoreAPI ->> MetaDB: MLPipelineRun 상태 갱신 + CoreAPI ->> Broker: ML 재트리거 작업 발행 + CoreAPI -->> Admin: 202 Accepted + Broker ->> MLWorker: 재트리거 작업 전달 + MLWorker ->> MLWorker: 파이프라인 재트리거
(실패한 경우 실패 지점부터 재개) + end + + %% === 모델 롤백 === + rect rgb(255, 245, 230) + Note over Admin, VS: 모델 롤백 + Admin ->> CoreAPI: 모델 롤백 요청 + CoreAPI ->> Broker: ROLLBACK_REQUEST 발행 + CoreAPI -->> Admin: 202 Accepted + Broker ->> MLWorker: 롤백 작업 전달 + + MLWorker ->> MetaDB: ModelRelease 조회 + MetaDB -->> MLWorker: 마지막 정상 서빙 조합 스냅샷 + + MLWorker ->> MetaDB: 문제 모델 버전으로 생성된 데이터 식별 + MLWorker ->> MetaDB: 영향 영상 search_serving_state=ROLLBACK_EXCLUDED + Note over Admin, CoreAPI: 일부 영상이 복구 중이라
현재 검색 범위에서 제외될 수 있음 + + MLWorker ->> ModelFiles: 스냅샷 기준 롤백 대상 모델 아티팩트 조회 + ModelFiles -->> MLWorker: 롤백 대상 모델 아티팩트 + MLWorker ->> MEE: 롤백 대상 모델 로드 요청 + MEE -->> MLWorker: readiness 확인 + + MLWorker ->> IndexSnaps: 스냅샷 기준 인덱스 조회 + IndexSnaps -->> MLWorker: 롤백 대상 인덱스 스냅샷 + MLWorker ->> VS: 스냅샷 인덱스 복원 + VS -->> MLWorker: 복원 완료 + + MLWorker ->> MetaDB: ModelRelease 갱신
(스냅샷 기준 active/previous 복원) + Note over MetaDB: candidate 관련 메타데이터 초기화 + + MLWorker ->> VS: 영향 데이터 백그라운드 재임베딩 및 인덱스 반영 + MLWorker ->> MetaDB: 복구 완료 영상부터 SERVABLE 복귀 + Note over VS: 문제 모델 인덱스와 불필요해진 데이터는 이후 비동기 정리 가능 + end + + + ``` \ No newline at end of file diff --git a/docs/Tech_Spec/feedback_loop_&_admin_ops/Admin_Control_Plane_Spec.md b/docs/Tech_Spec/feedback_loop_&_admin_ops/Admin_Control_Plane_Spec.md new file mode 100644 index 0000000..c306010 --- /dev/null +++ b/docs/Tech_Spec/feedback_loop_&_admin_ops/Admin_Control_Plane_Spec.md @@ -0,0 +1,248 @@ +# [Admin Control Plane] SPEC + +**메타 정보 (Meta)** +- Component ID: `admin-control-plane` +- SOT: `docs/system-design.md` +- Related docs: + - `docs/PRD.md` + - `docs/Tech_Spec/upload_search_Service/Core_Api_Server_Spec.md` + - `docs/Tech_Spec/feedback_loop_&_admin_ops/ML_Pipeline_Execution_Spec.md` + - `docs/Tech_Spec/feedback_loop_&_admin_ops/Model_Release_and_Reindex_Spec.md` +- Status: Draft + +--- + +## 1. 목적과 범위 (Purpose and Scope) + +### 1.1 한 줄 요약 +- Admin Control Plane은 운영자가 video 처리 상태와 ML 운영 상태를 조회하고, 허용된 운영 액션만 기존 async 계약에 맞춰 안전하게 요청할 수 있게 하는 admin 전용 HTTP 표면이다. + +### 1.2 책임 경계 +- In scope: + - 운영자 권한 검증을 거친 admin 전용 조회/액션 HTTP 계약 + - 소유권 제한 없이 `Video`, `MLPipelineRun`, `ModelRelease` 상태 조회 + - 실패 video 강제 재처리 요청 + - 임의 video 삭제 절차 시작 요청 + - ML 파이프라인 수동 재트리거 요청 + - rollback 요청의 동기 precondition 확인과 control message 발행 +- Out of scope: + - dashboard 화면 구성, polling 주기, 시각화 방식 + - raw log / metrics 저장소의 검색 API 또는 프록시 + - `MLPipelineRun` 내부 상태 전이 의미 + - `ModelRelease` cutover / rollback restore 내부 절차 + - 평가 데이터셋 관리, 모델 설정 관리, 모델 파일 배포 절차 +- Upstream dependencies: + - JWT claim의 admin role + - 외부 observability 인프라의 로그/메트릭 수집 체계 +- Downstream consumers: + - Admin Dashboard + - Core API Server 내부 user/admin 경로 구현 + - Message Broker의 `PREPROCESS_REQUEST`, `DELETE_REQUEST`, `TRAINING_REQUEST`, `ROLLBACK_REQUEST` 소비자 + +### 간단한 흐름 (Simple Flow) +1. 운영자가 admin 경로로 조회 또는 액션 요청을 보낸다. +2. Admin Control Plane은 JWT claim의 role을 기준으로 운영자 권한을 검증한다. +3. 조회 요청은 shared SOT인 `Video`, `MLPipelineRun`, `ModelRelease`를 읽어 응답한다. +4. 액션 요청은 현재 상태를 확인한 뒤 허용된 경우에만 shared SOT를 갱신하거나 기존 control message를 발행한다. +5. 실제 재처리, 삭제 연쇄, ML 실행 요청, rollback restore는 후속 컴포넌트가 수행한다. + +### 1.3 기술 스택 선택 +| 영역 (Area) | 선택안 (Choice) | 왜 이 선택인가 | +| --- | --- | --- | +| Runtime / framework | Core API Server의 admin 전용 FastAPI surface | system design이 admin 기능을 Core API Server 경로로 둔다 | +| Storage / DB | Metadata DB (`Video`, `MLPipelineRun`, `ModelRelease`) | 운영 조회와 동기 precondition 판단의 SOT가 이미 여기에 있다 | +| Messaging / async | 기존 broker control message 발행 | 재처리/삭제/학습/rollback은 기존 async 계약을 그대로 사용한다 | +| Key libraries | Core API와 동일한 JWT 검증, DB session, broker adapter 계층 | user path와 다른 구현 체계를 새로 만들지 않고 auth/error/trace semantics를 재사용한다 | + +--- + +## 2. 계약 (Contracts) + +### 2.1 외부 인터페이스 + +#### 외부 진입 인터페이스 +| Interface | Method / Trigger | Input summary | Output summary | Auth / tenancy | Notes | +| --- | --- | --- | --- | --- | --- | +| `/api/v1/admin/videos` | `GET` | `status?`, `failed_stage?`, `user_id?`, `cursor?`, `limit?` | `200 OK`, `items[]`, `next_cursor` | admin role 필수, user ownership filter 없음 | 정렬은 `(updated_at DESC, id DESC)` | +| `/api/v1/admin/videos/{video_id}` | `GET` | path `video_id` | `200 OK`, 단일 video 상태와 실패 요약 | admin role 필수 | raw log 본문은 포함하지 않는다 | +| `/api/v1/admin/videos/{video_id}/retry` | `POST` | path `video_id` | `202 Accepted`, `{"video_id","status":"PENDING","retry_requested":true}` | admin role 필수 | `FAILED`일 때만 허용 | +| `/api/v1/admin/videos/{video_id}` | `DELETE` | path `video_id` | `202 Accepted`, `{"video_id","delete_requested":true}` | admin role 필수 | 삭제 절차 시작 요청만 수행한다 | +| `/api/v1/admin/ml-pipeline-runs` | `GET` | `status?`, `failure_type?`, `cursor?`, `limit?` | `200 OK`, `items[]`, `next_cursor` | admin role 필수 | 정렬은 `(created_at DESC, id DESC)` | +| `/api/v1/admin/ml-pipeline-runs/{run_id}` | `GET` | path `run_id` | `200 OK`, 단일 run 상태와 실패 요약 | admin role 필수 | 평가 상세 artifact 본문은 포함하지 않는다 | +| `/api/v1/admin/ml-pipeline-runs/retrigger` | `POST` | empty body | `202 Accepted`, `{"retrigger_requested":true}` | admin role 필수 | `TRAINING_REQUEST` 발행 요청 | +| `/api/v1/admin/model-release` | `GET` | empty | `200 OK`, 현재 serving 조합과 rollback 가능 여부 | admin role 필수 | `ModelRelease` 단일 레코드 조회 | +| `/api/v1/admin/model-release/rollback` | `POST` | empty body | `202 Accepted`, `{"rollback_requested":true}` | admin role 필수 | precondition을 통과한 경우만 `ROLLBACK_REQUEST` 발행 | + +Response shape rules: +- `GET /api/v1/admin/videos`의 각 item은 최소 `video_id`, `user_id`, `status`, `failed_stage`, `search_serving_state`, `updated_at`를 포함한다. +- `GET /api/v1/admin/videos/{video_id}`는 목록 item 필드에 더해 최소 `title`, `category`, `input_type`, `source_url`, `created_at`를 포함한다. +- `GET /api/v1/admin/ml-pipeline-runs`의 각 item은 최소 `run_id`, `status`, `failed_stage`, `failure_type`, `dataset_version`, `candidate_model_version`, `created_at`, `updated_at`를 포함한다. +- `GET /api/v1/admin/ml-pipeline-runs/{run_id}`는 목록 item 필드에 더해 최소 `failure_reason`, `evaluation_id`, `candidate_index_name`, `cutover_time`를 포함한다. +- `GET /api/v1/admin/model-release`는 최소 `release_status`, `active_model_version`, `active_index_name`, `previous_model_version`, `previous_index_name`, `candidate_model_version`, `candidate_index_name`, `rollback_snapshot_captured_at`, `rollback_available`를 포함한다. +- 위에 열거한 최소 필드는 응답에서 항상 같은 key로 제공한다. 값이 현재 상태에 없으면 key를 생략하지 않고 `null`로 표현 한다. +- `source_url`은 `input_type=LOCAL_FILE`이면 `null`이다. +- `failed_stage`, `failure_type`, `failure_reason`, `evaluation_id`, `candidate_model_version`, `candidate_index_name`, `cutover_time`는 해당 상태에서 값이 없으면 `null`이다. +- `previous_model_version`, `previous_index_name`, `candidate_model_version`, `candidate_index_name`, `rollback_snapshot_captured_at`는 현재 release 상태에서 값이 없으면 `null`이다. +- `rollback_available`은 `release_status=STABLE`이고 rollback snapshot 포인터가 모두 존재할 때만 `true`다. +- list filter는 exact-match만 지원하며, 여러 filter가 함께 주어지면 AND로 적용한다. +- `cursor`는 정렬 키를 담는 opaque token이며, video list는 `(updated_at,id)`, run list는 `(created_at,id)`를 기준으로 한다. +- `cursor`는 생성 당시의 filter set에 바인딩된다. filter를 바꾼 뒤 이전 `cursor`를 재사용하면 `400 INVALID_ARGUMENT`로 거부한다. + +#### 메시지 / 이벤트 계약 (해당 시) +- Queue / topic: + - `PREPROCESS_REQUEST` + - `DELETE_REQUEST` + - `TRAINING_REQUEST` + - `ROLLBACK_REQUEST` +- Producer / consumer responsibility: + - Producer(Admin Control Plane): + - video retry 승인 시 `PREPROCESS_REQUEST`를 발행한다. + - video delete 승인 시 `DELETE_REQUEST`를 발행한다. + - ML retrigger 요청 시 `TRAINING_REQUEST`를 발행한다. + - rollback precondition 통과 시 `ROLLBACK_REQUEST`를 발행한다. + - Consumer: + - video worker는 `PREPROCESS_REQUEST`, `DELETE_REQUEST`를 처리한다. + - ML Pipeline Execution은 `TRAINING_REQUEST`를 받아 활성 실행 1개와 최신 대기 실행 1개만 유지하도록 처리한다. + - Model Release and Reindex는 `ROLLBACK_REQUEST`를 rollback 규칙에 따라 처리한다. +- Delivery semantics: at-least-once +- Payload versioning rules: + - `PREPROCESS_REQUEST`, `DELETE_REQUEST`는 `docs/system-design.md` 3.12의 video-processing shared envelope를 따른다. + - `TRAINING_REQUEST`, `ROLLBACK_REQUEST`는 같은 section의 control-message schema를 따른다. + - Admin Control Plane은 새 payload shape를 정의하지 않는다. + +```json +{ + "message_type": "ROLLBACK_REQUEST", + "payload_version": "v1", + "trace_id": "UUID4", + "attempt": 1, + "issued_at": "ISO8601_TIMESTAMP" +} +``` + +#### 외부 연동 컴포넌트 계약 (해당 시) +| Dependency | Used for | Required behavior / assumption | Failure impact | +| --- | --- | --- | --- | +| Metadata DB `Video` | video 상태 조회, retry/delete precondition 확인 | `status`, `failed_stage`, `search_serving_state`, `updated_at`를 최신 기준으로 읽고 갱신할 수 있어야 한다 | 잘못된 운영 판단 또는 잘못된 액션 허용으로 이어진다 | +| Metadata DB `MLPipelineRun` | run 목록/상세 조회 | `status`, `failed_stage`, `failure_type`, `failure_reason`, `dataset_version`, `candidate_model_version`를 읽을 수 있어야 한다 | 운영자가 실패 상태를 정확히 파악할 수 없다 | +| Metadata DB `ModelRelease` | serving 상태 조회, rollback precondition 확인 | `release_status`와 rollback snapshot 포인터를 동기 조회할 수 있어야 한다 | invalid rollback 요청을 동기 차단할 수 없다 | +| Message Broker | async control message 발행 | 동일 `trace_id`를 포함해 메시지를 발행할 수 있어야 한다 | 액션은 접수되었지만 후속 실행이 시작되지 않는다 | +| 외부 observability 인프라 | trace 기반 장애 분석 | backend 로그/메트릭이 `trace_id`와 자원 식별자를 기준으로 조회 가능해야 한다 | Admin Dashboard의 심화 장애 분석은 admin API만으로 완결되지 않는다 | + +### 2.2 데이터 계약 + +#### 소유 데이터 (이 컴포넌트가 SOT인 경우) +- 이 컴포넌트는 신규 SOT 엔터티를 만들지 않는다. +- 이 컴포넌트가 소유하는 것은 admin-only HTTP contract와 그에 따른 허용 액션 규칙이다. + +#### 참조 데이터 (다른 SOT를 읽는 경우) +| Source owner | Entity / table | Fields relied on | Read-only assumptions | +| --- | --- | --- | --- | +| Core video lifecycle | `Video` | `id`, `user_id`, `status`, `failed_stage`, `search_serving_state`, `created_at`, `updated_at` | retry 가능 여부는 `status=FAILED`로만 판단한다 | +| ML Pipeline Execution | `MLPipelineRun` | `id`, `status`, `failed_stage`, `failure_type`, `failure_reason`, `dataset_version`, `candidate_model_version`, `evaluation_id`, `created_at`, `updated_at` | 활성 실행 1개와 최신 대기 실행 1개 규칙은 ML Pipeline Execution의 SOT와 이를 집행하는 ML Lifecycle Worker가 강제한다. | +| Model Release and Reindex | `ModelRelease` | `release_status`, `active_model_version`, `active_index_name`, `previous_model_version`, `previous_index_name`, `rollback_snapshot_active_model_version`, `rollback_snapshot_active_index_name`, `rollback_snapshot_captured_at`, `switched_at` | rollback 허용 여부는 `release_status`와 snapshot 포인터 존재로 판단한다 | + +### 2.3 상태 및 비즈니스 규칙 +- 항상 유지되어야 하는 불변조건: + - admin 경로는 JWT claim의 role이 운영자 권한일 때만 접근할 수 있다. + - admin 경로는 user ownership 제한을 우회할 수 있지만, user 경로의 tenancy 규칙을 바꾸지 않는다. + - video retry는 `Video.status=FAILED`일 때만 허용된다. + - rollback 요청은 `ModelRelease.release_status=STABLE`이고 rollback snapshot 포인터가 존재할 때만 허용된다. + - admin API는 raw log/metrics 저장소를 직접 프록시하지 않고, domain 상태와 control action만 제공한다. +- 거부되어야 하는 전이 / invalid condition: + - `FAILED`가 아닌 video에 retry를 요청하는 동작 + - 존재하지 않는 `video_id` 또는 `run_id`에 대한 조회/액션 + - rollback snapshot이 없거나 `release_status!=STABLE`인 상태에서 rollback을 요청하는 동작 + - non-admin 사용자가 admin 경로에 접근하는 동작 +- Idempotency rule: + - 이미 `DELETING`인 video에 대한 admin delete는 `202 Accepted`로 처리하되 새 `DELETE_REQUEST`를 추가 발행하지 않는다. + - 중복 `TRAINING_REQUEST`가 들어오면 downstream은 활성 실행 1개와 최신 대기 실행 1개만 남도록 정리한다. + - 중복 `ROLLBACK_REQUEST`는 admin path가 precondition을 먼저 검사하고, downstream은 이미 같은 rollback 요청이 처리 중이거나 완료된 경우 추가 변경 없이 그대로 유지할 수 있어야 한다. +- Multi-tenant / authorization rule: + - admin path는 소유권 확인 대신 운영자 role만 본다. + - 응답에는 운영 판단에 필요한 shared SOT 필드만 포함하며, 민감 원문 로그나 평가 artifact 본문은 포함하지 않는다. + +| From | To | Trigger | Guard / rule | Required side effects | +| --- | --- | --- | --- | --- | +| `Video.status=FAILED` | `PENDING` | admin retry 요청 | target video 존재, admin role 확인 | `PREPROCESS_REQUEST` 발행 | +| `Video.status!=DELETING` | `DELETING` | admin delete 요청 | target video 존재, admin role 확인 | `DELETE_REQUEST` 발행 | +| `Video.status=DELETING` | `DELETING` | admin delete 재요청 | 중복 삭제 요청은 허용하되 추가 publish 없음 | 기존 delete 절차 유지 | +| `ModelRelease.release_status=STABLE` | direct state change 없음 | admin rollback 요청 | rollback snapshot 포인터 존재, admin role 확인 | `ROLLBACK_REQUEST` 발행 | + +### 2.4 한계와 운영 제약 +- Performance / latency target: + - admin 조회는 운영 판단용 동기 경로이며, dashboard polling에 사용할 수 있을 정도로 안정적으로 응답해야 한다. +- Throughput / rate / concurrency limits: + - 목록 조회 pagination은 기본 20, 최대 50으로 제한한다. + - 동시에 들어온 운영 액션의 최종 적용 순서와 충돌 해결은 shared SOT(Video, MLPipelineRun, ModelRelease)와 후속 처리 컴포넌트가 담당한다. +- Payload / file size / pagination limits: + - admin 조회 응답은 상태 요약과 식별자만 포함하며, 대용량 raw log/metrics payload는 포함하지 않는다. +- Timeout / TTL / retry constraints: + - broker publish 재시도 횟수와 backoff 값은 Core API 운영 정책을 따른다. + - `TRAINING_REQUEST`와 `ROLLBACK_REQUEST`의 최종 실행 여부는 동기 응답이 아니라 downstream 처리 결과로 확정된다. +- Security / privacy constraints: + - admin 경로는 모든 사용자 자원 메타데이터를 읽을 수 있으므로 내부 운영 경로로만 노출되어야 한다. + - `query_text`, raw transcript, raw logs, 평가 상세 artifact 원문은 이 API의 기본 응답에 포함하지 않는다. + +### 2.5 에러 계약 +| Surface | Condition | Code / status | Retryable | Notes | +| --- | --- | --- | --- | --- | +| 모든 admin 경로 | JWT 누락 또는 검증 실패 | `401 UNAUTHENTICATED` | N | user path와 같은 인증 실패 의미 | +| 모든 admin 경로 | admin role 부재 | `403 FORBIDDEN` | N | ownership이 아니라 role 기준 거부 | +| list/query | 잘못된 cursor 또는 filter 값 | `400 INVALID_ARGUMENT` | N | pagination/filter contract 위반 | +| video 조회/액션 | `video_id` 미존재 | `404 NOT_FOUND` | N | | +| run 조회 | `run_id` 미존재 | `404 NOT_FOUND` | N | | +| retry | `Video.status!=FAILED` | `409 CONFLICT` | N | 강제 재처리는 실패 건만 허용 | +| rollback | `release_status!=STABLE` 또는 snapshot 포인터 부재 | `409 CONFLICT` | N | 동기 precondition 차단 | +| action path | DB 갱신 또는 broker publish 최종 실패 | `500 INTERNAL_ERROR` | Y | operator는 같은 요청을 재시도할 수 있다 | + +- 표준 에러 응답 형태: +```json +{"code":"ERROR_CODE","message":"human-readable summary","trace_id":"UUID4"} +``` + +--- + +## 3. 관측성과 운영 (Observability and Operations) + +- Required log fields: + - `trace_id` + - `operator_user_id` + - `admin_action` + - `target_type` + - `target_id` + - `result` + - `http_status` +- Key metrics / alerts worth tracking: + - `admin_request_count{action,result}` + - `admin_retry_request_count` + - `admin_delete_request_count` + - `admin_ml_retrigger_request_count` + - `admin_rollback_request_count` + - `admin_precondition_conflict_count{action}` + - `admin_broker_publish_failure_count{message_type}` +- Trace / correlation propagation rule: + - admin 요청에서 생성하거나 전달받은 `trace_id`는 응답, 구조화 로그, 후속 broker message에 동일하게 유지한다. +- Reconciliation / cleanup requirement (if any): + - 별도 admin 전용 정리 배치는 두지 않는다. + - delete/retry/retrigger/rollback의 최종 상태 정합성은 shared SOT와 downstream consumer 이력으로 재구성 가능해야 한다. + +--- + +## 4. 인수 기준 (Acceptance Criteria) + +### 4.1 반드시 통과해야 하는 시나리오 +- [ ] admin role을 가진 운영자는 소유권 제한 없이 `Video`, `MLPipelineRun`, `ModelRelease` 상태를 조회할 수 있다. +- [ ] admin retry는 `Video.status=FAILED`인 대상에만 허용되고, 승인 시 `PENDING` 전환과 `PREPROCESS_REQUEST` 발행이 함께 일어난다. +- [ ] admin delete는 대상 video를 `DELETING`으로 전환하고 `DELETE_REQUEST`를 발행하며, 이미 `DELETING`이면 중복 publish 없이 수용된다. +- [ ] admin ML retrigger 요청은 `TRAINING_REQUEST`를 발행하고, 중복 요청이 와도 downstream의 활성 실행/최신 대기 실행 규칙을 깨지 않는다. +- [ ] admin rollback 요청은 `release_status=STABLE`과 rollback snapshot 존재를 동기 검증한 뒤에만 `ROLLBACK_REQUEST`를 발행한다. +- [ ] 모든 admin 액션은 `trace_id`, `operator_user_id`, action 결과를 구조화 로그에 남기며, 후속 async message와 상관관계를 유지한다. + +### 4.2 비목표 / 보류 항목 +- Dashboard UI layout, widget 구성, polling 주기 +- raw log / metrics / Grafana query proxy +- 평가 데이터셋 CRUD와 모델 설정 관리 +- rollback restore 이후의 재임베딩 진행도 표현 방식 + +--- diff --git a/docs/Tech_Spec/feedback_loop_&_admin_ops/Feedback_Ingestion_Pipeline_Spec.md b/docs/Tech_Spec/feedback_loop_&_admin_ops/Feedback_Ingestion_Pipeline_Spec.md new file mode 100644 index 0000000..0c80433 --- /dev/null +++ b/docs/Tech_Spec/feedback_loop_&_admin_ops/Feedback_Ingestion_Pipeline_Spec.md @@ -0,0 +1,218 @@ +# [Feedback Ingestion Pipeline] SPEC + +**메타 정보 (Meta)** +- Component ID: feedback-ingestion-pipeline +- SOT: `docs/system-design.md` (이 SPEC은 system design SOT와 일관되어야 한다) +- Related docs: `docs/Tech_Spec/upload_search_Service/Core_Api_Server_Spec.md`, `docs/Tech_Spec/upload_search_Service/Search_Service_Spec.md` +- Status: Draft (cycle-2) + +--- + +## 1. 목적과 범위 (Purpose and Scope) + +### 1.1 한 줄 요약 +- Feedback Ingestion Pipeline(FIP)은 Core API Server가 검증 완료한 검색 응답 단위 피드백 이벤트를 비동기로 수신하여, 손실을 최소화하면서 수정 불가능한 원본 로그로 Object Storage에 적재하는 컴포넌트다. + +### 1.2 책임 경계 +- In scope: + - Core API가 발행한 validated feedback event 읽기 + - 원본 이벤트를 append-only 로그로 Object Storage에 저장 + - 재전송/중복 수신 가능성을 전제로 손실 최소화 계약 제공 + - trace_id/event_id 기준 관측성과 운영 재조정 근거 제공 +- Out of scope: + - 사용자 인증/인가, req_id 유효성 검증, 허용 시간 창 판정 + - SearchResponseSnapshot 생성/수정/만료 관리 + - 피드백 집계, 라벨 정제, 학습용 데이터셋 생성 + - ranking/학습 로직, offline analytics schema 설계 +- Upstream dependencies: + - Client가 호출한 Core API Server + - Metadata DB의 `SearchResponseSnapshot` (검증 책임은 Core API 소유) + - Message Broker +- Downstream consumers: + - Object Storage 원본 이벤트 로그 + - ML Lifecycle Worker의 피드백 데이터셋 생성 배치 + +간단한 흐름: +1. Client가 Core API Server에 검색 응답 단위 feedback를 보낸다. +2. Core API Server가 `req_id`, 사용자, 허용 시간 창을 검증하고 검색 시점 문맥이 포함된 validated feedback event를 만든다. +3. Core API Server가 이 이벤트를 broker에 publish하면, 사용자 요청 경로의 핵심 처리는 끝난다. +4. FIP는 Vector 파이프라인으로 broker 이벤트를 읽는다. +5. Vector는 설정된 라우팅 규칙에 따라 이벤트를 Object Storage의 append-only raw log로 보낸다. +6. broker 또는 Object Storage에 일시 장애가 있으면, Vector와 broker 설정에 따라 버퍼링 또는 재전달 경로를 따른다. + +### 1.3 기술 스택 선택 +| 영역 (Area) | 선택안 (Choice) | 왜 이 선택인가 | +| --- | --- | --- | +| Ingestion runtime | Vector | `schema_version` 기반 라우팅과 멀티 sink(raw_logs/error_logs/)를 설정으로 구성할 수 있다. broker에서 읽고 Object Storage로 쓰는 과정의 버퍼링, 재시도, 처리 완료 확인은 Vector source/sink 설정과 broker 재전달 메커니즘을 따른다. Logstash 대비 경량(Rust 기반, JVM 불필요)이며, 별도 수집 프로그램을 새로 만들 필요가 적다. | + + +--- + +## 2. 계약 (Contracts) + +### 2.1 외부 인터페이스 + +#### 외부 진입 인터페이스 +| Interface | Method / Trigger | Input summary | Output summary | Auth / tenancy | Notes | +| --- | --- | --- | --- | --- | --- | +| Vector ingestion pipeline | broker queue에 validated feedback event 도착 | 검색 응답 단위 피드백 1건 | 정상 이벤트는 raw_logs sink로 보내고, 구조상 처리할 수 없는 이벤트는 error_logs sink로 분기 | 사용자 인증은 upstream에서 완료 | FIP는 public HTTP API를 소유하지 않는다 | + +#### 메시지 / 이벤트 계약 (해당 시) +- Transport: Core API가 broker로 publish하고, FIP는 Vector source를 통해 비동기로 읽는다. +- Routing contract: + - Core API와 FIP는 feedback event 전용 PostgreSQL MQ queue를 공유한다. + - queue 이름과 PostgreSQL MQ 설정값은 FIP PLAN 또는 배포 설정에서 정한다. +- Producer / pipeline responsibility: + - Producer(Core API): `req_id` 스냅샷 검증, 사용자/시간 창/무효화 여부 검사, feedback 전용 계약으로 broker publish + - FIP(Vector pipeline): 수신 이벤트를 원본 로그 형태로 Object Storage에 보내고 trace/event 기준으로 관측 가능하게 유지 +- Delivery semantics: at-least-once 기준. 중복 수신 가능성을 허용하고 손실 최소화를 우선한다. +- Payload versioning rules: + - feedback 전용 schema를 사용한다. 기존 video_id 중심 async contract에 끼워 넣지 않는다. + - 버전 식별자 필드는 `schema_version`(정수)이며 반드시 포함되어야 한다. 현재 지원 버전은 `1`이다. + - 호환되지 않는 구조 변경은 새 정수 버전으로만 도입한다. minor/patch 구분은 두지 않는다. + - Vector 라우팅 설정은 지원하지 않는 `schema_version` 값을 정상 raw log가 아니라 error_logs sink로 보낸다. +- 최소 포함 정보: + - 식별/추적: `event_id`, `req_id`, `user_id`, `created_at`, `schema_version`, `trace_id` 또는 동등한 상관관계 키 + - 피드백 본문: `rating` + - 검색 시점 문맥: `query_text`, `topk_ids`, `used_ids`, `active_model_version`, `active_index_name`, `response_snapshot_ref` +- 필드 의미는 `docs/system-design.md`의 `Feedback Event` 및 `SearchResponseSnapshot` 정의를 따른다. + + +#### 외부 연동 컴포넌트 계약 (해당 시) +| Dependency | Used for | Required behavior / assumption | Failure impact | +| --- | --- | --- | --- | +| Core API Server | 검증된 피드백 이벤트 생산 | invalid req_id/타사용자/허용 시간 초과 이벤트는 publish 전에 차단한다 | 잘못된 upstream 검증은 잘못된 raw log로 이어진다 | +| Message Broker | validated event 전달 | 일시 장애 후 재시도 가능한 publish/consume 경로를 제공한다 | 적재 지연 또는 재전달 증가 | +| Object Storage | append-only raw log 보관 | 원본 이벤트를 overwrite 없이 보존 가능해야 한다 | 장기 장애 시 적재 지연 또는 누적 적체 | + +### 2.2 데이터 계약 + +#### 소유 데이터 (이 컴포넌트가 SOT인 경우) +| Entity / table | Purpose | Key fields / invariants | Notes | +| --- | --- | --- | --- | +| Feedback Event raw log (Object Storage) | 검색 응답 단위 원본 피드백 보존 | append-only, 원본 의미 보존 | FIP가 원본 로그 적재 책임을 가진다 | + +논리 데이터 모델 ( Feedback Event : `docs/system-design.md` 3.5 기반): +- `schema_version` (정수, 현재 `1`) +- `event_id` +- `user_id` +- `req_id` +- `query_text` +- `rating` +- `topk_ids` +- `used_ids` +- `active_model_version` +- `active_index_name` +- `response_snapshot_ref` +- `created_at` + +#### 참조 데이터 (다른 SOT를 읽는 경우) +| Source owner | Entity / table | Fields relied on | Read-only assumptions | +| --- | --- | --- | --- | +| Search Service / Metadata DB | `SearchResponseSnapshot` | `req_id`, `user_id`, `query_text`, `topk_ids`, `used_ids`, `active_model_version`, `active_index_name`, `created_at`, `expires_at` | FIP는 직접 조회하지 않아도 되며, upstream validation이 이 스냅샷에 기반한다고 가정한다 | + +### 2.3 상태 및 비즈니스 규칙 +- 항상 유지되어야 하는 불변조건: + - FIP는 검증된 feedback event만 처리 대상으로 간주한다. + - raw log는 append-only이며, 적재 후 의미를 바꾸는 in-place 수정 경로를 두지 않는다. + - 한 raw event는 검색 시점의 핵심 문맥(`query_text`, `topk_ids`, `used_ids`, 모델/인덱스 정보)을 함께 포함해야 한다. +- 이 파이프라인이 표현하는 처리 결과: + - `validated event received -> raw log persisted` + - `validated event received -> temporary retry pending` (broker/object storage 일시 실패 시) + - retry pending은 저장이 아직 끝나지 않아 Vector/broker 설정에 따라 다시 처리될 수 있는 상태를 의미한다. +- 거부되어야 하는 전이 / invalid condition: + - 지원하지 않는 버전의 데이터를 무단으로 정상 저장하는 행위 + - raw log를 후처리 결과로 overwrite 하는 동작 +- Idempotency / dedupe contract: + - `event_id`는 feedback event 단위의 전역 중복 제거 키다. + - FIP는 at-least-once 전달로 인한 같은 `event_id`의 재수신을 허용하며, raw log에는 중복 수신 사실을 보존할 수 있다. + - curation dataset 생성 단계는 raw log를 읽을 때 `event_id` 기준으로 중복 제거를 수행한다. + - 최종 curation dataset에는 동일한 `event_id`가 두 번 반영되지 않아야 한다. +- Multi-tenant / authorization rule: + - 테넌시 검증 책임은 Core API에 있다. + - FIP는 메시지 내 `user_id`를 raw log에 그대로 보존하지만 별도 권한 판정을 수행하지 않는다. +- Persistence / retry boundary: + - FIP는 feedback event가 raw log에 영속화된 뒤에만 정상 처리 완료로 간주한다. + - 영속화 실패 event는 폐기하지 않고 재처리 가능한 상태로 보존한다. + - retry가 고갈된 event는 원본 payload, `event_id`, `trace_id`, 실패 원인을 `error_logs/` 또는 동등한 quarantine sink에 남긴다. + - retry/backoff/buffer/timeout의 구체 값은 FIP PLAN에서 정한다. +- Vector config contract: + - Vector 설정은 FIP의 schema routing, raw/error sink 분기, retry/buffer 동작을 정의하는 버전 관리 대상이다. + + +### 2.4 한계와 운영 제약 +- Performance / latency target: + - 사용자 동기 응답이 아니라 비동기 적재 경로이므로 per-event 저지연보다 손실 최소화와 backlog 회복 가능성을 우선한다. +- Throughput / rate / concurrency limits: + - concurrency/batch size 구체 수치는 FIP PLAN 문서에서 확정한다. + backlog depth와 ingestion lag을 운영 지표로 본다. +- Payload / file size / pagination limits: + - 이벤트 payload는 단일 검색 응답 문맥만 포함하며, 대용량 본문/원본 answer 전체 장기 저장은 범위 밖이다. +- Timeout / TTL / retry constraints: + - SearchResponseSnapshot TTL과 허용 피드백 시간 창은 upstream/system 정책이다. + - Object Storage / broker 일시 장애는 Vector의 buffer/retry 설정과 broker 재전달 경로로 처리한다. 브로커 레이어의 별도 DLQ는 두지 않는다. + - 미지원 version 또는 필수 필드 누락처럼 구조상 정상 저장할 수 없는 메시지는 Vector 라우팅 설정으로 `error_logs/` 경로에 보존한다. 이 경로는 운영 알림 트리거 대상이며, schema 호환 후 수동 재처리 입력으로 사용할 수 있다. + - retry, buffer, timeout 관련 값은 FIP PLAN 문서에서 Vector와 broker 설정값으로 확정한다. +- Security / privacy constraints: + - raw log에는 사용자 식별자와 질의 텍스트가 포함되므로 운영 접근은 최소 권한으로 제한해야 한다. + - 원본 로그는 데이터셋 생성 입력으로 쓰이더라도 개인정보/민감질의 처리 정책을 우회하는 저장소가 되어서는 안 된다. + +### 2.5 에러 계약 +| Surface | Condition | Code / status | Retryable | Notes | +| --- | --- | --- | --- | --- | +| Vector transform | 미지원 `schema_version` | unsupported_schema_version | N | `error_logs/` sink에 원본 메시지를 보존한다 | +| Vector transform | 필수 정보 누락 또는 구조 불일치 | malformed_feedback_event | N | `error_logs/` sink에 원본 메시지를 보존한다 | +| Vector sink | Object Storage 일시 실패 | transient persistence failure | Y | Vector buffer/retry와 broker 재전달 설정에 따라 다시 처리될 수 있어야 한다 | +| Vector source/sink | Broker 재전달로 동일 `event_id` 재수신 | duplicate delivery | Y | at-least-once의 정상 범주 | + +- FIP는 사용자 권한, `req_id` 소유자, 피드백 허용 시간 창 같은 의미 검증을 재수행하지 않는다. 이 검증은 Core API 책임이다. +- upstream 검증 누락으로 의미상 잘못된 데이터가 들어온 경우, FIP는 원본을 보존하고 이후 품질 점검이나 운영 분석에서 탐지할 수 있게 추적 정보를 남긴다. + +- FIP는 public HTTP API를 소유하지 않으므로 외부 에러 응답 바디 형식은 본 SPEC 범위가 아니다. + +--- + +## 3. 관측성과 운영 (Observability and Operations) + +- Required log fields: + - `trace_id`(또는 동등한 상관관계 키), `event_id`, `req_id`, `user_id`, `schema_version`, `rating`, `result` +- Key metrics / alerts worth tracking: + - 큐(Queue)에 처리되지 못하고 밀려있는 피드백 대기열 수 (Backlog) + - 원본 로그의 저장 성공 및 실패 건수 + - 통신 오류 등으로 인한 재시도 및 재전송 발생 횟수 + - 중복 여부를 나중에 확인할 수 있도록 raw log와 Vector 운영 로그에 남는 `event_id` + - 데이터 수집 지연 시간 (피드백이 큐에 들어온 시점부터 최종 저장될 때까지 걸린 시간) + - `error_logs/` error sink에 적재된 non-retryable 메시지 건수 (알림 트리거 대상) + - 데이터 전송 파이프라인(Vector) 내의 임시 보관량(Buffer), 재시도 현황, 처리 완료 상태를 운영자가 모니터링할 수 있어야 한다. +- Trace / correlation propagation rule: + - Core API가 생성/전달한 `trace_id`를 broker message와 FIP 로그, object metadata(지원 시)에 동일하게 유지한다. + - `event_id`는 이벤트 단위 상관관계 키, `req_id`는 검색 응답 단위 상관관계 키로 함께 남긴다. +- Service level contract: + - validated feedback event는 정상 상태에서 p95 5분 이내에 raw log로 적재되어야 한다. + - raw log 적재 실패율은 월간 0.1% 이하를 목표로 한다. + - PostgreSQL MQ backlog와 ingestion lag은 운영자가 확인할 수 있는 지표로 노출한다. + +--- + +## 4. 인수 기준 (Acceptance Criteria) + +### 4.1 반드시 통과해야 하는 시나리오 +- [ ] Core API가 `SearchResponseSnapshot` 검증을 통과한 feedback event를 broker에 publish하면, Vector 파이프라인이 이를 읽어 Object Storage raw log에 필요한 문맥 필드와 함께 남긴다. +- [ ] transport는 검색/영상 처리용 기존 video_id 중심 메시지 계약과 분리된 feedback 전용 schema를 사용한다. +- [ ] duplicate delivery가 발생해도 이벤트 손실 없이 운영자가 `event_id`/`trace_id` 기준으로 추적할 수 있으며, raw log는 중복 수신 사실을 보존한다. +- [ ] Object Storage 일시 장애 시 즉시 영구 손실로 간주하지 않고 retry/re-delivery 가능한 경로를 따른다. +- [ ] raw log는 `docs/system-design.md`의 `Feedback Event` 논리 필드를 보존한다. +- [ ] 미지원 version 식별자 또는 필수 필드 누락 메시지 수신 시 Vector 라우팅 설정에 따라 raw log가 아니라 `error_logs/` sink에 원본 메시지를 보존하며 운영 알림이 발생한다. + + + +--- + + +## 5. 참고 문서 (References) +- Related specs: + - `docs/Tech_Spec/upload_search_Service/Core_Api_Server_Spec.md` + - `docs/Tech_Spec/upload_search_Service/Search_Service_Spec.md` +- Diagrams or schemas: + - `docs/system-design.md` §2.6 Feedback 수집, §3.5 Feedback Event, §3.6 SearchResponseSnapshot + - `docs/Diagram/sequence_diagram/SD_Mermaid.md` diff --git a/docs/Tech_Spec/feedback_loop_&_admin_ops/ML_Pipeline_Execution_Spec.md b/docs/Tech_Spec/feedback_loop_&_admin_ops/ML_Pipeline_Execution_Spec.md new file mode 100644 index 0000000..1174641 --- /dev/null +++ b/docs/Tech_Spec/feedback_loop_&_admin_ops/ML_Pipeline_Execution_Spec.md @@ -0,0 +1,281 @@ +# [ML Pipeline Execution] SPEC + +**메타 정보 (Meta)** +- Component ID: `ml-pipeline-execution` +- SOT: `docs/system-design.md` +- Related docs: + - `docs/PRD.md` + - `docs/Tech_Spec/feedback_loop_&_admin_ops/Feedback_Ingestion_Pipeline_Spec.md` + - `docs/Tech_Spec/upload_search_Service/Managed_Embedding_Endpoint_Spec.md` + - `docs/Tech_Spec/feedback_loop_&_admin_ops/Model_Release_and_Reindex_Spec.md` (후속 작성 ) +- Status: Draft + +--- + +## 1. 목적과 범위 (Purpose and Scope) + +### 1.1 한 줄 요약 +- ML Pipeline Execution은 신규 피드백 로그를 학습 데이터셋 버전으로 만들고, 그 최신 버전으로 후보 임베딩 모델을 학습·평가하며, 동시에 1개의 활성 실행만 유지하도록 파이프라인을 제어하는 컴포넌트다. + +### 1.2 책임 경계 +- In scope: + - 피드백 원본 로그를 읽어 학습용 데이터셋 버전을 생성한다. + - 정기 배치와 수동 재트리거를 동일한 실행 계약으로 받아들인다. + - Scheduler는 실행 필요성을 만들고, 최신 데이터셋 기준으로 실행 수렴을 시작한다. + - Driver는 정상 경로의 상태 전진을 관리한다. + - Reconciler는 장시간 진전이 없는 실행을 식별하고, 복구 또는 실패 정리의 시작 책임을 가진다. + - 동시에 활성 실행인 `MLPipelineRun`을 하나만 유지한다. + - 실행 중 새 데이터셋이 준비되면 모든 대기 실행을 쌓지 않고 최신 데이터셋 기준 다음 실행 하나만 유지한다. + - 후보 모델을 학습하고 Model Artifact Files에 저장한다. + - 후보 모델과 기준 모델을 평가용 데이터셋으로 비교 평가한다. + - `MLPipelineRun`, `ModelEvaluation`, 평가 상세 아티팩트를 기록한다. + - 평가 `PASS` 시 hand off 준비 상태를 남기고, 같은 Worker 내부의 다음 책임이 이어받을 수 있도록 hand off한다. +- Out of scope: + - 피드백 이벤트 검증과 원본 로그 적재 + - 후보 인덱스 구축, dual-write, cutover 시각 계산 + - Managed Embedding Endpoint의 모델 로드 방식과 readiness 내부 동작 + - `ModelRelease` 갱신, 서빙 전환, 롤백 실행 + - 운영자용 HTTP API 표면 +- Upstream dependencies: + - Feedback Ingestion Pipeline이 적재한 원본 피드백 로그 + - 운영자가 관리하는 변경 불가 평가 데이터셋 + - 현재 활성 모델 버전을 담은 `ModelRelease` + - 정기 스케줄러 또는 수동 재트리거 발행자 +- Downstream consumers: + - Model Artifact Files의 후보 모델 산출물 + - Metadata DB의 `MLPipelineRun`, `ModelEvaluation` + - Object Storage의 학습 데이터셋, 평가 상세 아티팩트 + - 후속 `Model_Release_and_Reindex` 단계 + +### 간단한 흐름 (Simple Flow) +1. 원본 피드백 로그에서 신규 이벤트를 읽어 학습 데이터셋 버전을 만든다. +2. 활성 실행이 없으면 즉시 실행을 시작하고, 있으면 최신 데이터셋 기준 다음 실행 하나만 남긴다. +3. 실행이 시작되면 기준 모델 버전과 데이터셋 버전을 고정하고 후보 모델을 학습한다. +4. 후보 모델을 변경 불가 평가 데이터셋으로 기준 모델과 비교 평가한다. +5. 평가 `PASS`면 재색인 단계로 넘기고, `FAIL` 또는 시스템 오류면 실패로 종료한다. + + +--- + +## 2. 계약 (Contracts) + +### 2.1 외부 인터페이스 + +#### 외부 진입 인터페이스 +| Interface | Method / Trigger | Input summary | Output summary | Auth / tenancy | Notes | +| --- | --- | --- | --- | --- | --- | +| 정기 배치 트리거 | 스케줄 도래 | raw feedback log의 신규 구간 | 새 학습 데이터셋 생성, 필요 시 실행 시작 또는 대기 실행 갱신 | 내부 운영 경로 | Scheduler는 배치 실행 필요성을 만들고, 시스템이 최신 데이터셋 기준으로 수렴하도록 한다 | +| 학습 실행 consumer | `TRAINING_REQUEST` 수신 | ML 실행 요청용 메시지 | 실행 시작 또는 최신 대기 실행 갱신 | 운영자 권한 검증은 upstream 책임 | 자동 트리거와 수동 재트리거 모두 같은 메시지 계약을 사용한다 | + +#### 내부 hand off 인터페이스 +| Interface | Trigger | Input summary | Output summary | Notes | +| --- | --- | --- | --- | --- | +| 평가 `PASS` 내부 hand off | run이 릴리스 단계로 넘길 준비를 마침 | `run_id`, `trace_id` | 릴리스·재색인 시작 또는 실행 불가 기록 | 같은 Worker 내부 직접 호출만 사용한다. 수신 책임은 `MLPipelineRun`, `ModelEvaluation` 등 공유 SOT를 다시 읽어 문맥을 복원한다 | + +#### 내부 실행 책임 +- Scheduler는 새 데이터셋 생성과 실행 필요성 판단의 시작 책임을 가진다. +- Driver는 `PENDING`, `RUNNING`, `READY_FOR_RELEASE`, `FAILED`, `SUPERSEDED` 상태 전진을 집행한다. +- Reconciler는 장시간 진전이 없는 실행을 식별하고, 복구 가능한 실행은 다시 이어받게 하며, 복구가 불가능한 실행은 운영자가 식별 가능한 실패 상태로 남긴다. + +#### 메시지 / 이벤트 계약 +- Queue / topic: `TRAINING_REQUEST` +- Producer / consumer responsibility: + - Producer: 스케줄러 또는 운영 경로가 실행 요청을 발행한다. + - Consumer: 실행 시작 시점의 최신 학습 데이터셋 버전과 현재 활성 모델 버전을 조회해 이번 run의 기준으로 고정한다. +- Delivery semantics: at-least-once +- Payload versioning rules: + - `TRAINING_REQUEST`는 video 처리 메시지와 분리된 ML 전용 메시지 규격을 사용한다. + - 공통 메타데이터는 `message_type`, `payload_version`, `trace_id`, `attempt`, `issued_at`만 사용한다. + - 학습 대상 `dataset_version`은 payload에 넣지 않는다. Consumer가 시작 시점에 최신 버전을 조회한다. + - 지원하지 않는 `payload_version`은 정상 실행으로 처리하지 않는다. + +```json +{ + "message_type": "TRAINING_REQUEST", + "payload_version": "v1", + "trace_id": "UUID4", + "attempt": 1, + "issued_at": "ISO8601_TIMESTAMP" +} +``` + +Notes: +- 이 메시지의 실행 키는 `dataset_version`과 `MLPipelineRun.id`다. + +#### 외부 서비스 계약 +| Dependency | Used for | Required behavior / assumption | +| --- | --- | --- | +| Feedback Ingestion Pipeline 원본 로그 | 학습 데이터셋 생성 입력 | 원본 이벤트는 append-only이고 검색 시점의 모델/인덱스 문맥을 보존해야 한다 | +| Object Storage | 학습 데이터셋/평가 상세 산출물 저장 | 버전형 산출물을 overwrite 없이 보존할 수 있어야 한다 | +| Model Artifact Files | 후보 모델 저장, 최초 기동 부트스트랩 | 모델 파일은 버전별 artifact로 저장되어야 하며, artifact 경로에서 모델 버전을 일관되게 식별할 수 있어야 한다. 최초 기동 시 서비스는 환경변수로 지정한 경로에서 모델을 로드하고 버전을 파싱한다고 가정한다. | +| Metadata DB `ModelRelease` | 실행 중 활성/후보 모델 및 인덱스 조회 | 서비스 실행 중 모델 선택의 SOT는 `ModelRelease`여야 하며, 각 컴포넌트는 여기서 활성/후보 모델 버전과 대응 인덱스를 동일하게 읽는다고 가정한다. 레코드가 없을 때만 환경변수 기반 기본값으로 초기화한다. | +| Evaluation Dataset Artifact | 후보/기준 모델 비교 평가 | 학습셋과 분리된 immutable artifact여야 한다 | + +### 2.2 데이터 계약 + +#### 소유 데이터 (이 컴포넌트가 SOT인 경우) +| Entity / table | Purpose | Key fields / invariants | Notes | +| --- | --- | --- | --- | +| TrainingDataset Artifact | 원본 피드백 로그를 학습 입력으로 고정한 버전형 산출물 | `dataset_version`, `storage_path`, `created_at`; 변경 불가; 학습셋과 평가셋은 분리 | Object Storage 저장 | +| `MLPipelineRun` | 실행 제어와 추적의 SOT | `status`, `failed_stage`, `failure_type`, `failure_reason`, `candidate_model_version`, `dataset_version`, `evaluation_id`, `superseded_by_run_id`, `created_at`, `updated_at` | `candidate_index_name`, `cutover_time`는 후속 release/reindex 단계가 채운다 | +| `ModelEvaluation` | 후보 vs 기준 모델의 집계 평가 결과 | `candidate_model_version`, `baseline_model_version`, `evaluation_dataset_ref`, `quality_metrics`, `pass_criteria`, `overall_decision`, `fail_reason` | `overall_decision`은 `PASS | FAIL` | +| ModelEvaluationDetail Artifact | 질의별 상세 비교 결과 | `evaluation_id`, `storage_path`, `format=jsonl`, `created_at`; immutable | Object Storage 저장 | + +학습 데이터셋 산출물은 저장 포맷과 무관하게 아래 의미를 보존해야 한다: +- `event_id` +- `user_id` +- `query_text` +- `rating` +- `topk_ids` +- `used_ids` +- `active_model_version` +- `active_index_name` +- `response_snapshot_ref` +- `created_at` + +평가 데이터셋은 저장 포맷과 무관하게 아래 의미를 제공해야 한다: +- `query_text` +- `expected_results` +- `evaluation_dataset_ref` + +`ModelEvaluation.quality_metrics`는 최소 아래 집계 지표를 포함해야 한다: +- `recall_at_5` +- `mrr_at_5` +- `ndcg_at_5` + +`ModelEvaluation.pass_criteria`는 최소 아래 판정 규칙을 기록해야 한다: +- 후보 모델의 `recall_at_5`, `mrr_at_5`, `ndcg_at_5`가 모두 기준 모델 이상일 때만 `PASS` + +#### 참조 데이터 (다른 SOT를 읽는 경우) +| Source owner | Entity / table | Fields relied on | Read-only assumptions | +| --- | --- | --- | --- | +| Feedback Ingestion Pipeline | 원본 피드백 이벤트 로그 | `event_id`, `user_id`, `query_text`, `rating`, `topk_ids`, `used_ids`, `active_model_version`, `active_index_name`, `response_snapshot_ref`, `created_at` | 이벤트는 append-only이며 후행 수정되지 않는다 | +| Model Release / Metadata DB | `ModelRelease` | `active_model_version` | 실행 시작 후에는 이번 run의 baseline으로 고정한다 | +| Admin-managed evaluation artifact | 평가 데이터셋 | `evaluation_dataset_ref`, `query_text`, `expected_results` | 변경 불가이며 학습셋과 분리되어 있다 | + +### 2.3 상태 및 비즈니스 규칙 +- 항상 유지되어야 하는 불변조건: + - 동시에 활성 실행인 `MLPipelineRun`은 하나만 존재한다. + - 동시에 다음 실행 대기 레코드도 하나만 존재한다. + - 다음 실행 대기 레코드는 항상 최신 `dataset_version`을 가리켜야 하며, 이전 대기 실행은 `SUPERSEDED`가 된다. + - 한 번 시작한 run의 `dataset_version`과 `baseline_model_version`은 중간에 바뀌지 않는다. + - 평가 데이터셋은 학습 데이터셋과 분리된 변경 불가 산출물이어야 한다. + +#### Run state ownership +`MLPipelineRun` 생성과 상태 전이는 실행 상태 관리 경계에서만 수행한다. Scheduler, Driver, Consumer, Reconciler는 `MLPipelineRun` 레코드를 직접 생성하거나 상태를 직접 갱신하지 않고, 이 경계가 제공하는 원자적 전이 작업을 호출한다. + +이 경계는 각 상태 전이를 DB 트랜잭션 안에서 처리하며, 2.3의 불변조건과 invalid condition을 같은 쓰기 경계 안에서 검증한다. + +- `MLPipelineRun.status`는 SOT의 다섯 상태(`PENDING`, `RUNNING`, `READY_FOR_RELEASE`, `FAILED`, `SUPERSEDED`)만 사용한다. +- `failed_stage`는 최소한 `데이터셋 생성`, `학습`, `평가`를 구분할 수 있어야 한다. +- `failure_type`은 `FAIL | ERROR`를 사용한다. +- 평가 `PASS` hand off는 평가 결과와 상세 아티팩트가 영속 저장된 뒤에만 수행한다. +- 내부 직접 호출에는 최소 식별자만 포함한다. 릴리스·재색인 단계의 실행 문맥은 수신 책임이 공유 SOT에서 다시 읽는다. +- Scheduler는 실행 필요성을 만들지만, 이미 시스템이 최신 데이터셋 기준으로 수렴 중이면 같은 목표를 중복으로 확장하지 않는다. +- Driver는 `PENDING` 실행을 `RUNNING`으로 전진시키고, 평가 `PASS`가 확정되면 `READY_FOR_RELEASE`를 기록한 뒤 hand off를 시작한다. +- Reconciler는 장시간 진전이 없는 `RUNNING` 실행을 방치하지 않는다. +- 복구 가능한 실행은 다시 이어받을 수 있어야 하며, 복구가 불가능한 실행은 `FAILED`로 남아야 한다. +- 거부되어야 하는 전이 / invalid condition: + - 활성 실행이 있는데 두 번째 활성 실행을 시작하는 동작 + - 이미 대기 중인 실행이 있을 때, 더 최신 데이터셋 기준 실행이 생기었을 때 기존 대기 실행이 남아 있는 경우 + - 평가 결과와 상세 아티팩트가 모두 기록되기 전에 재색인 단계로 넘길 준비 완료로 표기하는 동작 + +- Idempotency rule: + - 동일한 `TRAINING_REQUEST`가 중복 전달되어도 병렬 실행을 추가로 만들지 않는다. + - 이미 같은 최신 `dataset_version`으로 대기 중인 실행이 있으면 새 대기 실행을 더 만들지 않는다. + - 같은 원본 피드백 이벤트는 데이터셋 생성 단계에서 `event_id` 기준으로 중복 제거할 수 있어야 한다. +- Multi-tenant / authorization rule: + - 이 컴포넌트는 사용자별 테넌시를 직접 다루지 않는다. + - 정기 배치와 수동 재트리거의 권한 검증은 upstream 운영 경로 책임이다. + +| From | To | Trigger | Guard / rule | Required side effects | +| --- | --- | --- | --- | --- | +| 없음 | `RUNNING` | 새 데이터셋 생성 또는 `TRAINING_REQUEST` 수신 | 활성 실행 없음, 시작 가능한 최신 데이터셋 존재 | `dataset_version`, `baseline_model_version`, `candidate_model_version` 고정 | +| 없음 또는 기존 대기 | `PENDING` | 활성 실행 중 새 데이터셋 준비 | 최신 데이터셋 기준 다음 실행만 유지 | 기존 대기 실행은 `SUPERSEDED` 처리 | +| `PENDING` | `RUNNING` | 활성 슬롯 확보 | 가장 최신 대기 실행만 시작 | 실행 기준 버전 고정 | +| `RUNNING` | `READY_FOR_RELEASE` | 학습 완료 + 평가 `PASS` | 후보 모델 저장, 평가 요약/상세 저장 완료 | hand off 준비 상태를 기록하고 `run_id`, `trace_id`로 내부 직접 호출 | +| `RUNNING` | `FAILED` | 평가 `FAIL` 또는 시스템 오류 | `failed_stage`, `failure_type` 기록 | 기존 서빙 유지 | +| `PENDING` | `SUPERSEDED` | 더 최신 데이터셋 준비 | 자신보다 최신 `dataset_version`이 대기 슬롯을 차지 | `superseded_by_run_id` 기록 | + +### 2.4 한계와 운영 제약 +- Performance / latency target: + - 사용자 동기 요청 경로가 아니므로 저지연보다 최신 데이터셋 수렴과 실행 직렬화가 우선이다. +- Throughput / rate / concurrency limits: + - 실행 단위 동시성은 활성 실행 1개, 다음 실행 대기 1개로 고정한다. + - 학습 내부 병렬도와 배치 크기는 PLAN에서 확정한다. +- Payload / file size / pagination limits: + - `TRAINING_REQUEST` 메시지에는 데이터셋 본문이나 평가 본문을 넣지 않는다. + +- Timeout / TTL / retry constraints: + - 메시지 재전달은 at-least-once를 전제로 한다. + - 구체 timeout, retry 횟수, backoff 수치는 PLAN에서 확정한다. + - `FAIL`은 품질 미달, `ERROR`는 시스템 오류로 운영 화면에서 구분 가능해야 한다. +- Security / privacy constraints: + - 학습 데이터셋과 원본 피드백 로그는 `user_id`, `query_text`, 모델/인덱스 문맥을 포함할 수 있으므로 내부 운영 경로만 접근 가능해야 한다. + - 평가 상세 아티팩트는 사용자 외부 노출 경로를 갖지 않는다. + +### 2.5 에러 계약 +| Surface | Condition | Code / status | Retryable | Notes | +| --- | --- | --- | --- | --- | +| 정기 배치 / 데이터셋 생성 | 원본 피드백 로그 읽기 또는 산출물 저장 실패 | 실행 실패 / 실패 단계=`데이터셋 생성` / `failure_type=ERROR` | Y | 기존 서빙은 유지 | +| 학습 실행 consumer | 지원하지 않는 `payload_version` | invalid message | N | 정상 실행으로 처리하지 않는다 | +| 학습 단계 | 후보 모델 학습 또는 artifact 저장 실패 | 실행 실패 / 실패 단계=`학습` / `failure_type=ERROR` | Y | 기존 서빙은 유지 | +| 평가 단계 | 평가 계산/저장 오류 | 실행 실패 / 실패 단계=`평가` / `failure_type=ERROR` | Y | 기존 서빙은 유지 | +| 평가 단계 | 품질 지표가 `pass_criteria` 미달 | 실행 실패 / 실패 단계=`평가` / `failure_type=FAIL` | N | 후보 모델은 서빙 전환 대상이 아니다 | + + +--- + +## 3. 관측성과 운영 (Observability and Operations) + +- Required log fields: + - `trace_id` + - `ml_pipeline_run_id` + - `dataset_version` + - `baseline_model_version` + - `candidate_model_version` + - `evaluation_id` + - `status` + - `failed_stage` + - `failure_type` + - `trigger_source` (`schedule` or `manual`) +- Key metrics / alerts worth tracking: + - 신규 학습 데이터셋 생성 건수 + - 현재 실행 중인 MLPipelineRun 존재 여부 + - 현재 다음 순서로 대기 중인 MLPipelineRun 존재 여부 + - 장시간 진전이 없는 실행 존재 여부 + - `SUPERSEDED` 발생 건수 + - 학습 단계 소요 시간 + - 평가 단계 소요 시간 + - 평가 `PASS` / `FAIL` / `ERROR` 비율 + - `failed_stage`별 실패 건수 + - 최신 성공 run이 반영한 `dataset_version`과 최신 생성 `dataset_version`의 차이 +- Trace / correlation propagation rule: + - 자동 트리거와 수동 재트리거 모두 `trace_id`를 `MLPipelineRun`, 로그, 평가 아티팩트 메타데이터에 일관되게 남겨야 한다. +- Reconciliation / cleanup requirement: + - `SUPERSEDED`된 run은 삭제하지 않고 이력으로 남겨야 한다. + - 장시간 진전이 없는 실행은 운영적으로 식별 가능해야 한다. + - 재색인 단계로 넘긴 뒤의 후보 인덱스/릴리스 정리는 이 SPEC이 아니라 후속 release/reindex 단계 책임이다. + +--- + +## 4. 인수 기준 (Acceptance Criteria) + +### 4.1 반드시 통과해야 하는 시나리오 +- [ ] 신규 원본 피드백 로그가 있으면 학습 데이터셋 버전이 생성되고, 활성 실행이 없으면 즉시 실행을 시작하며, 있으면 최신 데이터셋 기준 다음 실행 하나만 유지된다. +- [ ] 더 새로운 데이터셋이 준비되면 이전 대기 실행은 `SUPERSEDED`로 남고, 오래된 대기 실행이 실제 시작되지 않는다. +- [ ] 실행이 시작되면 `dataset_version`, `baseline_model_version`, `candidate_model_version`이 이번 run 기준으로 고정되고 후보 모델 artifact가 저장된다. +- [ ] 평가가 끝나면 `ModelEvaluation` 요약과 질의별 상세 artifact가 모두 저장되며, `quality_metrics`와 `pass_criteria`만으로 `PASS` / `FAIL`을 재현할 수 있다. +- [ ] 평가 `PASS` 시 run은 `READY_FOR_RELEASE` 상태로 남고, 내부 직접 호출에는 최소 식별자만 전달되며, 이 단계에서는 아직 `ModelRelease`가 변경되지 않는다. +- [ ] 평가 `FAIL` 또는 시스템 오류 시 run은 실패로 종료되고, `failed_stage`와 `failure_type(FAIL|ERROR)`가 운영 화면에서 구분 가능하게 남는다. +- [ ] 중복 `TRAINING_REQUEST` 또는 중복 배치 트리거가 와도 활성 실행이 2개 이상 생기지 않는다. +- [ ] 장시간 진전이 없는 실행은 운영적으로 식별 가능하며, 복구 또는 실패 정리 대상으로 분류된다. + +### 4.2 비목표 / 보류 항목 +- 후보 인덱스 물리 생성 방식 +- 재색인 중 online ingest dual-write 규칙 +- 후보 모델 readiness 검증 절차 +- `ModelRelease` 갱신과 실제 서빙 전환 +- 롤백 실행과 rollback 대상 선택 + +--- diff --git a/docs/Tech_Spec/feedback_loop_&_admin_ops/Model_Release_and_Reindex_Spec.md b/docs/Tech_Spec/feedback_loop_&_admin_ops/Model_Release_and_Reindex_Spec.md new file mode 100644 index 0000000..03956cc --- /dev/null +++ b/docs/Tech_Spec/feedback_loop_&_admin_ops/Model_Release_and_Reindex_Spec.md @@ -0,0 +1,261 @@ +# [Model Release and Reindex] SPEC + +**메타 정보 (Meta)** +- Component ID: `model-release-and-reindex` +- SOT: `docs/system-design.md` (이 SPEC은 system design SOT와 일관되어야 한다) +- Related docs: + - `docs/PRD.md` + - `docs/Tech_Spec/feedback_loop_&_admin_ops/ML_Pipeline_Execution_Spec.md` + - `docs/Tech_Spec/upload_search_Service/Managed_Embedding_Endpoint_Spec.md` + - `docs/Tech_Spec/upload_search_Service/Search_Service_Spec.md` + - `docs/Tech_Spec/upload_search_Service/Core_Api_Server_Spec.md` +- Status: Draft + +--- + +## 1. 목적과 범위 (Purpose and Scope) + +### 1.1 한 줄 요약 +- Model Release and Reindex는 후보 임베딩 모델의 candidate 재색인, 서빙 전환, 마지막으로 정상 서빙되던 active 조합만 담는 rollback snapshot 복구, rollback 중 검색 범위 제외, 복구 후 재편입을 관리하는 릴리스 제어 컴포넌트다. + +### 1.2 책임 경계 +- In scope: + - 현재 서빙 조합과 전환 상태는 릴리스 레코드(`ModelRelease`)를 SOT로 관리한다. + - 평가 PASS 후보에 대해서는 candidate index를 만들고, 서빙 전환 전까지 새로 READY가 되는 데이터만 우선 반영한다. + - 서빙 전환 직전의 마지막 정상 active 조합만 rollback snapshot으로 저장하고, 전환 후에는 active/previous 2세대만 서빙 대상으로 유지한다. + - rollback 요청(`ROLLBACK_REQUEST`)을 받으면 rollback 준비 상태를 열고 snapshot 기반 복구를 수행한다. + - rollback 중 영향을 받는 영상은 검색에서 제외하고, 복구 완료분부터 다시 검색 범위에 편입한다. 이 상태 표시는 `Video.search_serving_state`로 관리한다. + - 배포용 재색인(`CANDIDATE_REINDEXING`) 상태에서는 새로 READY가 되는 데이터를 active index와 candidate index에 함께 기록한다. + - rollback restore의 복원 대상은 snapshot이 가리키는 active 조합으로 한정한다. +- Out of scope: + - 모델 학습, 평가, `MLPipelineRun` 활성 실행 제어 + - 검색 랭킹, RRF, LLM 응답 생성 + - 공개 Admin HTTP API + - full immediate reindex + - retired/problem index의 최종 물리 삭제 시점 + - Search Service의 사용자 고지 문구 포맷 +- Upstream dependencies: + - 같은 Worker 내부 ML Pipeline Execution이 남긴 `READY_FOR_RELEASE` run 상태 + - Admin control path가 발행한 `ROLLBACK_REQUEST` control message + - Managed Embedding Endpoint readiness + - Metadata DB의 `ModelRelease`, `Video`, `Chunk` +- Downstream consumers: + - Search Service + - Media & AI Pipeline Worker online ingest 경로 + - Admin Dashboard의 상태 조회 경로 + +### 간단한 흐름 (Simple Flow) +1. 평가 `PASS` 후보가 전달되면 `ModelRelease`를 `CANDIDATE_REINDEXING`으로 전환하고 candidate index를 생성한다. +2. CANDIDATE_REINDEXING 상태에 들어간 뒤 처리 완료(READY)된 새 데이터는 현재 서빙용 active index와 후보용 candidate index에 함께 기록한다. +3. 새 모델이 실제 서빙 가능한 상태이고, 전환 기준 시점까지의 데이터가 새 인덱스에 모두 반영된 것이 확인되면, 현재 서빙 상태를 snapshot으로 저장한 뒤 candidate를 active로 전환한다. +4. 서빙 전환 이후 원래 active 였던 모델은 previous로 남기고, previous보다 오래된 모델로 색인한 데이터는 최신 active 기준으로 점진 재임베딩한다. +5. 롤백 요청이 들어오면, 영향받는 영상은 우선 검색 대상에서 제외한다. 그 후 미리 저장해 둔 정상 서빙 상태가 다시 준비되면, 시스템을 그 상태로 복구한다. +6. 복구 중 제외된 영상은 Search Service의 검색 범위에서 빠지며, 복구 완료 후 다시 검색 범위에 합류한다. + + +--- + +## 2. 계약 (Contracts) + +### 2.1 외부 인터페이스 + +#### 외부 진입 인터페이스 +| Interface | Method / Trigger | Input summary | Output summary | Auth / tenancy | Notes | +| --- | --- | --- | --- | --- | --- | +| 평가 `PASS` 수신 | 같은 Worker 내부 직접 호출 | `run_id`, `trace_id` | 후보 재색인 시작 또는 run 실패 기록 | internal only | 이 컴포넌트는 `READY_FOR_RELEASE` 상태의 run을 다음 단계 시작 조건으로 사용한다. 실행 문맥은 `MLPipelineRun`, `ModelEvaluation`, `ModelRelease`를 다시 읽어 복원한다 | +| 롤백 요청 수신 | `ROLLBACK_REQUEST` control message 수신 | `message_type`, `payload_version`, `trace_id`, `attempt`, `issued_at` | rollback 준비 시작 또는 invalid state 기록 | admin 권한 검증은 upstream 책임 | rollback 대상 선택은 `ModelRelease` snapshot 기준이다 | + +#### 메시지 / 이벤트 계약 (해당 시) +- Queue / topic: rollback control channel (`ROLLBACK_REQUEST`) +- 평가 `PASS` hand off는 broker나 polling을 쓰지 않고 같은 Worker 내부 직접 호출로 처리한다. +- Producer / consumer responsibility: + - Producer: admin control path가 rollback control message를 발행한다. + - Consumer: Consumer는 되돌릴 정상 모델과 인덱스가 실제로 다시 준비됐는지 확인한 뒤, 롤백 상태 전환을 수행한다. +- Delivery semantics: at-least-once +- Payload versioning rules: + - ROLLBACK_REQUEST는 video-processing shared envelope가 아니라 별도의 control-message schema를 사용한다. + - control-message schema에는 `message_type`, `payload_version`, `trace_id`, `attempt`, `issued_at`만 포함한다. + - 지원하지 않는 `payload_version`은 정상 rollback 실행으로 처리하지 않는다. +- Required control-message fields: + - `message_type` + - `payload_version` + - `trace_id` + - `attempt` + - `issued_at` + +```json +{ + "message_type": "ROLLBACK_REQUEST", + "payload_version": "v1", + "trace_id": "UUID4", + "attempt": 1, + "issued_at": "ISO8601_TIMESTAMP" +} +``` + +#### 외부 연동 컴포넌트 계약 (해당 시) +| Dependency | Used for | Required behavior / assumption | Failure impact | +| --- | --- | --- | --- | +| Metadata DB `ModelRelease` | current serving SOT | active/previous/candidate 및 rollback snapshot active field를 원자적으로 갱신할 수 있어야 한다 | 서빙 전환 또는 rollback restore가 불가능하다 | +| Metadata DB `Video` | search exclusion gate | `search_serving_state`는 `SERVABLE | ROLLBACK_EXCLUDED`만 허용한다 | rollback 중 검색 제외/복귀 계약이 깨진다 | +| Metadata DB `Chunk` | rollback 영향 범위 식별 | `embedding_model_version`으로 문제 모델 데이터를 식별할 수 있어야 한다 | affected video 선별이 불가능하다 | +| Managed Embedding Endpoint | candidate / rollback target readiness | target model이 실제로 로드되고 readiness를 통과해야만 release record를 전환할 수 있다 | 상태는 유지되고 전환은 차단된다 | +| Vector Store | candidate / active / previous index 유지 | candidate index는 staging 전용이며 end-user search 대상이 아니다 | 검색 또는 reindex 정합성이 깨진다 | +| Index Snapshot Files | rollback snapshot restore | rollback snapshot active index 식별자에 대응하는 인덱스 스냅샷을 복원할 수 있어야 한다 | rollback restore가 불가능하다 | +| Media & AI Pipeline Worker | online ingest 반영 | `CANDIDATE_REINDEXING`이면 active/candidate dual-write를 따라야 하며, rollback 후 영향 영상 재임베딩을 수행할 수 있어야 한다 | latest-only 반영과 rollback 복구 계약이 깨진다 | + +### 2.2 데이터 계약 + +#### 소유 데이터 (이 컴포넌트가 SOT인 경우) +| Entity / table | Purpose | Key fields / invariants | Notes | +| --- | --- | --- | --- | +| `ModelRelease` | 현재 서빙 조합과 전환 상태의 SOT | `release_status`, `active_model_version`, `active_index_name`, `previous_model_version`, `previous_index_name`, `candidate_model_version`, `candidate_index_name`, `rollback_snapshot_active_model_version`, `rollback_snapshot_active_index_name`, `rollback_snapshot_captured_at`, `candidate_ready_at`, `switched_at` | `release_status` 허용값은 정확히 `STABLE`, `CANDIDATE_REINDEXING`, `ROLLBACK_PREPARING`이다 | +| `Video.search_serving_state` (shared field) | rollback 중 일시 검색 제외 | 허용값은 `SERVABLE`, `ROLLBACK_EXCLUDED`다 | Search Service는 `READY + SERVABLE`인 영상만 검색에 포함한다 | +| `MLPipelineRun.candidate_index_name`, `MLPipelineRun.cutover_time` (shared fields) | 평가 PASS hand off 추적 | `candidate_index_name`은 candidate index 식별자다. `cutover_time`은 서빙 전환 전에 candidate 반영이 끝나 있어야 하는 데이터 범위를 가르는 기준 시각이다. 이 값은 release/reindex 단계가 서빙 전환 직전에 고정한다. | 나머지 run 제어는 `ML_Pipeline_Execution_Spec.md`가 소유한다 | + +#### 참조 데이터 (다른 SOT를 읽는 경우) +| Source owner | Entity / table | Fields relied on | Read-only assumptions | +| --- | --- | --- | --- | +| ML Pipeline Execution | `MLPipelineRun` | `id`, `status`, `candidate_model_version`, `dataset_version`, `evaluation_id`, `cutover_time` | `READY_FOR_RELEASE` 상태의 run만 이 컴포넌트로 들어오며, 수신 시 공유 SOT를 다시 읽어 기준 상태를 복원한다 | +| Search Service | serving path | active/previous index 조합, `READY + SERVABLE` gate | candidate index는 검색 대상이 아니며, `ROLLBACK_EXCLUDED` 영상이 있어도 다른 검색 가능 영상으로 검색을 계속 제공할 수 있다 | +| Managed Embedding Endpoint | runtime state | active/previous/candidate readiness | readiness가 통과한 모델만 release record에 반영한다 | + +### 2.3 상태 및 비즈니스 규칙 +- 항상 유지되어야 하는 불변조건: + - `release_status`는 `STABLE | CANDIDATE_REINDEXING | ROLLBACK_PREPARING`만 허용한다. + - end-user search는 active/previous 2세대까지만 사용한다. + - candidate index는 검증/재색인 전용이며 end-user search 표면에 노출되지 않는다. + - 서빙 전환 전 반영 대상은 `CANDIDATE_REINDEXING`이 열린 뒤부터 `cutover_time`까지 dual-write gate에 진입한 online ingest 작업 집합이다. + - previous보다 더 오래된 데이터는 서빙 전환 이후 최신 active 모델로 점진 재임베딩한다. + - rollback 중 영향 영상은 현재 문제 active 모델 버전과 같은 `Chunk.embedding_model_version`을 가진 청크가 1개 이상 있는 `Video`다. + - `ROLLBACK_EXCLUDED` 영상은 restored active 모델 버전 기준으로 재임베딩과 vector 반영이 끝난 뒤에만 다시 `SERVABLE`이 된다. + - rollback 중 신규 업로드 요청과 일반 ingest는 계속 처리될 수 있다. rollback은 영향 영상 검색 제외와 snapshot restore를 우선 수행한다. + - rollback snapshot은 cutover 직전의 last known-good active model/index만 저장한다. `previous_model_version` / `previous_index_name`은 snapshot에 포함하지 않으며 restore에도 사용하지 않는다. + - rollback restore는 서빙 상태를 마지막 정상 상태로 되돌리는 책임이다. 영향 영상의 재임베딩과 검색 재편입은 그 뒤에 이어지는 후속 복구 책임이다. +- 서빙 전환 전 반영 기준: + - `CANDIDATE_REINDEXING`이 시작되면 online ingest는 active index와 candidate index에 함께 기록된다. + - candidate readiness가 확인되면, 서빙 전환 직전에 이 컴포넌트가 기준 시각 `MLPipelineRun.cutover_time`을 고정한다. + - `cutover_time` 이전에 들어온 데이터가 candidate index에 모두 반영된 것이 확인된 뒤에만 서빙을 전환한다. +- 릴리스·재색인 판단은 호출자가 들고 온 메모리 문맥이 아니라 공유 SOT에 기록된 상태를 기준으로 수행한다. +- snapshot capture / restore 규칙: + - snapshot capture는 서빙 전환 직전에 수행한다. + - capture 시 `active_model_version`, `active_index_name`만 각각 `rollback_snapshot_active_model_version`, `rollback_snapshot_active_index_name`으로 복사하고 `rollback_snapshot_captured_at`를 기록한다. + - rollback snapshot 필드는 실제 모델/인덱스 본체가 아니라 복원 대상 active model/index를 가리키는 포인터 메타데이터다. + - 서빙 전환 성공 시 `candidate_model_version`, `candidate_index_name`, `candidate_ready_at`는 null로 초기화한다. + - rollback restore 시 rollback snapshot이 가리키는 active model/index가 준비된 뒤 현재 serving 필드의 active model/index만 복원하고, `previous_model_version` / `previous_index_name`은 snapshot에서 복원하지 않는다. `candidate_model_version`, `candidate_index_name`, `candidate_ready_at`는 null로 초기화한다. +- dual-write / visibility 규칙: + - dual-write는 `release_status=CANDIDATE_REINDEXING`이고 candidate fields가 non-null일 때만 시작된다. + - dual-write는 `release_status`가 `CANDIDATE_REINDEXING`을 벗어나거나 candidate fields가 cleared 되면 즉시 중단된다. + - candidate index는 서빙 전환 전까지 staging 전용이며 Search Service의 user-facing query surface에 포함되면 안 된다. +- 거부되어야 하는 전이 / invalid condition: + - candidate readiness 또는 서빙 전환 전 반영 기준 충족 전에 서빙 전환을 완료하는 동작 + - rollback snapshot 없이 rollback restore를 시도하는 동작 + - rollback snapshot active model/index 없이 restore를 완료하는 동작 + - rollback restore에서 `previous_model_version` / `previous_index_name`를 snapshot 값으로 복원하는 동작 + - `release_status!=STABLE`인 상태에서 새 `ROLLBACK_REQUEST`를 정상 수락하는 동작 + - `ROLLBACK_EXCLUDED` 영상을 search-visible로 취급하는 동작 + - `CANDIDATE_REINDEXING`이 아닌데 candidate index로 dual-write 하는 동작 +- Idempotency rule: + - 동일한 평가 `PASS` hand off가 재전달되어도 같은 `MLPipelineRun.id` 기준으로 candidate 상태를 중복으로 열지 않는다. + - 동일한 `ROLLBACK_REQUEST`가 재전달되어도 이미 `ROLLBACK_PREPARING`이면 추가 side effect 없이 현재 상태를 유지한다. + - rollback restore가 완료되어 현재 active 조합이 rollback snapshot과 이미 같으면, 이후 동일 rollback 요청 재전달은 no-op로 처리한다. + - 이 작업들은 재시도될 수 있지만, 현재 상태와 필드 값을 보고 아직 필요한 경우에만 실행한다. +- Multi-tenant / authorization rule: + - 이 컴포넌트는 사용자별 권한 판정을 직접 하지 않는다. + - rollback 요청 권한 검증은 upstream admin control path 책임이다. + +| From | To | Trigger | Guard / rule | Required side effects | +| --- | --- | --- | --- | --- | +| `STABLE` | `CANDIDATE_REINDEXING` | `READY_FOR_RELEASE` run 수신 | candidate 전환 중인 다른 release가 없어야 함 | `candidate_model_version/candidate_index_name` 설정, candidate index 생성 시작, online ingest dual-write 개시 | +| `CANDIDATE_REINDEXING` | `STABLE` | 서빙 전환 실행 | candidate readiness 통과 + `cutover_time`까지의 서빙 전환 전 반영 대상 데이터가 candidate에 반영 완료 | rollback snapshot 저장, `rollback_snapshot_active_model_version/rollback_snapshot_active_index_name/rollback_snapshot_captured_at` 갱신, `active=candidate`, `previous=직전 active`, `candidate_model_version/candidate_index_name/candidate_ready_at=null`, `switched_at` 갱신 | +| `STABLE` | `ROLLBACK_PREPARING` | `ROLLBACK_REQUEST` control message 수신 | rollback snapshot이 존재해야 함 | 현재 active 모델 버전과 같은 `Chunk.embedding_model_version`을 가진 영상 전체를 `ROLLBACK_EXCLUDED`, rollback target model load 시작, snapshot active index restore 시작 | +| `ROLLBACK_PREPARING` | `STABLE` | rollback restore 실행 | rollback target readiness 통과 + snapshot active index restore 완료 | snapshot active model/index 복원, `previous_model_version` / `previous_index_name`은 snapshot에서 복원하지 않음, `candidate_model_version/candidate_index_name/candidate_ready_at=null`, `switched_at` 갱신, 복구 완료 영상부터 `SERVABLE` 재편입 | +| `CANDIDATE_REINDEXING` | `STABLE` | candidate reindex 실패 | current serving은 유지 | candidate fields clear, 관련 run 실패 기록, dual-write 중단 | + +### 2.4 한계와 운영 제약 +- Performance / latency target: + - 사용자 동기 요청 경로가 아니므로 즉시성보다 serving 정합성과 복구 가능성을 우선한다. +- Throughput / rate / concurrency limits: + - 동시에 열린 release state는 한 serving 조합당 하나만 허용한다. + - exact batch size, worker parallelism, retry 횟수는 PLAN에서 확정한다. +- Payload / file size / pagination limits: + - `ROLLBACK_REQUEST`는 video_id나 추가 payload 없이 control-message schema만 사용한다. +- Timeout / TTL / retry constraints: + - 서빙 전환과 rollback restore는 readiness gate가 만족될 때까지 전이를 완료하지 않는다. + - readiness load 실패와 rollback restore 실패는 retryable로 취급하되, 구체 backoff 수치는 여기서 고정하지 않는다. +- Security / privacy constraints: + - `ROLLBACK_EXCLUDED`는 hard delete가 아니라 일시적 서빙 제외다. + - release record와 rollback snapshot은 내부 운영 경로만 접근 가능해야 한다. + +### 2.5 에러 계약 +| Surface | Condition | Code / status | Retryable | Notes | +| --- | --- | --- | --- | --- | +| 평가 `PASS` hand off | `MLPipelineRun`이 릴리스·재색인 진입 가능 상태가 아님 | invalid handoff | N | current serving과 `ModelRelease`는 유지한다 | +| Cutover | candidate readiness 미충족 또는 `cutover_time`까지 서빙 전환 전 반영 대상 데이터의 candidate 반영 미완료 | transition blocked | Y | `CANDIDATE_REINDEXING` 유지 | +| Rollback consumer | `release_status!=STABLE`에서 `ROLLBACK_REQUEST` 수신 | invalid rollback request | N | 현재 전환 상태를 유지하고 새 rollback은 시작하지 않는다 | +| Rollback consumer | rollback snapshot active model/index 부재 | invalid rollback request | N | `release_status`는 유지한다 | +| Rollback restore | rollback target readiness 미충족 또는 snapshot active index restore 미완료 | transition blocked | Y | `ROLLBACK_PREPARING`과 `ROLLBACK_EXCLUDED`를 유지한다 | +| Search serving gate | `READY`지만 `search_serving_state!=SERVABLE` | excluded from search | Y | restored-model 재임베딩 완료 후 재편입 대상이다 | + +- 이 컴포넌트는 public HTTP API를 소유하지 않는다. operator-visible failure는 `MLPipelineRun`, `ModelRelease`, 구조화 로그에 기록한다. + +--- + +## 3. 관측성과 운영 (Observability and Operations) + +- Required log fields: + - `trace_id` + - `ml_pipeline_run_id` + - `release_status` + - `active_model_version` + - `active_index_name` + - `previous_model_version` + - `previous_index_name` + - `candidate_model_version` + - `candidate_index_name` + - `cutover_time` + - `rollback_snapshot_active_model_version` + - `rollback_snapshot_active_index_name` + - `rollback_snapshot_captured_at` + - `candidate_ready_at` + - `switched_at` + - `affected_video_count` + - `excluded_video_count` +- Key metrics / alerts worth tracking: + - current `release_status` + - candidate cutover backlog + - candidate dual-write lag + - `ROLLBACK_EXCLUDED` video count + - rollback snapshot index restore status + - cutover success/failure count + - rollback request / restore success/failure count +- Trace / correlation propagation rule: + - 평가 `PASS` hand off, `ROLLBACK_REQUEST` control message, `ModelRelease` update, candidate/rollback model load, 관련 reindex 작업은 동일 `trace_id`로 연결해야 한다. +- Reconciliation / cleanup requirement (if any): + - rollback restore 후 `candidate_model_version`, `candidate_index_name`, `candidate_ready_at`는 즉시 null이어야 한다. + - problem-model index의 최종 물리 삭제는 current active/previous가 아닌 것이 확인된 뒤 비동기로 수행할 수 있다. + - `previous_model_version` / `previous_index_name` 처리와 `ROLLBACK_EXCLUDED` 해제 조건은 2.3의 snapshot capture / restore 규칙을 따른다. + +--- + +## 4. 인수 기준 (Acceptance Criteria) + +### 4.1 반드시 통과해야 하는 시나리오 +- [ ] 평가 `PASS` hand off가 들어오면 `ModelRelease`가 `CANDIDATE_REINDEXING`으로 전환되고 `candidate_model_version/candidate_index_name`이 채워지며, end-user serving 조합은 아직 바뀌지 않는다. +- [ ] `CANDIDATE_REINDEXING` 동안 `cutover_time`까지 새로 `READY`가 된 데이터는 active/candidate에 모두 반영되고, Search Service는 여전히 active/previous만 사용한다. +- [ ] 서빙 전환은 candidate readiness와 서빙 전환 전 반영 기준 충족이 모두 확인될 때만 실행되고, 실행 직전 rollback snapshot이 저장된다. +- [ ] 서빙 전환 후 `active=candidate`, `previous=직전 active`, `candidate_model_version/candidate_index_name/candidate_ready_at`는 null로 정리되고 `release_status=STABLE`이 된다. +- [ ] rollback control message가 수신되면 problem-model 데이터가 포함된 영상은 `ROLLBACK_EXCLUDED`가 되고, snapshot active index restore가 시작된다. +- [ ] rollback restore는 snapshot active model/index 존재, rollback target readiness, snapshot active index restore 완료가 모두 확인될 때만 실행되며, 복원 결과는 2.3의 snapshot capture / restore 규칙과 일치한다. +- [ ] restored-model 기준 재임베딩이 완료되기 전까지 `ROLLBACK_EXCLUDED` 영상은 검색 결과에 포함되지 않는다. +- [ ] `ROLLBACK_EXCLUDED` 영상이 있어도 Search Service는 남아 있는 `READY + SERVABLE` 영상으로 검색을 계속 제공할 수 있다. +- [ ] 동일한 평가 `PASS` hand off 또는 rollback 요청이 중복 수신되어도 duplicate cutover 또는 duplicate restore가 발생하지 않는다. + +### 4.2 비목표 / 보류 항목 +- activity-based priority reindexing +- full immediate reindex +- 모델 학습/평가와 pass/fail 판정 +- 공개 Admin API와 UI 설계 +- retired/problem index의 최종 cleanup 스케줄 + +--- diff --git a/docs/Tech_Spec/template/plan_template.md b/docs/Tech_Spec/template/plan_template.md index f2bf10a..eda4ec7 100644 --- a/docs/Tech_Spec/template/plan_template.md +++ b/docs/Tech_Spec/template/plan_template.md @@ -1,205 +1,186 @@ # [ComponentName] PLAN -**Meta** -- **Component ID:** (예: core-api-server / pipeline-worker / search-service) -- **Target SPEC:** `./spec.md` (이 PLAN이 구현할 스펙 문서의 경로) -- **SOT:** `docs/system-design.md`, `(Target SPEC 경로)`, `(필요 시 관련 ADR / 관련 Tech Spec)` +> 대상 SPEC을 구현하기 위한 실행 문서. +> 이 문서는 전략, 순서, workstream, 의존성, 검증, rollout을 통해 작업을 어떻게 전달할지 설명한다. +> +> 이 문서에 포함할 것 (Belongs here): +> - 구현 접근 방식과 순서 +> - 작업 분해와 책임 경계 +> - 의존성, blocker, migration path, rollout / rollback +> - validation strategy와 test focus +> +> 이 문서에 포함하지 말 것 (Does NOT belong here): +> - SPEC의 계약을 그대로 다시 길게 쓰는 것 +> - 긴 설계 근거 또는 옵션 비교 -> ADR +> - 실행 가치가 거의 없는 과도한 task 나열 +> +> 작성 규칙 (Authoring rules): +> - narrative completeness보다 execution을 우선한다. +> - 특별한 이유가 없다면 기본적으로 2~5개의 workstream으로 구성한다. +> - 잘게 쪼갠 task 다수보다 high-signal workstream 몇 개를 선호한다. +> - delivery risk 때문에 재서술이 꼭 필요한 경우가 아니라면 계약은 다시 쓰지 말고 SPEC section / acceptance criteria를 참조한다. +> - 모든 workstream은 user-visible, operator-visible, 또는 contract-relevant outcome을 만들어야 한다. +> - 모든 work item은 concrete outcome과 meaningful verification method를 가져야 한다. +> - SPEC이 아직 불안정하면 세부사항을 상상해서 채우지 말고 blocker로 적는다. + +**메타 정보 (Meta)** +- Component ID: +- SOT: `docs/system-design.md` (이 PLAN은 system design SOT를 구현하며 그 내용과 일관되어야 한다) +- Target SPEC: +- Related docs: [ADR if any], [related specs] +- Plan status: Draft / Ready / In Progress / Done --- -> 이 PLAN은 특정 구현자나 특정 에이전트 도구에 종속되지 않는 실행 계획 문서다. -> 목표는 구현자가 `SPEC + PLAN`만 읽고도 작업 순서, 검증 기준, 통합 경계를 오해 없이 이해할 수 있게 만드는 것이다. +## 1. 구현 의도 (Implementation Intent) -## 1. Goals & Strategy +### 1.1 전달 목표 +- 이 plan이 끝났을 때 실제로 동작해야 하는 것: +- 검증 가능한 형태로 입증되어야 하는 것: -### 1.1 달성 목표 (Goals) +### 1.2 이번 구현의 범위 +- Included in this plan: +- Explicitly excluded / later phase: -- **(핵심 기능 A):** (E2E 흐름 완성 목표 — 정상/예외 경로 포함하여 명시) -- **(핵심 기능 B):** (추가 주요 흐름이 있으면 bullet 추가) -- **관측성:** 모든 요청·발행 로그에 `trace_id`를 포함하고, 핵심 메트릭을 노출한다. -- **테스트:** (통합 테스트 환경 — 예: Postgres/Testcontainers)을 기반으로 SPEC §5.1에 정의된 시나리오를 모두 충족하고, 단위·통합 테스트 합산 커버리지 (목표 %) 이상을 달성한다. +### 1.3 전제조건과 blocker +- 이미 고정된 spec contract: +- 필요한 upstream work / dependency: +- 구현을 막는 open question: -### 1.2 제외 대상 (Non-Goals) -- (이번 PLAN에서 의도적으로 배제하는 작업) - - *예: 실패 시 DLQ(Dead Letter Queue) 재처리 로직은 다음 페이즈에서 진행함.* - -### 1.3 리스크 및 대응 방안 (Risk & Mitigation) -- **위험 요소:** (예: 외부 STT API 응답 지연으로 인한 워커 스레드 고갈) -- **대응 방안:** (예: HTTP Timeout을 30초로 강제하고, 서킷 브레이커 패턴 적용) - -### 1.4 구현 전제 및 열려 있는 결정사항 (Preconditions & Open Decisions) - -- **구현 전제:** (예: Target SPEC의 상태 전이 / 메시지 계약 / DDL이 확정되어 있음) -- **선행 필요 사항:** (예: 공용 인증 모듈 선구현 필요, 공용 라이브러리 버전 확정 필요) -- **열려 있는 결정사항:** 구현을 막는 미확정 항목이 있다면 `Open Decision`, `Blocker`, `Pending Decision` 같은 일반적인 표기로 명시한다. - -### 1.5 핵심 의존성 패키지 - -| 패키지 | 용도 | 최소 버전 | -| --- | --- | --- | -| `(패키지명)` | (용도 설명) | (x.x+) | -| ... | ... | ... | +### 1.4 구현 전략 +- Overall approach: +- Key technical moves or slices: +- Risk reduction strategy: +- Merge strategy (single PR / phased PRs / parallel workstreams): +- Spec traceability anchor(s): [이 plan이 전달하는 SPEC section 및/또는 acceptance criteria] --- -## 2. Implementation Phasing Strategy - -- **Phase 1:** (스캐폴딩 및 인터페이스) 앱 팩토리/엔트리포인트, 설정 로딩, DTO/스키마, 라우터/컨슈머 스켈레톤, 공통 미들웨어. -- **Phase 2:** (영속성 및 핵심 유스케이스) Repository/트랜잭션 경계, 상태 전이, 외부 의존성 어댑터, 멱등성/복구 로직. -- **Phase 3:** (통합 및 운영) 관측성(로그·메트릭), 통합 테스트, 마이그레이션/배포 검증, 성능 스모크. -- **병합 게이트:** 각 단계 또는 자율적으로 나눈 PR 단위로 CI(단위·통합 테스트) 통과 + 1인 이상 리뷰. +## 2. Workstream과 순서 (Workstreams and Sequence) + +> 레이어 자체보다 전달 가능한 기능 슬라이스를 기준으로 구성한다. +> 각 workstream은 독립적으로 이해 가능하고 검증 가능해야 한다. +> 각 workstream은 반드시 SPEC section 또는 acceptance criterion에 연결되어야 한다. + +### 2.1 권장 순서 +| Order | Workstream | Maps to SPEC | Why this comes now | Depends on | +| --- | --- | --- | --- | --- | +| 1 | | | | | +| 2 | | | | | +| 3 | | | | | + +### 2.2 Workstream 상세 + +#### Workstream: [Name] +- Goal: +- Maps to SPEC: +- Main changes: +- Key files / areas likely affected: +- Dependency / integration points: +- Done when: +- Verification: + +#### Workstream: [Name] +- Goal: +- Maps to SPEC: +- Main changes: +- Key files / areas likely affected: +- Dependency / integration points: +- Done when: +- Verification: + +### 2.3 병렬화와 병합 지점 +- Safe parallel work: +- Shared integration points / likely conflict areas: +- Final integration checkpoint: -### 2.1 작업 분해 원칙 (Task Decomposition Rules) +--- -- 각 Task는 하나의 명확한 출력물과 하나의 검증 가능한 완료 조건을 가져야 한다. -- 병렬 작업이 가능하도록 Task 간 파일 수정 범위와 계약 경계를 최대한 분리한다. -- 선행 작업이 있는 경우 `Depends On`으로 명시하고, 독립 수행 가능하면 `병렬 가능`으로 표시한다. -- 각 Phase는 중간 병합 가능한 상태여야 하며, 부분 구현만 남은 채 다음 Phase로 넘어가지 않는다. -- 구현 순서는 레이어 나열보다 “기능 슬라이스가 검증 가능한 순서”를 우선한다. +## 3. 검증 및 테스트 전략 (Validation and Testing Strategy) + +> 테스트는 비즈니스 정합성을 증명하고 delivery risk를 줄여야 한다. +> 테스트 개수나 넓지만 얕은 coverage를 목표로 하지 않는다. +> 사소한 assertion 여러 개보다 가치 높은 테스트 몇 개를 우선한다. +> Meaningful verification = 약속한 동작, 계약, 또는 operator-visible outcome이 실제로 성립함을 보여주는 관찰 가능한 증거다. + +### 3.1 리스크 기반 테스트 초점 +가장 위험한 동작부터 적는다. + +| Spec ref | Risk / business rule | Why it matters | Best test level | Planned proof | +| --- | --- | --- | --- | --- | +| | | | | | +| | | | | | + +가이드: +- business rule, state transition, authorization boundary, data integrity, failure recovery를 우선 검증한다. +- SPEC의 각 critical business rule, invariant, auth rule, failure path마다 planned proof를 적거나, 왜 자동화를 추가하지 않는지 정당화한다. +- correctness가 persistence, transaction boundary, framework wiring, external-contract mapping에 의존하면 integration test를 추가한다. +- domain logic에 의미 있는 branching이나 invariant가 있다면 unit test를 추가한다. +- framework 기본 동작, getter/setter, trivial DTO shape 반복, 의미 없는 one-assertion wrapper를 검증하는 테스트는 넣지 않는다. +- 위험도가 진짜 높지 않다면 모든 invalid permutation을 exhaustive하게 테스트하지 않는다. + +### 3.2 계획된 자동화 테스트 +| Spec ref / acceptance criterion | Scenario / rule | Test level | Why this level | Observable proof | +| --- | --- | --- | --- | --- | +| | | Unit / Integration / E2E-smoke | | | +| | | Unit / Integration / E2E-smoke | | | + +### 3.3 자동화 테스트로 다루지 않는 항목 +| Spec ref / rule | Why not automated | Manual / operational proof | +| --- | --- | --- | +| | | | -### 2.2 선행 경로 및 병렬 가능 범위 (Critical Path & Parallelism) +### 3.4 테스트 환경과 double +- DB / storage / broker setup: +- External dependency isolation approach: +- Time / async / retry control approach: +- Required fixtures or seed data: -- **Critical Path:** 반드시 먼저 완료되어야 하는 작업 흐름을 bullet로 적는다. -- **Parallelizable Workstreams:** 계약이 고정된 뒤 병렬로 진행 가능한 작업 묶음을 적는다. -- **Merge Owner / Integration Point:** 병렬 작업 결과를 어디서 통합하고 어떤 검증을 거칠지 적는다. +### 3.5 검증 명령과 quality gate +- Required commands: +- Minimum meaningful checks before merge: +- Evidence to attach (test output, screenshots, logs, migration proof, etc.): --- -## 3. Work Breakdown Structure (WBS) - -> 구현자가 그대로 실행할 수 있는 구체 작업 지시서. -> 모든 작업은 반드시 `Output / Files / Test Files / Commands / Verify / Linked AC / Depends On`를 포함해야 한다. -> 병렬화 가능한 작업은 `병렬 가능: Y` 또는 `병렬 가능: N`으로 표시한다. - -### Phase 1: Skeleton & Contracts - -- [ ] **Task 0: 프로젝트 엔트리포인트 및 설정 스캐폴딩** - - **Output:** 앱 엔트리포인트, 설정 로딩, 기본 디렉토리 구조, 공통 의존성 주입 뼈대 - - **Files:** `src/main.py`, `src/core/config.py`, `.env.example` - - **Test Files:** `(필요 시) tests/unit/test_config.py` - - **Commands:** `pytest tests/unit/test_config.py` / `(프로젝트 실행 확인 명령)` - - **Verify:** 필수 환경 변수 누락 시 실패하고, 정상 설정으로 앱이 기동한다. - - **Linked AC:** SPEC §(관련 계약 섹션), DoD §5.1의 공통 전제 - - **Depends On:** 없음 - - **병렬 가능:** N - -- [ ] **Task 1: DTO/Validation 및 인터페이스 스켈레톤** - - **Output:** 요청/응답 스키마, 기본 라우터/컨슈머 시그니처, 유효성 검증 규칙 - - **Files:** `src/schemas/...`, `src/api/...` 또는 `src/adapters/...` - - **Test Files:** `tests/unit/test_*.py` (유효성 검사 및 기본 예외 흐름) - - **Commands:** `pytest tests/unit/test_*.py` - - **Verify:** 잘못된 입력이 SPEC의 Error Contract에 맞는 에러로 매핑된다. - - **Linked AC:** SPEC §2.1, §2.4, DoD §5.1 해당 시나리오 - - **Depends On:** Task 0 - - **병렬 가능:** Y - -### Phase 2: Core Logic & Persistence - -- [ ] **Task 2: 저장소/트랜잭션 경계 구현** - - **Output:** SOT 저장소 접근 로직, 트랜잭션 경계, 조회/수정 필터, 필요한 인덱스 사용 경로 - - **Files:** `src/infra/db/...`, `src/models/...`, `alembic/...` - - **Test Files:** `tests/integration/test_*repository.py` - - **Commands:** `alembic upgrade head`, `pytest tests/integration/test_*repository.py` - - **Verify:** 정상 저장/조회/롤백 및 테넌시 필터가 통합 테스트로 재현된다. - - **Linked AC:** SPEC §2.2, §2.5, DoD §5.1 해당 시나리오 - - **Depends On:** Task 0 - - **병렬 가능:** Y - -- [ ] **Task 3: 핵심 유스케이스 및 상태 전이 구현** - - **Output:** 핵심 비즈니스 로직, 상태 전이 가드, 멱등성/재시도/복구 규칙 - - **Files:** `src/services/...` 또는 `src/usecases/...` - - **Test Files:** `tests/unit/test_*service.py` - - **Commands:** `pytest tests/unit/test_*service.py` - - **Verify:** 동일 입력 재시도 시 허용된 부수 효과만 발생하고, 상태 전이가 SPEC과 일치한다. - - **Linked AC:** SPEC §3.2, §3.3, DoD §5.1 해당 시나리오 - - **Depends On:** Task 1, Task 2 - - **병렬 가능:** N - -- [ ] **Task 4: 외부 의존성 어댑터 및 인프라 연동** - - **Output:** Storage/Broker/외부 API 클라이언트 인터페이스 및 운영/테스트 구현체 - - **Files:** `src/infra/storage/...`, `src/infra/broker/...`, `src/infra/external/...` - - **Test Files:** `tests/unit/test_*client.py` - - **Commands:** `pytest tests/unit/test_*client.py` - - **Verify:** 메시지/요청 페이로드, 타임아웃, 재시도 정책이 계약과 일치한다. - - **Linked AC:** SPEC §2.1, §2.3, §2.4, DoD §5.1 해당 시나리오 - - **Depends On:** Task 1 - - **병렬 가능:** Y - -### Phase 3: Integration & Ops - -- [ ] **Task 5: 라우터/컨슈머와 서비스 통합** - - **Output:** 인터페이스 계층과 서비스/저장소/어댑터가 실제 흐름으로 연결된 실행 가능한 기능 - - **Files:** `src/api/...`, `src/services/...`, `src/infra/...` - - **Test Files:** `tests/api/test_*.py` 또는 `tests/integration/test_*flow.py` - - **Commands:** `pytest tests/api/test_*.py` 또는 `pytest tests/integration/test_*flow.py` - - **Verify:** 주요 정상/예외 흐름이 엔드투엔드 또는 통합 테스트로 통과한다. - - **Linked AC:** DoD §5.1의 대상 시나리오 묶음 - - **Depends On:** Task 2, Task 3, Task 4 - - **병렬 가능:** N - -- [ ] **Task 6: 관측성 및 전역 예외 처리 적용** - - **Output:** 구조화 로깅(`trace_id` 포함), 핵심 메트릭, 공통 에러 응답/예외 매핑 - - **Files:** `src/common/...`, `src/middlewares/...`, `src/observability/...` - - **Test Files:** `tests/unit/test_error_handler.py`, `(필요 시) tests/unit/test_metrics.py` - - **Commands:** `pytest tests/unit/test_error_handler.py` - - **Verify:** 성공/실패 경로 모두에서 추적 필드와 에러 응답 계약이 일관된다. - - **Linked AC:** SPEC §4, SPEC §2.4, DoD §5.2 - - **Depends On:** Task 1, Task 3 - - **병렬 가능:** Y - -- [ ] **Task 7: 최종 검증 및 릴리스 준비** - - **Output:** 통합 체크리스트 완료, 배포/롤백 절차 검증, 커버리지 및 CI 기준 충족 - - **Files:** `.github/workflows/...`, `docs/...` 또는 배포 설정 파일 - - **Test Files:** 전체 테스트 스위트 - - **Commands:** `(lint 명령)`, `(type check 명령)`, `pytest`, `pytest --cov`, `alembic upgrade head` - - **Verify:** 완료 조건과 병합 게이트를 모두 만족한다. - - **Linked AC:** DoD §5.1, §5.2, §5.3 - - **Depends On:** Task 5, Task 6 - - **병렬 가능:** N - ---- +## 4. 전달 리스크와 안전장치 (Delivery Risks and Safeguards) -## 4. Integration Checklist & Done Criteria +| Risk | Impact | Mitigation | Verification | +| --- | --- | --- | --- | +| | | | | -> 컴포넌트 통합 및 최종 완료 판정을 위한 체크리스트 - -### 4.1 통합 체크리스트 (Integration Checklist) -- [ ] **SPEC 추적성:** 모든 구현 대상 엔드포인트/메시지/상태 전이가 SPEC의 명시된 계약과 연결되어 있는가? -- [ ] **계약 정합성:** API/MQ/스토리지/스키마 필드가 관련 컴포넌트 문서의 기대 버전과 일치하는가? -- [ ] **테넌시 및 권한:** 데이터 조회/조작 시 `user_id` 또는 동등한 권한 격리 규칙이 모든 레이어에 적용되었는가? -- [ ] **네트워크 복원력:** 외부 연동 포인트에 적절한 Timeout, Retry, Circuit Breaker 또는 대체 정책이 적용되었는가? -- [ ] **데이터 일관성:** SOT와 파생 데이터 간 부분 실패 시 보정/정리 로직이 동작하는가? -- [ ] **마이그레이션/초기화 재현성:** 새로운 환경에서 스키마/설정/필수 리소스를 재현 가능한가? -- [ ] **공유 계약 검토:** 관련 Tech Spec 문서를 확인했고, 남은 충돌이 없는가? - -### 4.2 완료 조건 (Definition of Done) -- [ ] SPEC §5.1에 정의된 시나리오 테스트가 모두 녹색이다. -- [ ] 단위·통합 테스트 합산 커버리지가 (목표 %) 이상이다 (`pytest-cov` 기준). -- [ ] 단위·통합 테스트가 CI에서 통과한다. -- [ ] 핵심 메트릭이 정상 노출된다. +다음 같은 항목을 포함한다: +- schema 또는 contract drift +- partial failure로 인한 inconsistent state +- idempotency gap +- rollout compatibility issue +- observability blind spot --- -## 5. Rollout & Rollback Plan - -> 배포 및 장애 발생 시 데이터/스키마 원복(되돌리기) 계획 +## 5. Rollout and Rollback -### 5.1 배포 계획 (Rollout) -- **환경 변수 추가:** (필수 환경 변수 목록과 기본 검증 방식) -- **인프라/스키마 변경:** 배포 전 필요한 마이그레이션/리소스 생성 절차 -- **호환성 확인:** 기존 메시지/스키마/클라이언트와의 호환성 영향 여부 +### 5.1 Rollout plan +- Migration / schema steps: +- Config / secret / infra changes: +- Backward / forward compatibility considerations: +- Monitoring signals to watch during rollout: +- Post-deploy checks: -### 5.2 롤백 계획 (Rollback) -- **애플리케이션 롤백:** 이전 버전 이미지/아티팩트로 즉시 복귀하는 절차 -- **데이터베이스 스키마 원복:** 역방향 마이그레이션 또는 안전한 복원 전략 -- **메시지/비동기 호환성:** 큐/토픽에 남아 있는 신규 포맷 메시지 처리 방안 -- **부분 적용 복구:** 배포 도중 일부 단계만 반영된 경우의 복구 순서 +### 5.2 Rollback plan +- App rollback: +- Data rollback or safe-forward plan: +- Async / message compatibility fallback: +- Partial deployment recovery: --- -## Assumptions (확정된 사항) +## 6. 완료 체크리스트 (Completion Checklist) -- (확정된 기술 선택, 도구, 외부 의존성 전제 조건을 기술한다) -- *예: DB 마이그레이션 도구는 Alembic을 사용한다.* -- *예: 통합 테스트는 Testcontainers 기반으로 수행한다.* -- *예: Worker의 멱등성은 system-design SOT를 준수하여, 중복 메시지를 안전하게 처리한다고 가정한다.* +- [ ] 모든 계획된 workstream이 target SPEC에 매핑된다. +- [ ] 계획된 테스트가 SPEC section 또는 acceptance criteria에 매핑된다. +- [ ] 최고 위험도의 business rule에 대해 명시적 자동화 검증 또는 문서화된 예외 사유가 있다. +- [ ] 저가치 또는 중복 테스트를 의도적으로 피했다. +- [ ] 필요한 observability와 failure-path 점검이 포함되어 있다. +- [ ] rollout / rollback 단계에 compatibility 가정과 monitoring signal이 포함되어 있다. +- [ ] 남아 있는 open question 또는 deferred item이 기록되어 있다. diff --git a/docs/Tech_Spec/template/spec_template.md b/docs/Tech_Spec/template/spec_template.md index 7d06f86..cbaee52 100644 --- a/docs/Tech_Spec/template/spec_template.md +++ b/docs/Tech_Spec/template/spec_template.md @@ -1,195 +1,167 @@ # [ComponentName] SPEC -**Meta** -- **Component ID:** (예: core-api-server / pipeline-worker / search-service) -- **SOT References:** [PRD], [System Design], [Plan], [Sequence Diagram 등] +> 독자 우선(reader-first) 계약 문서. +> 간결하지만 필요한 내용은 빠짐없이 담는다. 템플릿은 준수하되, 컴포넌트가 정말 더 많은 설명을 필요로 하지 않는 한 보통 80~180줄 내외의 compact한 문서를 지향한다. +> 이 문서에는 이 컴포넌트가 무엇을 해야 하는지, 왜 존재하는지, 핵심 기술 선택, 그리고 다른 구성요소가 신뢰할 수 있는 계약/제약만 기록한다. +> 이 문서를 구현 계획서로 만들지 않는다. +> +> 이 문서에 포함할 것 (Belongs here): +> - 컴포넌트가 소유하는 책임 +> - 컴포넌트의 존재 이유 / 성공 결과 +> - 외부 계약, 불변조건, 제약, 상태 규칙 +> - 선택한 기술 스택과 간단한 선정 근거 +> +> 이 문서에 포함하지 말 것 (Does NOT belong here): +> - 알고리즘, 내부 모듈/클래스 구조, 실행 순서, 작업 분할, 파일 단위 구현 내용, 마이그레이션 단계, rollout choreography -> PLAN +> - 긴 설계 대안 비교, 결정 이력 -> ADR +> - 저가치 테스트 목록, 테스트 permutation 나열 -> PLAN / 테스트 코드 +> +> 작성 규칙 (Authoring rules): +> - 서술형 문장보다 bullet/table을 우선한다. +> - 모든 bullet은 반드시 이 컴포넌트에 특화된 내용이어야 하며, generic filler는 삭제한다. +> - scalable / robust / maintainable 같은 모호한 근거는 구체적인 제약이나 계약에 연결되지 않으면 금지한다. +> - 한 섹션이 대략 5~7개 bullet 또는 작은 표 1개를 넘기기 시작하면 세부사항을 PLAN 또는 ADR로 이동한다. +> - 관련 없는 섹션은 통째로 삭제하고, 빈 placeholder는 남기지 않는다. +> - 구현 중 바뀔 수 있고 계약의 일부가 아닌 내용은 PLAN으로 보낸다. +> - 여러 대안 비교가 있었다면 여기에는 최종 선택안과 결정적 이유만 남기고, 비교 내용은 ADR로 보낸다. + +**메타 정보 (Meta)** +- Component ID: +- SOT: `docs/system-design.md` (이 SPEC은 system design SOT와 일관되어야 한다) +- Related docs: [PRD], [PLAN], [ADR if any] +- Status: Draft / Approved / Superseded --- -## 1. Context & Scope +## 1. 목적과 범위 (Purpose and Scope) -### 1.1 목적 (Purpose) -- **한 줄 요약:** 컴포넌트가 시스템에서 수행하는 핵심 역할. -- **비즈니스 목표:** (예: 업로드 완료 후 10분 이내 READY 달성) +### 1.1 한 줄 요약 +- [이 컴포넌트가 시스템에서 하는 일] -### 1.2 요구 기술 스택 및 환경 변수 (Tech Stack & Configs) -- 언어/프레임워크, ORM/DB, 주요 외부 라이브러리 -- 필수 환경 변수: `...` +### 1.2 책임 경계 +- In scope: +- Out of scope: +- Upstream dependencies: +- Downstream consumers: -### 1.3 경계 (Boundaries) -- **In-Scope:** 컴포넌트가 직접 책임지는 기능 나열. -- **Out-of-Scope:** 다른 컴포넌트에 위임하거나 이번 스펙에서 제외되는 기능 명시. +### 간단한 흐름 (Simple Flow) -### 1.4 상태 라이프사이클 기준 (필요 시) -- 정상 전이: 예) `PENDING -> UPLOADED -> PROCESSING -> READY` -- 예외 전이: 예) `FAILED` (`failed_stage` 기록) +### 1.3 기술 스택 선택 +| 영역 (Area) | 선택안 (Choice) | 왜 이 선택인가 | +| --- | --- | --- | +| Runtime / framework | | | +| Storage / DB | | | +| Messaging / async | | | +| Key libraries | | | ---- - -## 2. Contracts (Interface & Data) -> 외부 통신 규격과 저장소 접근을 **Delta 중심**으로 명세 - -### 2.1 API / Message Endpoint - -#### [HTTP API] +Notes: +- 각 행의 근거는 한 줄로 짧게 적고, 결정에 영향을 준 핵심 제약만 쓴다. +- tradeoff 분석이 길어지면 ADR을 작성하거나 링크한다. -- **Auth / Tenancy:** 예) JWT 필수, `user_id`로 테넌시 필터. - -| HTTP Method | Endpoint (URI) | Request | Success Response | Notes | -| --- | --- | --- | --- | --- | -| POST | `/api/v1/...` | 주요 필드/제약 | 201/202 응답 스키마 | 분기 조건 등 | +--- -- **스키마 제약 조건 (Pydantic 기준):** 주요 필드별 타입·범위·필수 여부를 나열한다. +## 2. 계약 (Contracts) -#### [Object Storage] *(해당 시)* +> 다른 컴포넌트, 운영자, 미래 구현자가 실제로 의존할 수 있는 계약만 정의한다. +> 다른 컴포넌트/운영자가 의존할 수 없는 내용이면 여기에 넣지 않는다. +> 외부에서 관찰 가능하거나 계약적으로 요구되는 경우가 아니라면 알고리즘, 내부 모듈 구조, 실행 순서, 작업 분해, 마이그레이션 단계는 금지한다. -- **StorageClient 인터페이스:** `generate_signed_url()`, `get_blob_metadata()`, `delete_object()` 등 메서드를 가진 추상 클래스를 정의한다. 구현체는 의존성 주입(DI)으로 교체 가능하다. - - `(ConcreteStorageClient)` — 운영 환경 구현체 - - `InMemoryStorageClient` — 로컬/단위 테스트 전용 구현체 (더미 URL 반환, 내부 dict로 적재·삭제 상태 관리) +### 2.1 외부 인터페이스 -#### [Message Broker / 비동기 큐] *(해당 시)* +#### 외부 진입 인터페이스 +| Interface | Method / Trigger | Input summary | Output summary | Auth / tenancy | Notes | +| --- | --- | --- | --- | --- | --- | +| | | | | | | -- **BrokerClient 인터페이스:** `publish(message)` 메서드를 가진 추상 클래스를 정의한다. 구현체는 의존성 주입(DI)으로 교체 가능하다. - - `(ConcreteBrokerClient)` — 운영 환경 구현체 - - `InMemoryBrokerClient` — 로컬/단위 테스트 전용 구현체 (발행 메시지를 메모리 리스트에 누적) -- **Exchange / Topic, Routing Key, Queue:** (해당 브로커 구성 명시) -- **Message Contract:** `docs/system-design.md` MessageEnvelope와 동일한 필드를 사용한다. +#### 메시지 / 이벤트 계약 (해당 시) +- Topic / exchange / queue: +- Producer / consumer responsibility: +- Delivery semantics: at-most-once / at-least-once / exactly-once-like +- Payload versioning rules: ```json { - "message_type": "PREPROCESS_REQUEST", + "event_type": "EXAMPLE_EVENT", "payload_version": "v1", - "trace_id": "UUID4", - "attempt": 1, - "video_id": "UUID4", - "issued_at": "ISO8601" + "trace_id": "UUID4" } ``` -### 2.2 Data Access (Reads & Writes) -| Type | Store | Entity/Table | Key/Filter | Mutation/Action | Notes (트랜잭션/인덱스/보안) | -| --- | --- | --- | --- | --- | --- | -| Read | ... | ... | ... | SELECT | 테넌시/인덱스 | -| Write | ... | ... | ... | INSERT/UPDATE | 트랜잭션 경계 | +#### 외부 연동 컴포넌트 계약 (해당 시) +| Dependency | Used for | Required behavior / assumption | Failure impact | +| --- | --- | --- | --- | +| | | | | -### 2.3 SLA & Constraints -- Timeout, Signed URL TTL, 파일 크기/확장자 제한, 페이지 limit 등 -- 강제 방식까지 명시 (예: `content-length-range`, 완료 시 size 재검증) +### 2.2 데이터 계약 -### 2.4 Error Contract & Messaging Semantics -| HTTP Status | Error Code | 발생 조건 | Retryable | +#### 소유 데이터 (이 컴포넌트가 SOT인 경우) +| Entity / table | Purpose | Key fields / invariants | Notes | | --- | --- | --- | --- | -| 400 | INVALID_ARGUMENT | 유효하지 않은 입력값 | N | -| 401 | UNAUTHENTICATED | JWT 미제공 또는 서명/만료 검증 실패 | N | -| 403 | FORBIDDEN | 테넌시 위반 (타인 리소스 접근) | N | -| 404 | NOT_FOUND | 미존재 리소스 접근 | N | -| 409 | CONFLICT | 허용되지 않은 상태 전이 시도 | N | -| 429 | RATE_LIMITED | 초당 요청 한도 초과 | Y (Backoff) | -| 500 | INTERNAL_ERROR | DB/외부 API 오류 | Y | -- **에러 응답 바디:** `{"code": "ERROR_CODE", "message": "설명 문자열", "trace_id": "UUID4"}` -- 멱등 응답 구분이 필요한 경우 (예: 최초 202, 중복 200) 별도 명시. -- MQ Ack/Nack, 재시도/Backoff, Poison/DLQ 정책 필요 시 추가. - -### 2.5 스키마 (DDL) *(SOT 소유 컴포넌트 필수 / 읽기 전용 참조 컴포넌트는 "참조 스키마"로 명시)* - -> SOT를 직접 관리하는 경우: 마이그레이션 도구(예: Alembic)로 관리하는 테이블 DDL을 전체 컬럼 기준으로 기술. -> 읽기 전용으로 참조하는 경우: 원본 관리 컴포넌트를 명시하고 접근 필드만 기재. - -```sql -CREATE TABLE (table_name) ( - id UUID PRIMARY KEY DEFAULT gen_random_uuid(), - user_id UUID NOT NULL, - status TEXT NOT NULL DEFAULT '...' CHECK (status IN ('...')), - deleted BOOLEAN NOT NULL DEFAULT FALSE, - created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), - updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW() -); -CREATE INDEX idx_(table)_(key) ON (table_name)(...); -``` +| | | | | ---- - -## 3. Core Design & Logic +#### 참조 데이터 (다른 SOT를 읽는 경우) +| Source owner | Entity / table | Fields relied on | Read-only assumptions | +| --- | --- | --- | --- | +| | | | | -### 3.1 주요 흐름 (Sequence) -- 핵심 플로우를 단계별 서술 (필요하면 Local/External 등 분기 별도 기술). +### 2.3 상태 및 비즈니스 규칙 +- 항상 유지되어야 하는 불변조건: +- 이 컴포넌트가 소유하는 허용 상태 전이: +- 거부되어야 하는 전이 / invalid condition: +- Idempotency rule: +- Multi-tenant / authorization rule: -### 3.2 상태 전이 (State Machine) -> 본 컴포넌트가 직접 트리거하는 전이만 구현 대상이다. ★ 표시 행은 타 컴포넌트 주도 전이로, 참조 목적으로만 기재한다. +명확성이 높아질 때만 표를 사용한다: -| From | To | Actor | Trigger | Guard | Side Effects | -| --- | --- | --- | --- | --- | --- | - -### 3.3 멱등성 및 복구 (Resilience) -- 멱등 키/조건, 중복 메시지 처리 정책, 재시도/보정(Reconcile) 전략. +| From | To | Trigger | Guard / rule | Required side effects | +| --- | --- | --- | --- | --- | +| | | | | | -### 3.4 Data Consistency & Orphan Prevention -- 트랜잭션 경계, 잠금/유니크 제약, 보정/클린업(예: lifecycle 정책). -- 삭제 책임 분리 여부 명시. +### 2.4 한계와 운영 제약 +- Performance / latency target: +- Throughput / rate / concurrency limits: +- Payload / file size / pagination limits: +- Timeout / TTL / retry constraints: +- Security / privacy constraints: ---- +### 2.5 에러 계약 +| Surface | Condition | Code / status | Retryable | Notes | +| --- | --- | --- | --- | --- | +| | | | | | -## 4. Observability & Ops -- **Logging:** 필수 필드(`trace_id`, `user_id`, 도메인 id), 포맷. -- **Metrics:** 핵심 지표(예: `mq_publish_fail_count`, `reconciler_republish_count`, `cursor_decode_fail_count`, p95 latency 등). -- **Alerts:** 임계치 정의는 인프라팀에 위임. 주요 감시 대상(예: 큐 지연, 재발행 급증, 5xx 비율 등)만 명시. -- **Trace Propagation:** 수신 `trace_id` 전파 규칙. +- 표준 에러 응답 형태: +```json +{"code":"ERROR_CODE","message":"human-readable summary","trace_id":"UUID4"} +``` --- -## 5. Acceptance Criteria (DoD) - -### 5.1 시나리오 검증 +## 3. 관측성과 운영 (Observability and Operations) -> 엔드포인트 또는 기능 단위로 섹션(`####`)을 나누고, 각 섹션 내에서 **정상** / **예외** 로 구분하여 `* [ ]` 체크박스로 기술한다. +운영자와 구현자가 반드시 보존해야 하는 것만 기록한다. -#### (엔드포인트 또는 기능명 — 예: POST /api/v1/resource) +- Required log fields: +- Key metrics / alerts worth tracking: +- Trace / correlation propagation rule: +- Reconciliation / cleanup requirement (if any): -**정상** -* [ ] (정상 흐름 시나리오 — 분기가 있으면 분기별로 기술) - -**예외** -* [ ] JWT 미제공 → 401 -* [ ] (유효성 위반 케이스) → 400 -* [ ] 타인 리소스 접근 → 403 -* [ ] 미존재 리소스 → 404 - -#### (엔드포인트 2 — 필요한 만큼 반복) - -**정상** -* [ ] ... - -**예외** -* [ ] ... - -#### (Reconciler / Background Worker 등 해당 시) +--- -**정상** -* [ ] (재발행 트리거 조건 충족 건 → 재발행 + 상태 변경) -* [ ] (조건 미충족 건 → 재발행 없음) +## 4. 인수 기준 (Acceptance Criteria) -### 5.2 검증을 위한 테스팅 전략 (Testing Strategy) +> 결과 중심으로 작성한다. 대략 3~7개의 critical outcome만 둔다. +> 각 기준은 외부에서 관찰 가능해야 하며, 비즈니스 규칙 또는 계약에 매핑되어야 한다. +> 여기에는 permutation이나 테스트 케이스를 나열하지 않는다. 상세 검증은 PLAN에 둔다. -에이전트는 아래 가이드라인을 만족하는 자동화 테스트를 작성해야 한다. -* 테스트 프레임워크는 `pytest`, `pytest-asyncio`, `httpx`를 사용한다. -* **커버리지 목표:** 단위·통합 테스트 합산 (목표 %) 이상을 달성한다 (`pytest-cov` 기준). -* DB 통합 테스트는 PostgreSQL 기반의 격리 환경(Testcontainers 또는 Docker Compose)을 사용한다. -* **외부 의존성 격리 전략:** - * Object Storage → `InMemoryStorageClient` (Test Double): 더미 URL 반환, 내부 dict로 적재·삭제 상태 관리. - * Message Broker → `InMemoryBrokerClient` (Test Double): 실제 MQ 없이 동작, 발행 메시지를 메모리 리스트에 누적. - * JWT 인증 → 테스트 전용 시크릿으로 실제 토큰을 생성하여 사용한다 (외부 인증 서버 호출 없음). - * 기타 외부 HTTP API(해당 시) → `AsyncMock`으로 대체하여 외부 호출 없이 동작한다. -* (해당 시) 시간 의존 로직(Reconciler, TTL 등)은 시간 고정(freezegun 등)과 상태별 fixture를 활용하여 재발행 조건을 검증한다. -* (해당 시) Opaque Cursor 계약은 encode/decode round-trip 테스트와 잘못된 토큰에 대한 예외 케이스를 포함한다. +### 4.1 반드시 통과해야 하는 시나리오 +- [ ] 외부에서 관찰 가능한 핵심 성공 결과가 end-to-end로 성립한다. +- [ ] 가장 위험도가 높은 비즈니스 규칙 또는 불변조건이 강제된다. +- [ ] Authorization / tenancy boundary가 강제된다. (해당 시) +- [ ] 실패 처리가 선언된 외부 에러/행동 계약을 따른다. +- [ ] Idempotency / duplicate handling이 명세대로 동작한다. (해당 시) -### 5.3 산출물 (Artifacts) +### 4.2 비목표 / 보류 항목 +- 이 spec에서 의도적으로 다루지 않는 항목을 명시한다. -폴더 구조는 `docs/Tech_Spec/folder_structure.md`를 참조한다. +--- -* [ ] HTTP 라우터 / 메시지 컨슈머 — (핵심 엔드포인트/큐 목록) -* [ ] Pydantic DTO — 요청/응답 스키마 -* [ ] 비즈니스 서비스 — (주요 유스케이스: 상태 전이, 멱등성 등) -* [ ] ORM 모델 — (엔티티 목록) -* [ ] StorageClient / BrokerClient 인터페이스 및 구현체 (운영 + InMemory) — 해당 시 -* [ ] (기타 인프라 어댑터 — 외부 API 클라이언트 등) -* [ ] 단위 테스트 / 통합 테스트 diff --git a/docs/Tech_Spec/Core_Api_Server_Plan.md b/docs/Tech_Spec/upload_search_Service/Core_Api_Server_Plan.md similarity index 100% rename from docs/Tech_Spec/Core_Api_Server_Plan.md rename to docs/Tech_Spec/upload_search_Service/Core_Api_Server_Plan.md diff --git a/docs/Tech_Spec/Core_Api_Server_Spec.md b/docs/Tech_Spec/upload_search_Service/Core_Api_Server_Spec.md similarity index 99% rename from docs/Tech_Spec/Core_Api_Server_Spec.md rename to docs/Tech_Spec/upload_search_Service/Core_Api_Server_Spec.md index 66eddde..a6baa0e 100644 --- a/docs/Tech_Spec/Core_Api_Server_Spec.md +++ b/docs/Tech_Spec/upload_search_Service/Core_Api_Server_Spec.md @@ -93,7 +93,7 @@ Core API는 `docs/system-design.md`에 정의된 상태 전이를 기준으로 * **PREPROCESS_REQUEST:** 큐 이름 `PREPROCESS_REQUEST`에 직접 발행한다. * **DELETE_REQUEST:** 큐 이름 `DELETE_REQUEST`에 직접 발행한다. * **재배달:** Visibility Timeout 기반으로 자동 재배달한다. -* **Message Contract:** `docs/system-design.md` 3.7 MessageEnvelope와 동일한 필드를 사용한다. +* **Message Contract:** `PREPROCESS_REQUEST`와 `DELETE_REQUEST`는 `docs/system-design.md` 3.12의 video 처리 메시지 규격과 동일한 필드를 사용한다. ```json { diff --git a/docs/Tech_Spec/Managed_Embedding_Endpoint_Plan.md b/docs/Tech_Spec/upload_search_Service/Managed_Embedding_Endpoint_Plan.md similarity index 100% rename from docs/Tech_Spec/Managed_Embedding_Endpoint_Plan.md rename to docs/Tech_Spec/upload_search_Service/Managed_Embedding_Endpoint_Plan.md diff --git a/docs/Tech_Spec/Managed_Embedding_Endpoint_Spec.md b/docs/Tech_Spec/upload_search_Service/Managed_Embedding_Endpoint_Spec.md similarity index 100% rename from docs/Tech_Spec/Managed_Embedding_Endpoint_Spec.md rename to docs/Tech_Spec/upload_search_Service/Managed_Embedding_Endpoint_Spec.md diff --git a/docs/Tech_Spec/Pipeline_Worker_Plan.md b/docs/Tech_Spec/upload_search_Service/Pipeline_Worker_Plan.md similarity index 100% rename from docs/Tech_Spec/Pipeline_Worker_Plan.md rename to docs/Tech_Spec/upload_search_Service/Pipeline_Worker_Plan.md diff --git a/docs/Tech_Spec/Pipeline_Worker_Spec.md b/docs/Tech_Spec/upload_search_Service/Pipeline_Worker_Spec.md similarity index 99% rename from docs/Tech_Spec/Pipeline_Worker_Spec.md rename to docs/Tech_Spec/upload_search_Service/Pipeline_Worker_Spec.md index 7ac0a47..f73702c 100644 --- a/docs/Tech_Spec/Pipeline_Worker_Spec.md +++ b/docs/Tech_Spec/upload_search_Service/Pipeline_Worker_Spec.md @@ -66,7 +66,7 @@ | Queue/Topic | Message Type | Payload (요약) | 동작 | | --- | --- | --- | --- | | 고정 큐명: `PREPROCESS_REQUEST` | `PREPROCESS_REQUEST` | `{"message_type": "PREPROCESS_REQUEST", "payload_version": "v1", "trace_id": "UUID", "attempt": 1, "video_id": "UUID", "issued_at": "ISO8601"}` | 파이프라인 구동 시작 | -| 고정 큐명: `DELETE_REQUEST` | `DELETE_REQUEST` | 동일 MessageEnvelope (message_type="DELETE_REQUEST") | 연쇄 삭제 실행 | +| 고정 큐명: `DELETE_REQUEST` | `DELETE_REQUEST` | 동일 video 처리 메시지 규격 (message_type="DELETE_REQUEST") | 연쇄 삭제 실행 | > **참고:** `user_id`, `storage_path` 등 상세 데이터는 payload에 포함하지 않는다. 워커가 진입 시 `video_id`를 키로 Metadata DB를 직접 조회하여 획득한다. (`system-design.md §3.7`) diff --git a/docs/Tech_Spec/Search_Service_Plan.md b/docs/Tech_Spec/upload_search_Service/Search_Service_Plan.md similarity index 100% rename from docs/Tech_Spec/Search_Service_Plan.md rename to docs/Tech_Spec/upload_search_Service/Search_Service_Plan.md diff --git a/docs/Tech_Spec/Search_Service_Spec.md b/docs/Tech_Spec/upload_search_Service/Search_Service_Spec.md similarity index 100% rename from docs/Tech_Spec/Search_Service_Spec.md rename to docs/Tech_Spec/upload_search_Service/Search_Service_Spec.md diff --git a/docs/system-design.md b/docs/system-design.md index 2e266d9..7eb7941 100644 --- a/docs/system-design.md +++ b/docs/system-design.md @@ -99,16 +99,16 @@ Admin 기능은 JWT claim의 role을 기준으로 운영자 권한을 별도 검 #### ML Lifecycle Worker -1. 데이터셋 전처리: 정기 배치 스케줄의 실행 책임은 ML Lifecycle Worker가 가진다. Worker는 정기 배치에 따라 신규 피드백을 학습 가능한 형태의 데이터셋으로 변환하여 Object Storage에 저장한다. 배치 실행은 중복 수행하지 않으며, 전처리 완료 후 학습 파이프라인을 자동 트리거한다. +1. 배치 스케줄과 실행 제어: 정기 배치 스케줄의 실행 책임은 ML Lifecycle Worker가 가진다. Worker는 신규 피드백을 학습 가능한 데이터셋으로 전처리하고, 같은 데이터셋에 대한 중복 배치를 시작하지 않는다. 2. 모델 학습: 최신 데이터셋을 입력으로 임베딩 모델 개선 학습을 자동 수행하고 후보 모델을 Model Artifact Files에 저장한다. 3. 모델 평가 및 결과 저장: 후보 모델과 기준 모델의 검색 성능을 별도 평가용 데이터셋으로 자동 비교 평가한다. 집계 결과는 Metadata DB에, 질의별 상세 결과는 아티팩트로 저장한다. 평가 결과는 품질 미달(FAIL)과 시스템 오류(ERROR)를 구분하여 기록한다. -4. 재색인 및 점진 재임베딩: 평가 PASS 시 후보 모델 전용 인덱스를 구축한다. 전체 코퍼스의 즉시 재색인을 배포 선행 조건으로 두지 않으며, 신규 유입 데이터부터 우선 반영한다. -5. 서빙 전환 및 세대 관리: 후보 모델 readiness와 우선 반영 대상의 인덱싱 완료가 확인되면 서빙을 전환한다. 서빙 전환 직전에는 마지막 정상 서빙 상태를 롤백 복구용 스냅샷으로 저장한다. 전환 후 온라인 검색은 active와 previous의 최대 2세대만 병행 지원한다. +4. 내부 단계 hand off: 평가 PASS가 확정되면 같은 Worker 내부의 다음 책임이 직접 이어받는다. 수신 책임은 실행 레코드와 관련 SOT를 다시 읽어 필요한 문맥을 복원한다. +5. 재색인 및 서빙 전환: 릴리스·재색인 책임은 후보 모델 전용 인덱스를 구축하고, 전체 코퍼스의 즉시 재색인을 배포 선행 조건으로 두지 않으며, 신규 유입 데이터부터 우선 반영한다. 후보 모델 readiness와 우선 반영 대상의 인덱싱 완료가 확인되면 서빙 전환 직전에 기준 시각을 고정하고 전환을 수행한다. 6. 이전 세대 정리: previous보다 더 오래된 세대의 데이터는 중간 버전을 거치지 않고 최신 active 모델로 점진 재임베딩한다. 7. 롤백 복구: 운영자가 새 모델을 문제 모델로 판정하면, 마지막 정상 서빙 상태를 가리키는 rollback snapshot을 기준으로 rollback 복구를 시작한다. 8. 복구 후 후처리: rollback 이후 문제 모델 기간에 생성된 기존 데이터가 포함된 영상은 복구가 끝날 때까지 검색 범위에서 제외한다. 해당 데이터는 복원된 정상 서빙 상태 기준으로 백그라운드 재임베딩하며, 복구가 끝난 영상부터 다시 검색 범위에 합류시킨다. 이 재임베딩은 rollback 완료와 분리된 후속 복구 작업이다. -9. 실행 제어 및 실패 처리: 동시에 활성 상태인 MLPipelineRun은 하나만 유지한다. 실행 중 새 데이터셋이 준비되면 FIFO로 모두 쌓지 않고, 최신 데이터셋 기준의 다음 실행만 남긴다. 각 단계의 진행 상태와 실패 정보는 MLPipelineRun에 계속 기록하며, 신규 데이터셋으로 대체된 실행은 SUPERSEDED로 표시한다. -10. 내부 책임 분리 원칙: ML Lifecycle Worker는 단일 배포 단위로 유지하되, 내부 구현은 단계별 책임이 섞이지 않도록 1모듈 1책임 원칙으로 분리한다. 실행 제어, 학습/평가, 재색인, 점진 재임베딩, 서빙 전환, 롤백 복구 책임은 서로 독립적으로 변경·재실행 가능해야 하며, 구체적인 모듈 구조와 상호작용은 후속 Spec에서 정의한다. +9. 재조정과 복구: Worker는 중단되거나 지연된 ML 실행과 릴리스·재색인 작업을 공유 SOT 기준으로 다시 이어받을 수 있어야 한다. 정체된 실행 감지, 재시도, 최신 대기 실행 승격은 같은 수명주기 레코드를 기준으로 집행한다. +10. 내부 책임 분리 원칙: ML Lifecycle Worker는 단일 배포 단위로 유지하되, 내부 구현은 일정 실행, 실행 제어, 릴리스·재색인, 재조정 책임처럼 역할이 섞이지 않도록 분리한다. 구체적인 모듈 구조와 예외 처리는 후속 Spec에서 정의한다. --- @@ -373,7 +373,7 @@ Admin 기능은 JWT claim의 role을 기준으로 운영자 권한을 별도 검 **처리** 1. ML Lifecycle Worker가 정기 배치에 따라 신규 피드백 원본 로그를 읽고, 검색 시점의 불변 서빙 맥락이 포함된 이벤트만 학습 가능한 데이터셋으로 전처리한다. 2. 생성된 데이터셋은 학습 시점의 입력을 다시 추적할 수 있도록 버전 단위로 저장한다. 학습용 데이터셋과 평가용 데이터셋은 분리된 산출물로 관리한다. -3. 전처리 완료 후 학습 파이프라인을 자동 트리거한다. 이미 MLPipelineRun이 실행 중이면 즉시 시작하지 않고, 최신 데이터셋 기준의 다음 실행을 대기 상태로 둔다. +3. 전처리 완료 후 같은 Worker의 실행 제어가 학습 파이프라인을 자동으로 이어받는다. 이미 MLPipelineRun이 실행 중이면 즉시 시작하지 않고, 최신 데이터셋 기준의 다음 실행 하나만 대기 상태로 둔다. **출력** - 학습용 데이터셋 생성 @@ -393,8 +393,8 @@ Admin 기능은 JWT claim의 role을 기준으로 운영자 권한을 별도 검 **처리** 1. 전처리 완료 후 ML Lifecycle Worker가 최신 학습용 데이터셋으로 후보 임베딩 모델을 학습하고, 결과 모델을 저장한다. 이후 실행 상태 추적을 위해 MLPipelineRun을 생성한다. 2. ML Lifecycle Worker가 후보 모델과 기준 모델의 검색 성능을 별도 평가용 데이터셋으로 비교 평가한다. 평가 결과 요약은 Metadata DB에 저장하고, 상세 결과는 아티팩트로 저장한다. -3. 평가를 통과하면 ML Lifecycle Worker가 후보 모델 전용 인덱스를 구축한다. 전체 코퍼스의 즉시 재색인은 필수 조건이 아니며, 신규 유입 데이터와 고활성 데이터부터 우선 반영한다. 이 과정에서도 사용자 검색은 기존 서빙을 유지한다. -4. 후보 모델 readiness와 우선 반영 대상의 인덱싱 완료가 확인되면 ML Lifecycle Worker가 마지막 정상 서빙 상태를 롤백 복구용 스냅샷으로 저장한 뒤 서빙을 전환한다. 전환 후에는 직전 active 조합이 previous 세대로 보존된다. +3. 평가를 통과하면 같은 Worker 내부의 릴리스·재색인 책임이 후보 모델 전용 인덱스 구축을 이어받는다. 전체 코퍼스의 즉시 재색인은 필수 조건이 아니며, 신규 유입 데이터와 고활성 데이터부터 우선 반영한다. 이 과정에서도 사용자 검색은 기존 서빙을 유지한다. +4. 후보 모델 readiness와 우선 반영 대상의 인덱싱 완료가 확인되면 릴리스·재색인 책임이 기준 시각을 고정하고, 마지막 정상 서빙 상태를 롤백 복구용 스냅샷으로 저장한 뒤 서빙을 전환한다. 전환 후에는 직전 active 조합이 previous 세대로 보존된다. 5. 서빙 전환 이후 남아 있는 더 오래된 세대 데이터는 최신 active 모델 기준으로 점진 재임베딩한다. 온라인 검색은 active와 previous의 최대 2세대까지만 병행 지원한다. 6. 운영자가 배포 후 새 모델을 문제 모델로 판정하면, 마지막 정상 서빙 상태를 가리키는 rollback snapshot을 기준으로 rollback 복구를 시작한다. 7. rollback 대상 모델 readiness와 snapshot 인덱스 복원이 완료되면 ML Lifecycle Worker는 마지막 정상 서빙 상태를 기준으로 `ModelRelease`를 복원하고, 전환 중 후보 조합 관련 메타데이터는 비운다. @@ -542,7 +542,7 @@ STT 결과물로 생성되는 시간 구간 단위의 원본 텍스트 ## 3.10 MLPipelineRun ML 피드백 루프 파이프라인 실행 1회에 대한 추적 레코드 - id: 실행 고유 ID -- status: 현재 진행 상태 +- status: 현재 진행 상태 (`PENDING`, `RUNNING`, `READY_FOR_RELEASE`, `FAILED`, `SUPERSEDED`) - failed_stage: 실패 단계 - failure_type: 실패 유형 - candidate_model_version: 이번 실행의 후보 모델 버전 @@ -553,6 +553,7 @@ ML 피드백 루프 파이프라인 실행 1회에 대한 추적 레코드 - superseded_by_run_id: 더 최신 실행으로 대체되었을 때의 실행 ID - failure_reason: 실패 원인 요약 - created_at / updated_at +- 제약: 동시에 `RUNNING` 상태인 run은 하나만 허용한다. 동시에 `PENDING` 상태인 run도 하나만 허용하며, 더 최신 `dataset_version`이 오면 기존 `PENDING` run은 `SUPERSEDED`로 전환한다. ## 3.11 ModelRelease 모델 서빙 상태의 SOT. 현재 서빙 조합, 전환 중인 후보 조합, 마지막 정상 서빙 조합의 롤백 복구용 스냅샷 포인터를 관리하는 릴리스 레코드. snapshot 필드는 실제 모델/인덱스 본체가 아니라 복원 대상 모델 버전과 인덱스 식별자를 가리키는 메타데이터다. 실제 모델 파일은 Model Artifact Files에, 인덱스 스냅샷은 Index Snapshot Files에 보관한다