Files
ethereum-rpc-docker/restore-volumes.sh
Claude Agent 3caa4ab873 restore: gate static-file offload on SLOWDISK env var (.env), not just /slowdisk presence
A `/slowdisk` directory exists on hosts even when it is just a folder on the root disk
(no dedicated extra disk) — offloading there gives no benefit. Source the host .env and
require SLOWDISK to be set (operator sets it only on hosts with a real extra disk mounted
at /slowdisk) before activating the static-file -> /slowdisk symlink offload. Unset =
normal extract everywhere. Target path stays /slowdisk (the fixed in-container mount).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-15 04:39:48 +00:00

119 lines
4.9 KiB
Bash
Executable File

#!/bin/bash
dir="$(dirname "$0")"
source "$dir/volume-utils.sh"
# Host config: SLOWDISK gates the static-file offload (see prep_static_offload). It must be
# set ONLY on hosts that have a real dedicated extra disk mounted at /slowdisk; on hosts
# where /slowdisk is just a folder on the root disk, offloading gives no benefit.
[ -f "$dir/.env" ] && source "$dir/.env"
remote_source="$2"
if [[ -n "$remote_source" ]] && is_local_backup_url "$remote_source"; then
echo "Source URL points to this server, using local /backup instead of $remote_source"
remote_source=""
fi
# Path to the backup directory
backup_dir="/backup"
# Path to the volume directory
volume_dir="/var/lib/docker/volumes"
if [ ! -d "$volume_dir" ]; then
echo "Error: /var/lib/docker/volumes directory does not exist"
exit 1
fi
# Pre-create static-file -> /slowdisk symlinks from a backup's ".txt" manifest, so the
# immutable "ancient"/freezer dirs land on the (SSD) /slowdisk during extraction while the
# hot/dynamic state stays on the primary disk. tar then extracts THROUGH the symlinks via
# --keep-directory-symlink (it keeps the dir-symlinks instead of clobbering them).
# Target naming matches delete-volumes.sh / delete_slowdisk_targets_for_key cleanup.
# GATED on the SLOWDISK env var from .env: only hosts with a real dedicated extra disk set
# it. Safe fallbacks (just extract normally): SLOWDISK unset, /slowdisk missing, no manifest,
# or no static paths. (/slowdisk is the fixed in-container mount, so the target path is fixed.)
prep_static_offload() {
local key=$1 meta=$2 data_dir=$3 rel target
[ -n "${SLOWDISK:-}" ] || { echo " SLOWDISK not set in .env (no dedicated extra disk) — no static offload"; return 0; }
[ -d /slowdisk ] || { echo " /slowdisk absent — no static offload"; return 0; }
[ -f "$meta" ] || { echo " no manifest ($meta) — no static offload"; return 0; }
# manifest data lines (after the 3-line header) are "<size> <relpath>"
while IFS= read -r rel; do
[ -z "$rel" ] && continue
rel="${rel#/}"
case "$rel" in *..*) echo " skip unsafe static path '$rel'"; continue;; esac
target="/slowdisk/rpc_${key}__data_${rel//\//_}"
echo " offload static '$rel' -> $target"
mkdir -p "$target" "$data_dir/$(dirname "$rel")" || { echo " WARN: mkdir failed for '$rel', skipping"; continue; }
ln -sfn "$target" "$data_dir/$rel"
done < <(awk 'NR>3 && NF>=2 {print $NF}' "$meta")
}
# Read the JSON input and extract the list of keys
keys=$(get_persistent_volume_keys "$dir/$1.yml" | grep -E '^[0-9a-z]')
echo "$keys"
while IFS= read -r key; do
[ -z "$key" ] && continue
data_dir="$volume_dir/rpc_$key/_data"
declare newest_file
if [[ -n "$remote_source" ]]; then
newest_file=$($dir/list-backups.sh "$remote_source" | grep "rpc_$key-20" | sort | tail -n 1)
else
newest_file=$(ls -1 "$backup_dir"/"rpc_$key"-[0-9]*G.tar.zst 2>/dev/null | sort | tail -n 1)
fi
if [ -z "$newest_file" ]; then
echo "Error: No backup found for volume 'rpc_$key'"
exit 1
fi
meta_file="${newest_file%.tar.zst}.txt"
echo "=== restoring rpc_$key <- $newest_file ==="
# 1) wipe live data AND any /slowdisk static targets for this key (no leak on re-restore)
delete_slowdisk_targets_for_key "$key"
[ -d "$data_dir" ] && rm -rf "$data_dir"/*
mkdir -p "$data_dir"
# 2) obtain the manifest (fetch the sidecar .txt for remote restores) and, unless this is
# a reth node (reth dropped whole-dir static-file symlinks), pre-create the offload.
local_meta="$meta_file"
if [[ -n "$remote_source" ]]; then
local_meta="$backup_dir/$(basename "$meta_file")"
[ -d "$backup_dir" ] || local_meta="/tmp/$(basename "$meta_file")"
if [ ! -f "$local_meta" ]; then
curl --ipv4 -fsS "${remote_source}${meta_file}" -o "$local_meta" 2>/dev/null || local_meta=""
fi
fi
if [[ "$1" == *reth* ]]; then
echo " reth node: static-file symlink offload disabled (reth broke whole-dir symlinks)"
elif [ -n "$local_meta" ]; then
prep_static_offload "$key" "$local_meta" "$data_dir"
fi
# 3) extract THROUGH the pre-created symlinks (keep them, don't clobber)
if [[ -n "$remote_source" ]]; then
if [ ! -d "$backup_dir" ]; then
echo "No /backup cache: streaming + extracting $newest_file directly"
curl --ipv4 -# "${remote_source}${newest_file}" | zstd -d | tar -xf - --keep-directory-symlink -C /
if [ $? -ne 0 ]; then
echo "Error processing $newest_file"
exit 1
fi
else
if [ ! -e "$backup_dir/$(basename "$newest_file")" ]; then
aria2c -c -Z -x8 -j8 -s8 -d "$backup_dir" "${remote_source}${newest_file}"
fi
tar -I zstd -xf "$backup_dir/$(basename "$newest_file")" --keep-directory-symlink -C /
fi
else
tar -I zstd -xf "$newest_file" --keep-directory-symlink -C /
fi
echo "Backup '$newest_file' restored"
done <<< "$keys"
"$dir/delete-node-keys.sh" "$1"
echo "node $1 restored."