fix: close qdrant archive restore race

This commit is contained in:
2026-08-08 19:43:54 +02:00
parent afaab5931f
commit af1e922a48
2 changed files with 136 additions and 23 deletions
+67 -21
View File
@@ -29,9 +29,29 @@ done
}
[ -r "$input" ] || { echo "backup input is not readable" >&2; exit 2; }
input_dir=$(dirname "$input")
input_name=$(basename "$input")
expected_volume_name="${project_name}_${volume_role}"
private_archive_dir=
private_archive_path=
cleanup() {
status=$?
if [ -n "${private_archive_dir:-}" ] && [ -d "${private_archive_dir:-}" ]; then
rm -rf "$private_archive_dir"
fi
if [ "${restart_qdrant:-0}" -eq 1 ]; then
docker compose --project-name "$project_name" start qdrant >/dev/null
fi
exit "$status"
}
trap cleanup EXIT HUP INT TERM
private_archive_dir=$(mktemp -d "${TMPDIR:-/tmp}/qdrant-archive.XXXXXX")
private_archive_path="$private_archive_dir/archive.tar"
umask 077
cp "$input" "$private_archive_path"
chmod 0600 "$private_archive_path"
input_dir=$private_archive_dir
input_name=$(basename "$private_archive_path")
resolve_volume() {
names=$(docker volume ls \
@@ -60,7 +80,7 @@ validate_volume_metadata() {
}
validate_archive_paths() {
paths=$(tar -tf "$input") || {
paths=$(tar -tf "$private_archive_path") || {
echo "archive listing failed" >&2
exit 2
}
@@ -102,7 +122,7 @@ EOF
}
validate_archive_types() {
tar -tvf "$input" | while IFS= read -r entry; do
tar -tvf "$private_archive_path" | while IFS= read -r entry; do
[ -n "$entry" ] || continue
type=$(printf '%.1s' "$entry")
case "$type" in
@@ -124,7 +144,7 @@ validate_archive_types() {
}
validate_archive_manifest() {
manifest=$(tar -xOf "$input" manifest.env 2>/dev/null) || {
manifest=$(tar -xOf "$private_archive_path" manifest.env 2>/dev/null) || {
echo "archive manifest could not be read" >&2
exit 2
}
@@ -134,25 +154,59 @@ validate_archive_manifest() {
manifest_role=
manifest_helper=
created_utc=
seen=
format_count=0
project_count=0
volume_count=0
role_count=0
helper_count=0
created_count=0
while IFS='=' read -r key value; do
[ -n "$key" ] || continue
case "$key" in
format) format=$value ;;
project_name) manifest_project=$value ;;
volume_name) manifest_volume=$value ;;
volume_role) manifest_role=$value ;;
helper_image) manifest_helper=$value ;;
created_utc) created_utc=$value ;;
format)
format_count=$((format_count + 1))
[ "$format_count" -eq 1 ] || { echo "archive manifest contains duplicate keys" >&2; exit 2; }
format=$value
;;
project_name)
project_count=$((project_count + 1))
[ "$project_count" -eq 1 ] || { echo "archive manifest contains duplicate keys" >&2; exit 2; }
manifest_project=$value
;;
volume_name)
volume_count=$((volume_count + 1))
[ "$volume_count" -eq 1 ] || { echo "archive manifest contains duplicate keys" >&2; exit 2; }
manifest_volume=$value
;;
volume_role)
role_count=$((role_count + 1))
[ "$role_count" -eq 1 ] || { echo "archive manifest contains duplicate keys" >&2; exit 2; }
manifest_role=$value
;;
helper_image)
helper_count=$((helper_count + 1))
[ "$helper_count" -eq 1 ] || { echo "archive manifest contains duplicate keys" >&2; exit 2; }
manifest_helper=$value
;;
created_utc)
created_count=$((created_count + 1))
[ "$created_count" -eq 1 ] || { echo "archive manifest contains duplicate keys" >&2; exit 2; }
created_utc=$value
;;
*)
echo "archive manifest contains unexpected fields" >&2
exit 2
;;
esac
seen="$seen $key"
done <<EOF
$manifest
EOF
[ "$format_count" -eq 1 ] || { echo "archive manifest is incomplete" >&2; exit 2; }
[ "$project_count" -eq 1 ] || { echo "archive manifest is incomplete" >&2; exit 2; }
[ "$volume_count" -eq 1 ] || { echo "archive manifest is incomplete" >&2; exit 2; }
[ "$role_count" -eq 1 ] || { echo "archive manifest is incomplete" >&2; exit 2; }
[ "$helper_count" -eq 1 ] || { echo "archive manifest is incomplete" >&2; exit 2; }
[ "$created_count" -eq 1 ] || { echo "archive manifest is incomplete" >&2; exit 2; }
[ "$format" = "$archive_format" ] || { echo "archive format is unsupported" >&2; exit 2; }
[ "$manifest_project" = "$project_name" ] || { echo "archive project does not match restore target" >&2; exit 2; }
[ "$manifest_volume" = "$expected_volume_name" ] || { echo "archive volume does not match restore target" >&2; exit 2; }
@@ -170,14 +224,6 @@ validate_volume_metadata "$volume_name"
running_container=$(docker compose --project-name "$project_name" ps --status running -q qdrant)
restart_qdrant=0
cleanup() {
status=$?
if [ "$restart_qdrant" -eq 1 ]; then
docker compose --project-name "$project_name" start qdrant >/dev/null
fi
exit "$status"
}
trap cleanup EXIT HUP INT TERM
if [ -n "$running_container" ]; then
docker compose --project-name "$project_name" stop qdrant >/dev/null