librelad 71a02374d1 perf(backup): raw-data stats, dedupe repo stats, SSH connection reuse
Follow-up to the backup-refresh throttle/dedupe, cutting the cost of the
remote pulls that do still happen.

* restic stats now runs in --mode raw-data (restic_check.sh). The default
  restore-size mode walks every snapshot's tree to sum logical file sizes —
  the slowest restic op — just to fill a size readout. raw-data reads the
  index only and reports the repository's actual deduplicated on-disk size,
  which is exactly what the dashboard already labels "deduplicated,
  encrypted". raw-data omits total_file_count, so the per-location card now
  shows that location's snapshot count (already loaded client-side, and more
  useful for a backup repo) instead of a file count.

* engineLocationStats now shares the same per-refresh memoiser as
  engineSnapshotsJson (engine_dispatch.sh). Both the locations and dashboard
  generators call it per location, so repo stats went from two restic calls
  per location per refresh to one. Factored the cache into _engineCachedPull.

* SSH connection reuse for SFTP locations (backup_ssh.sh): ControlMaster=auto
  with a self-reaping ControlPersist master, so the several restic
  subprocesses a refresh/backup spawns against one location share a single
  authenticated connection instead of a fresh handshake each — the dominant
  per-call cost on a high-latency link. Toggle via CFG_BACKUP_SSH_MULTIPLEX.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Signed-off-by: librelad <librelad@digitalangels.vip>
2026-07-17 21:56:55 +01:00

70 lines
2.8 KiB
Bash

#!/bin/bash
# Build the SSH command engines use to reach an SFTP location. Honours
# CFG_BACKUP_LOC_N_SSH_AUTH:
# - "key" (default): plain `ssh ... -s sftp` / `ssh ...`
# - "password": exports SSHPASS=<pass> and prefixes with `sshpass -e`
#
# Args:
# idx — location index
# port — SSH port
# mode — "sftp" (append `-s sftp`) or "raw" (just the ssh prefix, no -s)
#
# Echoes the command on stdout. Returns non-zero with an isError if password
# mode is requested without sshpass on PATH.
backupSshCommand()
{
local idx="$1"
local port="${2:-22}"
local mode="${3:-raw}"
local auth pass
auth=$(resticLocationField "$idx" SSH_AUTH)
pass=$(resticLocationField "$idx" SSH_PASS)
[[ -z "$auth" ]] && auth=key
# Connection multiplexing: a single WebUI refresh (repo stats + snapshot
# list) and each backup push run several restic subprocesses against the
# same location; ControlMaster lets them share one authenticated SSH
# connection instead of a fresh handshake each — the dominant per-call cost
# on a high-latency link. auto = reuse an existing master or open one; the
# master self-reaps ControlPersist seconds after its last channel closes.
# %C keeps the socket path short and unique per (host,port,user). Opt out
# with CFG_BACKUP_SSH_MULTIPLEX=false.
local mux=""
if [[ "${CFG_BACKUP_SSH_MULTIPLEX:-true}" != "false" ]]; then
local mux_dir="${TMPDIR:-/tmp}"; mux_dir="${mux_dir%/}"
mux=" -o ControlMaster=auto -o ControlPath=${mux_dir}/lp-bkmux-%C -o ControlPersist=30"
fi
local base="ssh -p $port -o StrictHostKeyChecking=accept-new${mux}"
[[ "$mode" == "sftp" ]] && local suffix=" -s sftp" || local suffix=""
if [[ "$auth" == "password" ]]; then
if [[ -z "$pass" ]]; then
isError "Location $idx is set to password auth but CFG_BACKUP_LOC_${idx}_SSH_PASS is empty"
return 1
fi
if ! command -v sshpass >/dev/null 2>&1; then
isError "sshpass not installed but location $idx uses password auth — apt install sshpass"
return 1
fi
export SSHPASS="$pass"
echo "sshpass -e $base -o PreferredAuthentications=password -o PubkeyAuthentication=no${suffix}"
else
# Key mode: when LibrePortal has a per-location key, pin -i and force
# identities-only so the right key is used; otherwise fall back to
# whatever the docker_install_user has configured.
local key_file=""
if declare -f backupSshKeyFile >/dev/null 2>&1; then
key_file=$(backupSshKeyFile "$idx")
fi
if [[ -n "$key_file" && -f "$key_file" ]]; then
echo "$base -i $key_file -o IdentitiesOnly=yes${suffix}"
else
echo "$base${suffix}"
fi
fi
}