fix: close internal semantic review gaps
This commit is contained in:
@@ -3,7 +3,17 @@ set -eu
|
||||
|
||||
cd "$(dirname "$0")/.."
|
||||
tmp=$(mktemp -d)
|
||||
trap 'rm -rf "$tmp"' EXIT HUP INT TERM
|
||||
holder_pid=
|
||||
holder_release="$tmp/holder-release"
|
||||
cleanup() {
|
||||
touch "$holder_release"
|
||||
if [ -n "${holder_pid:-}" ]; then
|
||||
kill "$holder_pid" 2>/dev/null || true
|
||||
wait "$holder_pid" 2>/dev/null || true
|
||||
fi
|
||||
rm -rf "$tmp"
|
||||
}
|
||||
trap cleanup EXIT HUP INT TERM
|
||||
fakebin="$tmp/bin"
|
||||
mkdir "$fakebin"
|
||||
|
||||
@@ -28,6 +38,15 @@ run_backup() {
|
||||
volume_name=$1
|
||||
backup_dir=$2
|
||||
output_name=$3
|
||||
if [ -n "${RUN_BLOCK_READY:-}" ]; then
|
||||
: >"$RUN_BLOCK_READY"
|
||||
attempts=0
|
||||
while [ ! -e "${RUN_BLOCK_RELEASE:?}" ]; do
|
||||
attempts=$((attempts + 1))
|
||||
[ "$attempts" -lt 400 ] || exit 24
|
||||
sleep 0.05
|
||||
done
|
||||
fi
|
||||
staging=$(mktemp -d "${TMPDIR:-/tmp}/fake-qdrant-backup.XXXXXX")
|
||||
mkdir -p "$staging/payload"
|
||||
cat >"$staging/manifest.env" <<EOF
|
||||
@@ -36,12 +55,59 @@ project_name=${PROJECT_NAME:?}
|
||||
volume_name=$volume_name
|
||||
volume_role=qdrant-data
|
||||
helper_image=${HELPER_IMAGE:?}
|
||||
created_utc=2026-08-08T17:35:36Z
|
||||
EOF
|
||||
cp -R "$(volume_dir_for "$volume_name")"/. "$staging/payload"/
|
||||
tar -C "$staging" -cf "$backup_dir/$output_name" manifest.env payload
|
||||
rm -rf "$staging"
|
||||
}
|
||||
|
||||
lock_state_root=${LOCK_ROOT:-$(dirname "${VOLUME_ROOT:?}")/locks}
|
||||
mkdir -p "$lock_state_root"
|
||||
|
||||
if [ "$1" = create ]; then
|
||||
shift
|
||||
lock_name=
|
||||
lock_owner=
|
||||
lock_volume=
|
||||
while [ "$#" -gt 0 ]; do
|
||||
case "$1" in
|
||||
--name) lock_name=$2; shift 2 ;;
|
||||
--label)
|
||||
case "$2" in
|
||||
com.thothii.qdrant-operation-owner=*) lock_owner=${2#*=} ;;
|
||||
com.thothii.qdrant-operation-volume=*) lock_volume=${2#*=} ;;
|
||||
esac
|
||||
shift 2
|
||||
;;
|
||||
*) shift ;;
|
||||
esac
|
||||
done
|
||||
[ -n "$lock_name" ] && [ -n "$lock_owner" ] && [ -n "$lock_volume" ] || exit 25
|
||||
state="$lock_state_root/$lock_name"
|
||||
mkdir "$state" 2>/dev/null || exit 1
|
||||
printf '%s\n' "$lock_owner" >"$state/owner"
|
||||
printf '%s\n' "$lock_volume" >"$state/volume"
|
||||
printf '%s\n' "$lock_name"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [ "$1" = inspect ]; then
|
||||
for lock_name in "$@"; do :; done
|
||||
state="$lock_state_root/$lock_name"
|
||||
[ -d "$state" ] || exit 1
|
||||
printf '%s %s\n' "$(cat "$state/owner")" "$(cat "$state/volume")"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [ "$1" = rm ]; then
|
||||
for lock_name in "$@"; do :; done
|
||||
state="$lock_state_root/$lock_name"
|
||||
[ -d "$state" ] || exit 1
|
||||
rm -rf "$state"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
run_restore() {
|
||||
volume_name=$1
|
||||
backup_dir=$2
|
||||
@@ -219,6 +285,7 @@ PY
|
||||
|
||||
backup_output="$tmp/qdrant-backup.tar"
|
||||
docker_log="$tmp/docker-backup.log"
|
||||
lock_name="${project}_qdrant-data-operation-lock"
|
||||
PATH="$fakebin:$PATH" DOCKER_LOG="$docker_log" PROJECT_NAME="$project" VOLUME_ROOT="$volume_root" \
|
||||
HELPER_IMAGE="qdrant/qdrant:v1.18.2@sha256:75eab8c4ba42096724fdcfde8b4de0b5713d529dde32f285a1f86fdcb2c9e50c" \
|
||||
./scripts/vector-backup.sh --project-name "$project" --output "$backup_output" >/dev/null
|
||||
@@ -231,6 +298,15 @@ tar -xOf "$backup_output" manifest.env | grep -qx "volume_name=$volume_name"
|
||||
grep -q "run --rm --mount type=volume,src=$volume_name,dst=/qdrant-data,readonly --mount type=bind,src=$tmp,dst=/backup" "$docker_log"
|
||||
grep -q "compose --project-name $project stop qdrant" "$docker_log"
|
||||
grep -q "compose --project-name $project start qdrant" "$docker_log"
|
||||
grep -q "create --name $lock_name" "$docker_log"
|
||||
grep -q "rm -f $lock_name" "$docker_log"
|
||||
test ! -e "$tmp/locks/$lock_name"
|
||||
lock_line=$(grep -n "create --name $lock_name" "$docker_log" | sed -n '1s/:.*//p')
|
||||
volume_line=$(grep -n '^volume ls ' "$docker_log" | sed -n '1s/:.*//p')
|
||||
[ "$lock_line" -lt "$volume_line" ] || {
|
||||
echo "backup resolved volume state before acquiring the operation lock" >&2
|
||||
exit 1
|
||||
}
|
||||
if grep -q "volume inspect --format {{ .Mountpoint }}" "$docker_log"; then
|
||||
echo "backup consulted Docker mountpoints" >&2
|
||||
exit 1
|
||||
@@ -240,6 +316,61 @@ if grep -q "prune" "$docker_log"; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
mkdir -p "$tmp/locks/$lock_name"
|
||||
printf '%s\n' foreign-owner >"$tmp/locks/$lock_name/owner"
|
||||
printf '%s\n' "$volume_name" >"$tmp/locks/$lock_name/volume"
|
||||
foreign_lock_log="$tmp/foreign-lock.log"
|
||||
if PATH="$fakebin:$PATH" DOCKER_LOG="$foreign_lock_log" PROJECT_NAME="$project" VOLUME_ROOT="$volume_root" \
|
||||
HELPER_IMAGE="qdrant/qdrant:v1.18.2@sha256:75eab8c4ba42096724fdcfde8b4de0b5713d529dde32f285a1f86fdcb2c9e50c" \
|
||||
./scripts/vector-backup.sh --project-name "$project" --output "$tmp/foreign-lock.tar" \
|
||||
>"$tmp/foreign-lock.out" 2>"$tmp/foreign-lock.err"; then
|
||||
echo "backup ignored a foreign operation lock" >&2
|
||||
exit 1
|
||||
fi
|
||||
grep -q 'operation already in progress' "$tmp/foreign-lock.err"
|
||||
test "$(cat "$tmp/locks/$lock_name/owner")" = foreign-owner
|
||||
rm -rf "$tmp/locks/$lock_name"
|
||||
|
||||
holder_ready="$tmp/holder-ready"
|
||||
holder_output="$tmp/holder.tar"
|
||||
holder_log="$tmp/holder.log"
|
||||
PATH="$fakebin:$PATH" DOCKER_LOG="$holder_log" PROJECT_NAME="$project" VOLUME_ROOT="$volume_root" \
|
||||
RUN_BLOCK_READY="$holder_ready" RUN_BLOCK_RELEASE="$holder_release" \
|
||||
HELPER_IMAGE="qdrant/qdrant:v1.18.2@sha256:75eab8c4ba42096724fdcfde8b4de0b5713d529dde32f285a1f86fdcb2c9e50c" \
|
||||
./scripts/vector-backup.sh --project-name "$project" --output "$holder_output" >/dev/null 2>"$tmp/holder.err" &
|
||||
holder_pid=$!
|
||||
attempts=0
|
||||
while [ ! -e "$holder_ready" ]; do
|
||||
attempts=$((attempts + 1))
|
||||
[ "$attempts" -lt 400 ] || { echo "lock holder did not reach copy phase" >&2; exit 1; }
|
||||
sleep 0.05
|
||||
done
|
||||
|
||||
for contender in backup restore; do
|
||||
contender_log="$tmp/contender-$contender.log"
|
||||
if [ "$contender" = backup ]; then
|
||||
set -- ./scripts/vector-backup.sh --project-name "$project" --output "$tmp/contender.tar"
|
||||
else
|
||||
set -- ./scripts/vector-restore.sh --project-name "$project" --input "$backup_output" --confirm-project "$project"
|
||||
fi
|
||||
if PATH="$fakebin:$PATH" DOCKER_LOG="$contender_log" PROJECT_NAME="$project" VOLUME_ROOT="$volume_root" \
|
||||
HELPER_IMAGE="qdrant/qdrant:v1.18.2@sha256:75eab8c4ba42096724fdcfde8b4de0b5713d529dde32f285a1f86fdcb2c9e50c" \
|
||||
"$@" >"$tmp/contender-$contender.out" 2>"$tmp/contender-$contender.err"; then
|
||||
echo "$contender interleaved with an active qdrant operation" >&2
|
||||
exit 1
|
||||
fi
|
||||
grep -q 'operation already in progress' "$tmp/contender-$contender.err"
|
||||
if grep -Eq '^volume (ls|inspect)|^compose .* (stop|start) qdrant|^run ' "$contender_log"; then
|
||||
echo "$contender touched qdrant state after lock contention" >&2
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
touch "$holder_release"
|
||||
wait "$holder_pid"
|
||||
holder_pid=
|
||||
test -s "$holder_output"
|
||||
test ! -e "$tmp/locks/$lock_name"
|
||||
|
||||
existing="$tmp/existing.tar"
|
||||
printf '%s' sentinel >"$existing"
|
||||
if PATH="$fakebin:$PATH" DOCKER_LOG="$tmp/existing.log" PROJECT_NAME="$project" VOLUME_ROOT="$volume_root" \
|
||||
@@ -392,5 +523,6 @@ fi
|
||||
test "$(cat "$volume_dir/collections/demo/state.json")" = rollback-source
|
||||
grep -q "compose --project-name $project stop qdrant" "$rollback_log"
|
||||
grep -q "compose --project-name $project start qdrant" "$rollback_log"
|
||||
test ! -e "$tmp/locks/$lock_name"
|
||||
|
||||
echo "qdrant backup/restore archive validation, scoped helper execution, and rollback safety passed."
|
||||
|
||||
Reference in New Issue
Block a user