granja: el aviso de "sin volumen" ahora CORTA en vez de sembrar igual

La noche del 2026-08-22 la campana corrio sharded en dos workers. Un volumen de
Hetzner se adjunta a UN server, y este script crea N workers en un bucle
apuntando todos al MISMO VOL_NAME: hworker-1 tomo harkaq-cosecha y para
hworker-2 no quedaba nada que adjuntar. Construyo el shard 1/2 entero en su
disco raiz y el dead-man se lo llevo a las 03:27Z. 21 G, 397 artefactos
sellados; 584 de sus 675 no existen en ningun otro sitio (work/perdidos-hworker2.txt).

Lo caro es que la deteccion YA ESTABA y era correcta:

    echo "   ⚠ el store NO quedó en el volumen ⇒ lo que construya se PIERDE..."

Ese aviso se imprimio, con esas palabras, y el script siguio adelante: armo el
dead-man y sembro la cola igual. Un aviso que no detiene el pipeline no es un
guardian cuando no hay nadie leyendo el log. Es la regla 3 del CLAUDE.md con
otra cara: el ausente (el volumen) llego hasta el final diciendo que todo fue
bien.

Tres cambios, todos sobre el mismo fallo:

  - `hcloud volume attach` dejaba de tragarse el error. Estaba escondido dos
    veces: `>/dev/null 2>&1` el mensaje y `|| true` el codigo de salida.
  - Se pregunta ANTES quien tiene el volumen tomado, y el motivo lo nombra.
  - Los dos ⚠ finales pasan a ser `sin_volumen`, que NO siembra: borra el server
    recien nacido (vacio, para que no quede idle facturando como hworker-4) y
    sale 1. Mismo blindaje que el dead-man y farm-down: solo borra con label
    role=hammer-worker, verificado que gioser no matchea.

Escape explicito para el caso deliberado: SIN_VOLUMEN_OK=1.

