respaldo: reintentar los cortes de red — el primero murió a los 2,3 G

La primera corrida real subió el cerebro (estado + repo, lo irreemplazable) y 2,3 G de
store, y entonces murió: `Connection reset by peer` / `Broken pipe`, rsync 255. Con ~19 h
de subida sobre un enlace de oficina, que la conexión se corte NO es un accidente: es lo
normal. Y un respaldo que hay que relanzar a mano cada vez que se cae no se completa nunca,
porque nadie está mirando.

Como rsync ya es incremental y `--partial-dir` conserva los trozos a medio subir,
reintentar es barato y seguro: cada intento retoma donde quedó, no reempieza. El bucle
insiste sólo ante fallos de RED (10/12/23/30/35/255) con espera creciente hasta 60 s; un
error de verdad —permisos, destino lleno, ruta inexistente— sale a la primera en vez de
repetirse 40 veces contra la misma pared.

DE PASO, UN ERROR MÍO QUE VALE REGISTRAR: comprobé el respaldo con `pgrep -f
respaldo-storagebox` y dijo que corría, cuando llevaba rato muerto — el pgrep se estaba
matcheando A SÍ MISMO, porque la cadena buscada está en la propia línea de comando del
shell que la ejecuta. Ya me había pasado hoy con el worker. La comprobación buena es mirar
lo que el proceso PRODUCE (bytes en el destino, última línea del log), no si hay un proceso
vivo. Es la misma lección que el guardián de store-gc: un `rm` que devuelve 0 no prueba que
el fichero se fue, y un proceso vivo no prueba que esté avanzando.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-08-07 13:30:58 -04:00
co-authored by Claude Opus 5
parent fe474dab63
commit 5e42d201b1
+34 -4
View File
@@ -66,6 +66,36 @@ SSH_CMD="ssh -4 -p $SB_PORT -i $KEY -o StrictHostKeyChecking=accept-new -o Serve
# Opciones comunes. `--partial-dir` es lo que hace el respaldo continuable a mitad de fichero.
RS="rsync -a --partial-dir=.rsync-partial --info=progress2 -z --compress-choice=zstd --compress-level=3"
# ── REINTENTOS: EL ENLACE SE CAE, Y ESO NO ES EXCEPCIONAL ───────────────────────────────────────
# Primera corrida real (2026-08-07): tras subir el cerebro y 2,3 G de store, murió con
# `Connection reset by peer` / `Broken pipe` y rsync salió con 255. Con una subida de ~19 h sobre un
# enlace de oficina, que la conexión se corte no es un accidente: es lo NORMAL. Un respaldo que hay
# que relanzar a mano cada vez que se cae no se completa nunca, porque nadie está mirando.
#
# Como rsync ya es incremental y `--partial-dir` conserva los trozos, reintentar es barato y seguro:
# cada intento retoma donde quedó. El bucle sólo insiste ante fallos de RED (códigos 10/12/23/30/35/
# 255); un error de verdad —permisos, disco lleno en destino, ruta inexistente— sale a la primera en
# vez de repetirse 40 veces contra la misma pared.
reintentar() {
etiqueta="$1"; shift
intento=1
while :; do
"$@" && return 0
rc=$?
case "$rc" in
10|12|23|30|35|255) ;; # red / pipe roto / timeout: insistir
*) echo "!! $etiqueta: error $rc que NO es de red — no reintento"; return "$rc" ;;
esac
if [ "$intento" -ge 40 ]; then
echo "!! $etiqueta: 40 intentos y sigue cayéndose. Dejo lo subido y paro."; return "$rc"
fi
espera=$(( intento < 6 ? intento * 10 : 60 ))
echo ".. $etiqueta: corte de red (rsync $rc). Intento $intento; reanudo en ${espera}s."
intento=$((intento + 1))
sleep "$espera"
done
}
if ! $SSH_CMD "$SB_USER@$SB_HOST" 'df -h .' >/dev/null 2>&1; then
echo "!! No hay SSH al Storage Box ($SB_HOST:$SB_PORT)."
echo "!! Si acabás de crearlo, el DNS tarda unos minutos en propagar. Reintentá."
@@ -77,10 +107,10 @@ $SSH_CMD "$SB_USER@$SB_HOST" 'mkdir hammer hammer/store hammer/repo hammer/estad
# ── 1. EL CEREBRO: estado + repo. Minutos, y es lo que no se puede perder. ──────────────────────
echo "==> [1/3] estado (el grafo: qué había construido y con qué hash)"
$RS $SECO -e "$SSH_CMD" "$RAIZ/docs/state/" "$SB_USER@$SB_HOST:hammer/estado/" || true
reintentar estado $RS $SECO -e "$SSH_CMD" "$RAIZ/docs/state/" "$SB_USER@$SB_HOST:hammer/estado/" || true
echo "==> [2/3] repo (recetas, scripts, SDDs — el cerebro; con esto solo se reconstruye el store)"
$RS --delete $SECO -e "$SSH_CMD" \
reintentar repo $RS --delete $SECO -e "$SSH_CMD" \
--exclude /store --exclude /store-rust --exclude /store-kern \
--exclude /work --exclude /target --exclude /.dev-fs --exclude /dist --exclude /.scratch \
"$RAIZ/" "$SB_USER@$SB_HOST:hammer/repo/"
@@ -92,10 +122,10 @@ echo "==> [3/3] store (128 G a 8 Mbps ⇒ NO cabe en una sentada; esto se corta
HUER="$(ls -t "$RAIZ"/work/store-gc-huerfanos-*.txt 2>/dev/null | head -1)"
if [ -n "$HUER" ] && [ -s "$HUER" ]; then
echo " · huérfanos primero ($(wc -l < "$HUER") artefactos, únicos ejemplares) — desde $HUER"
$RS $SECO -e "$SSH_CMD" --files-from="$HUER" "$RAIZ/store/" "$SB_USER@$SB_HOST:hammer/store/" || true
reintentar huerfanos $RS $SECO -e "$SSH_CMD" --files-from="$HUER" "$RAIZ/store/" "$SB_USER@$SB_HOST:hammer/store/" || true
fi
echo " · resto del store"
$RS $SECO -e "$SSH_CMD" "$RAIZ/store/" "$SB_USER@$SB_HOST:hammer/store/"
reintentar store $RS $SECO -e "$SSH_CMD" "$RAIZ/store/" "$SB_USER@$SB_HOST:hammer/store/"
echo "==> ocupación en el Storage Box:"
$SSH_CMD "$SB_USER@$SB_HOST" 'df -h .' 2>/dev/null | tail -2