#!/usr/bin/env bash # ──────────────────────────────────────────────────────────────────────────── # Qdrant Snapshot Sync Tool # Syncs a Qdrant collection from the local machine to a remote environment # using the safe Snapshot API to avoid RocksDB corruption. # ──────────────────────────────────────────────────────────────────────────── set -euo pipefail # Load environment variables if [ -f .env ]; then set -a; source .env; set +a fi # ── Configuration ────────────────────────────────────────────────────────── TARGET_ENV="${1:-}" # testing | staging | branch_slug | prod COLLECTION="${2:-kabelfachmann}" SSH_HOST="root@alpha.mintel.me" if [[ -z "$TARGET_ENV" ]]; then echo "Usage: pnpm run qdrant:push [collection]" echo "Example: pnpm run qdrant:push testing kabelfachmann" echo "Example: pnpm run qdrant:push mein-feature-slug kabelfachmann" exit 1 fi LOCAL_QDRANT_URL=${QDRANT_URL:-"http://localhost:6337"} TIMEOUT=300 # 5 minutes for large snapshots get_target_path() { case "$1" in testing) echo "/home/deploy/sites/testing.klz-cables.com" ;; staging) echo "/home/deploy/sites/staging.klz-cables.com" ;; prod|production) echo "/home/deploy/sites/klz-cables.com" ;; *) echo "/home/deploy/sites/branch.klz-cables.com/$1" ;; esac } get_project_name() { case "$1" in testing) echo "klz-testing" ;; staging) echo "klz-staging" ;; prod|production) echo "klz-cablescom" ;; *) echo "klz-branch-$1" ;; esac } TGT_PATH=$(get_target_path "$TARGET_ENV") PROJECT_NAME=$(get_project_name "$TARGET_ENV") QDRANT_CONTAINER="${PROJECT_NAME}-klz-qdrant-1" WORK_DIR=$(mktemp -d) echo "🚀 Syncing Qdrant Collection '$COLLECTION' to: $TARGET_ENV" # 1. Create Snapshot Locally echo "📸 1/5 Creating snapshot on local Qdrant ($LOCAL_QDRANT_URL)..." SNAPSHOT_INFO=$(curl --max-time $TIMEOUT -s -X POST "$LOCAL_QDRANT_URL/collections/$COLLECTION/snapshots") if ! echo "$SNAPSHOT_INFO" | grep -q '"status":"ok"'; then echo "❌ Failed to create snapshot." echo "Response: $SNAPSHOT_INFO" exit 1 fi SNAPSHOT_NAME=$(echo "$SNAPSHOT_INFO" | grep -o '"name":"[^"]*' | cut -d'"' -f4) echo " ✅ Snapshot created: $SNAPSHOT_NAME" # 2. Download Snapshot echo "⬇️ 2/5 Downloading snapshot..." curl --max-time $TIMEOUT -s -o "$WORK_DIR/$SNAPSHOT_NAME" "$LOCAL_QDRANT_URL/collections/$COLLECTION/snapshots/$SNAPSHOT_NAME" echo " ✅ Downloaded to $WORK_DIR/$SNAPSHOT_NAME" # 3. Transfer Snapshot echo "📤 3/5 Uploading snapshot to Alpha ($SSH_HOST)..." SSH_OPTS="-o ServerAliveInterval=60 -o ServerAliveCountMax=10 -o ConnectTimeout=30" ssh $SSH_OPTS "$SSH_HOST" "mkdir -p $TGT_PATH/qdrant_tmp" rsync --partial --progress --timeout=600 -e "ssh $SSH_OPTS" \ "$WORK_DIR/$SNAPSHOT_NAME" "$SSH_HOST:$TGT_PATH/qdrant_tmp/$SNAPSHOT_NAME" echo " ✅ Upload complete." # 4. Restore Snapshot on Remote Server echo "🔄 4/5 Restoring snapshot on target container ($QDRANT_CONTAINER)..." # Qdrant restore process: # - Recreate collection (so it is clean) # - Download snapshot to container # - Recover from snapshot file ssh $SSH_OPTS "$SSH_HOST" << EOF set -e # Step A: Copy file into the container docker cp "$TGT_PATH/qdrant_tmp/$SNAPSHOT_NAME" $QDRANT_CONTAINER:/qdrant/$SNAPSHOT_NAME # Step B: Delete existing collection curl -s -X DELETE "http://127.0.0.1:6333/collections/$COLLECTION" > /dev/null # Step C: Re-create empty collection (required before recovery) # wir nutzen die standard vector config vom Kabelfachmann (Cosine, 384 dim für all-MiniLM-L6-v2) curl -s -X PUT "http://127.0.0.1:6333/collections/$COLLECTION" \ -H 'Content-Type: application/json' \ -d '{ "vectors": { "size": 384, "distance": "Cosine" } }' > /dev/null # Step D: Recover echo " [Remote] Triggering recover API..." curl -s -X PUT "http://127.0.0.1:6333/collections/$COLLECTION/snapshots/recover" \ -H 'Content-Type: application/json' \ -d '{ "location": "file:///qdrant/'$SNAPSHOT_NAME'" }' > /dev/null # Step E: Cleanup docker exec $QDRANT_CONTAINER rm /qdrant/$SNAPSHOT_NAME rm -rf "$TGT_PATH/qdrant_tmp" EOF echo " ✅ Restore complete." # 5. Local Cleanup echo "🧹 5/5 Cleaning up..." rm -rf "$WORK_DIR" # Delete snapshot from local Qdrant server to save space curl -s -X DELETE "$LOCAL_QDRANT_URL/collections/$COLLECTION/snapshots/$SNAPSHOT_NAME" > /dev/null echo " ✅ Local cleanup done." echo "" echo "🎉 Successfully synced Qdrant collection '$COLLECTION' to $TARGET_ENV!"