# S1 데이터·백엔드 트랙 적용 runbook (plan ds-s1-backend-1) > 코드는 `feat/s1-dedup-fields` 브랜치에 완성. 이 문서는 **prod(GPU) 적용 게이트** 절차. > ⚠ 적용은 사용자 명시 go 필요 — 본 runbook 은 자동 실행되지 않는다. ## 0. 사전 조건 (게이트) - [ ] **검색실험 Soft Lock 확인** — `~/.claude/.search-experiment-active` 부재여야 함. 현재(2026-06-05) 부재 = 비활성. migration 317 은 startup 자동적용 → `docker compose up` 이 restart 를 유발하므로, 실험 활성 시엔 예외창 합의 후에만. - [ ] **불가침 면 (검색실험 유효성)**: embedding 모델 / 벡터 인덱스(ivfflat/partial) / retrieval config / config.yaml 의 ai·model 섹션 **미접촉**. 본 트랙 변경면은 dedup 컬럼 + office_md + storage scaffold(env) 뿐. ## 1. migration 번호 - 317(dedup 3컬럼) **단일** 클레임. P0-4=(C) 무변경이라 신규 migration 미추가. - S2/S3 트랙이 같은 317 을 발행하지 않도록 조율(startup 카오스 방지). ## 2. restart 셋 (한 번에 배치) | 서비스 | 변경 | 재시작 사유 | |---|---|---| | `fastapi` | A(317 dedup) + B(dedup API) + D(storage scaffold) | startup migration 자동적용 + 코드 | | `marker_worker`(fastapi 내 스케줄러) | C(office_md 분기) + **markitdown 신규 pip dep** | rebuild 필요 | > markitdown 은 신규 의존성 → `docker compose build` 필수(force-recreate 만으론 image 미갱신, > feedback_docker_compose_build_vs_force_recreate). office 변환(OOXML)에만 필요. ## 3. 적용 순서 (inventory → config → deploy → verify) ```bash ssh gpu && cd ~/Documents/code/hyungi_Document_Server # (1) pre-A-1 안전망 — DB 덤프 (repo 밖) bash scripts/s1_pre_change_backup.sh pre-a1 # (2) 코드 가져오기 + 빌드(markitdown dep 반영) + 적용 git fetch && git checkout feat/s1-dedup-fields # 또는 main 머지 후 main docker compose build fastapi # markitdown 설치 (requirements 에 추가 필요) docker compose up -d fastapi # startup 에서 migration 317 자동적용 # (3) migration 317 적용 확인 docker compose exec -T postgres psql -U pkm -d pkm -c \ "SELECT version,name FROM schema_migrations WHERE version=317;" docker compose exec -T postgres psql -U pkm -d pkm -c \ "\d documents" | grep -E 'original_filename|duplicate_of|duplicate_count' ``` > **requirements**: office OOXML 변환에 `markitdown` 추가 필요(`requirements.txt`/pyproject). > markdownify·LibreOffice 는 기존. 빌드 전 dep 추가 PR 필수(없으면 OOXML 변환이 OfficeMdError→failed, > hwp/PDF/passthrough 는 정상). ## 4. backfill (코드 적용·검증 후, 야간 비중첩창) > dedup 컬럼 정합은 **야간 잡 `dedup_reconcile`(03:30 KST, main.py)** 이 매일 멱등 재계산한다 > (soft-delete 잔여 드리프트 자동 정리). 아래 `backfill_dedup.py` 수동 실행은 적용 직후 1회 > 초기 채움/즉시 확인용 — 이후엔 야간 잡이 유지. ```bash # (4a) dedup backfill (초기 1회) — 먼저 dry-run 으로 정확한 UPDATE set 확인 bash scripts/s1_pre_change_backup.sh pre-b4 docker compose exec fastapi python /app/scripts/backfill_dedup.py --dry-run docker compose exec fastapi python /app/scripts/backfill_dedup.py --apply # (4b) office/hwp pending markdown 백필 — C-2 라이브 ingestion 과 비중첩 야간창 docker compose exec fastapi python /app/scripts/backfill_nonpdf_markdown.py --dry-run docker compose exec fastapi python /app/scripts/backfill_nonpdf_markdown.py --apply --limit 20 # sample 먼저 docker compose exec fastapi python /app/scripts/backfill_nonpdf_markdown.py --apply # 전체 ``` ## 5. verify (smoke) ```bash # /duplicates shape curl -s -H "Authorization: Bearer $TOK" https://document.hyungi.net/api/documents/duplicates | jq '{total_groups,total_duplicate_docs, g0:.groups[0]}' # office 변환 결과 (sample doc) docker compose exec -T postgres psql -U pkm -d pkm -c \ "SELECT md_status,md_extraction_engine,length(md_content) FROM documents WHERE id=;" # md_status success→completed 직렬화 (앱 계약) curl -s -H "Authorization: Bearer $TOK" https://document.hyungi.net/api/documents/ | jq '.md_status' ``` ## 6. 롤백 - 컬럼만 빠른 롤백: `scripts/rollback_317.sql` (수동, schema_migrations 317 행도 삭제). - 전체 복원: `scripts/s1_pre_change_backup.sh` 가 출력한 `.sql.gz` → psql 복원.