#!/usr/bin/env bash # (레거시) stock.exdev.co.kr Qdrant 스냅샷 → 로컬 Qdrant 복원 # 로컬 start-all.sh 는 원격 Qdrant(stock.exdev.co.kr) 를 직접 사용하므로 보통 불필요 # # ./local-dev/sync-qdrant-from-exdev.sh # ./local-dev/sync-qdrant-from-exdev.sh --force set -euo pipefail ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" RUN_DIR="${ROOT_DIR}/local-dev/run" SNAP_DIR="${RUN_DIR}/qdrant-snapshots" EXDEV_USER="${EXDEV_USER:-aidev}" EXDEV_HOST="${EXDEV_HOST:-chatbot.exdev.co.kr}" REMOTE_QDRANT="${REMOTE_QDRANT:-http://stock.exdev.co.kr:6333}" LOCAL_QDRANT="${LOCAL_QDRANT:-http://127.0.0.1:6335}" COLLECTION="${QDRANT_COLLECTION:-qa_vectors_v2}" FORCE=false for arg in "$@"; do case "$arg" in --force) FORCE=true ;; -h|--help) sed -n '2,6p' "$0" exit 0 ;; esac done log() { echo "[$(date '+%H:%M:%S')] $*"; } fail() { log "ERROR: $*"; exit 1; } mkdir -p "${SNAP_DIR}" local_count() { curl -sf "${LOCAL_QDRANT}/collections/${COLLECTION}" 2>/dev/null \ | python3 -c "import sys,json; d=json.load(sys.stdin); print(d.get('result',{}).get('points_count',0))" 2>/dev/null \ || echo 0 } remote_count() { ssh "${EXDEV_USER}@${EXDEV_HOST}" \ "curl -sf '${REMOTE_QDRANT}/collections/${COLLECTION}'" 2>/dev/null \ | python3 -c "import sys,json; d=json.load(sys.stdin); print(d.get('result',{}).get('points_count',0))" 2>/dev/null \ || echo 0 } curl -sf "${LOCAL_QDRANT}/readyz" >/dev/null 2>&1 \ || curl -sf "${LOCAL_QDRANT}/collections" >/dev/null 2>&1 \ || fail "로컬 Qdrant 미기동 — start-all.sh 먼저 실행" lc="$(local_count)" rc="$(remote_count)" log "로컬 points: ${lc}, exdev points: ${rc}" if [[ "${lc}" -gt 0 ]] && ! $FORCE; then log "로컬에 이미 ${lc}건 — 건너뜀 (--force 로 덮어쓰기)" exit 0 fi log "exdev 스냅샷 생성..." snap_json="$(ssh "${EXDEV_USER}@${EXDEV_HOST}" \ "curl -sf -X POST '${REMOTE_QDRANT}/collections/${COLLECTION}/snapshots'")" snap_name="$(printf '%s' "${snap_json}" | python3 -c "import sys,json; print(json.load(sys.stdin)['result']['name'])")" snap_size="$(printf '%s' "${snap_json}" | python3 -c "import sys,json; print(json.load(sys.stdin)['result']['size'])")" log "스냅샷: ${snap_name} ($(numfmt --to=iec "${snap_size}" 2>/dev/null || echo "${snap_size} bytes"))" local_file="${SNAP_DIR}/${snap_name}" log "다운로드 → ${local_file}" ssh "${EXDEV_USER}@${EXDEV_HOST}" \ "curl -sf '${REMOTE_QDRANT}/collections/${COLLECTION}/snapshots/${snap_name}'" \ > "${local_file}" [[ -s "${local_file}" ]] || fail "스냅샷 파일 비어 있음" log "로컬 컬렉션 삭제 후 복원..." curl -sf -X DELETE "${LOCAL_QDRANT}/collections/${COLLECTION}" >/dev/null 2>&1 || true restore_json="$(curl -sf -X POST \ "${LOCAL_QDRANT}/collections/${COLLECTION}/snapshots/upload?priority=snapshot" \ -H "Content-Type: multipart/form-data" \ -F "snapshot=@${local_file}")" || fail "스냅샷 업로드 실패" log "복원 응답: ${restore_json}" for i in $(seq 1 30); do lc="$(local_count)" if [[ "${lc}" -ge "$(( rc * 9 / 10 ))" ]] && [[ "${lc}" -gt 0 ]]; then log "✅ Qdrant 동기화 완료 — ${lc} points" curl -sf "${ROOT_DIR}/exAiChatBot-chatbot2.0-agent/.env" >/dev/null 2>&1 || true exit 0 fi sleep 2 done lc="$(local_count)" if [[ "${lc}" -gt 0 ]]; then log "✅ Qdrant 동기화 완료 — ${lc} points (일부만 반영됐을 수 있음)" else fail "복원 후 points_count=0" fi