Files
exAichatbot_agent/local-dev/sync-qdrant-from-exdev.sh
T
2026-08-13 17:39:39 +09:00

102 lines
3.4 KiB
Bash

#!/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