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:
+48
-3
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user