Verificado en negativo, que es como se comprueba lo que un guardian IMPIDE: la
llamada corta con exit 1 sin alcanzar la linea siguiente, y con un server sin
label no borra nada.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Xd5DComN9Nu47TBpYMBHBn
This commit is contained in:
Sergio
2026-08-23 17:13:09 +00:00
co-authored by Claude Opus 5
parent 86c3cc787e
commit 5cfebf0087
+48 -3
View File
@@ -59,6 +59,41 @@ SSH="ssh -i $SSH_KEY -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no
cd "$ROOT"
touch "$FLEET"
# ── GUARDIÁN DEL VOLUMEN (2026-08-23) ─────────────────────────────────────────────────────────
# El chequeo de más abajo YA detectaba este fallo — y sólo imprimía un ⚠ mientras el script seguía
# adelante: armaba el dead-man y sembraba la cola igual. La noche del 2026-08-22 eso costó 21 G y
# 397 artefactos sellados. hworker-2 nació sin volumen (lo tenía hworker-1: un volumen de Hetzner se
# adjunta a UN server), construyó el shard 1/2 entero en su disco raíz y el dead-man se lo llevó a
# las 03:27Z. El aviso salió impreso, con estas mismas palabras, y no lo leyó nadie: el hub estaba
# esperando una respuesta humana que llegó después del borrado.
#
# La lección no es "avisar mejor": es que un aviso que no detiene el pipeline no es un guardián
# cuando nadie mira. Sembrar trabajo en un disco efímero es peor que no crear el worker, porque
# gasta horas de CPU y las tira. Ahora CORTA.
sin_volumen() {
local name="$1" motivo="$2"
echo "$name: $motivo" >&2
echo " Sin volumen, lo que selle vive en disco EFÍMERO y el dead-man se lo lleva entero." >&2
echo " NO lo siembro. Salidas:" >&2
echo " · un volumen POR worker: VOL_NAME=harkaq-cosecha-$name scripts/farm/farm-up.sh 1" >&2
echo " · cosechar y clausurar el actual: scripts/farm/harkaq-vol.sh collect (luego close)" >&2
echo " · worker deliberadamente desechable: SIN_VOLUMEN_OK=1 scripts/farm/farm-up.sh …" >&2
if [ "${SIN_VOLUMEN_OK:-0}" = "1" ]; then
echo " SIN_VOLUMEN_OK=1 ⇒ sigo igual: este worker construye a disco efímero." >&2
return 0
fi
# El server acaba de nacer y todavía no tiene nada dentro. Dejarlo vivo sería un idle facturando
# (es el caso hworker-4, 5 días), así que se borra — con el MISMO blindaje que el dead-man y
# farm-down: sólo si lleva el label role=hammer-worker. gioser no lo lleva ⇒ jamás cae por acá.
if hcloud server describe "$name" -o format='{{.Labels}}' 2>/dev/null | grep -q hammer-worker; then
if hcloud server delete "$name" >/dev/null 2>&1; then
echo " $name borrado (recién creado, vacío): sin factura colgando." >&2
sed -i "/^$name /d" "$FLEET" 2>/dev/null || true
fi
fi
exit 1
}
# índice de arranque: el mayor hworker-N vivo + 1 (para escalar sin colisión de nombres)
base=$(sed -n 's/^hworker-\([0-9]*\) .*/\1/p' "$FLEET" | sort -n | tail -1)
base="${base:-0}"
@@ -120,7 +155,17 @@ for k in $(seq 1 "$N"); do
vid=$(hcloud volume list -o noheader -o columns=id,name 2>/dev/null | awk -v v="$VOL_NAME" '$2==v{print $1}')
fi
if [ -n "$vid" ]; then
hcloud volume attach --server "$name" "$VOL_NAME" >/dev/null 2>&1 || true
# UN VOLUMEN SE ADJUNTA A UN SERVER, y este script crea N workers en un bucle apuntando todos al
# MISMO VOL_NAME ⇒ del segundo en adelante el attach no tiene nada que adjuntar. Antes ese fallo
# se perdía dos veces: `>/dev/null 2>&1` escondía el mensaje y `|| true` el código de salida.
# Se pregunta ANTES, y el motivo se dice con el nombre del server que lo tiene tomado.
dueno=$(hcloud volume list -o noheader -o columns=name,server 2>/dev/null | awk -v v="$VOL_NAME" '$1==v{print $2}')
if [ -n "$dueno" ] && [ "$dueno" != "-" ] && [ "$dueno" != "$name" ]; then
sin_volumen "$name" "el volumen $VOL_NAME ya está adjunto a $dueno (un volumen = un server)"
fi
if ! err=$(hcloud volume attach --server "$name" "$VOL_NAME" 2>&1); then
sin_volumen "$name" "no pude adjuntar $VOL_NAME: ${err:-error desconocido}"
fi
dev="/dev/disk/by-id/scsi-0HC_Volume_$vid"
$SSH root@"$ip" "mkdir -p /mnt/cosecha
# ⚠ El mkfs.ext4 -F de rescate SÓLO si el dispositivo no tiene filesystem. La versión previa
@@ -175,10 +220,10 @@ for k in $(seq 1 "$N"); do
if $SSH root@"$ip" 'mountpoint -q /mnt/cosecha && mountpoint -q '"$REMOTE"'/store' 2>/dev/null; then
echo " ✓ store anclado al volumen ($( $SSH root@"$ip" 'ls -d /mnt/cosecha/store/*/ 2>/dev/null | wc -l') artefactos ya presentes)"
else
echo " ⚠ el store NO quedó en el volumen ⇒ lo que construya se PIERDE al auto-borrarse. Revisar."
sin_volumen "$name" "el store NO quedó anclado al volumen (mountpoint dice que no)"
fi
else
echo " ⚠ sin volumen ⇒ este worker construiría a disco local y la automuerte perdería todo."
sin_volumen "$name" "no existe el volumen $VOL_NAME y no pude crearlo"
fi
# DEAD-MAN SWITCH: el worker se borra SOLO tras 1h sin trabajo. Va acá, en el nacimiento, porque