From 6558b7c8dea60df7b7d704dca8a0675adf4b8359 Mon Sep 17 00:00:00 2001 From: Sergio Date: Tue, 1 Sep 2026 15:53:35 +0000 Subject: [PATCH] =?UTF-8?q?granja:=20la=20cosecha=20llenaba=20el=20disco?= =?UTF-8?q?=20=E2=80=94=20rsync=20sin=20-H=20y=20bajando=20.dmerge?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit La bajada del store murio con "No space left on device" con 95 G libres y un store remoto de 25 G. Dos causas que se multiplican: - `rsync -a` NO preserva hardlinks (eso es `-H`). El store es CAS y .dmerge hardlinkea los artefactos, asi que los 25 G que mide `du` en el origen -que deduplica por inodo- se escriben en destino como copias enteras, una por enlace. - `.dmerge` es cache pura: hammer la reconstruye sola, no aporta un artefacto y es la parte mas pesada del arbol (132 G en el hub, 111,7 GiB exclusivos). Ademas el fallo a mitad deja directorios creados sin ficheros, que es un cache-hit envenenado: `mise` se cosecho asi, como nombre vacio. Se anade un guardian que barre los vacios al terminar la bajada y avisa de re-correr. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01LykB7vw38b4Ck1Zij15RQ4 --- scripts/farm/farm-sync.sh | 35 +++++++++++++++++++++++++++++++++-- 1 file changed, 33 insertions(+), 2 deletions(-) diff --git a/scripts/farm/farm-sync.sh b/scripts/farm/farm-sync.sh index c88ae4a0..a6ff3f1d 100755 --- a/scripts/farm/farm-sync.sh +++ b/scripts/farm/farm-sync.sh @@ -75,13 +75,44 @@ echo "==> 2. bajando artefactos sellados del worker" # son inmutables y un artefacto superado no vuelve a ser vigente salvo que una receta retroceda— # excluirlos es seguro; y si una receta retrocediera, su artefacto se reconstruye, que es barato. LEDGER="work/store-gc-superados.txt" +# ── ⚠ `-H` Y EXCLUIR `.dmerge`: SIN ESTO LA COSECHA LLENA EL DISCO ───────────────────────────── +# Medido el 2026-09-01: esta bajada murió con `No space left on device (28)` escribiendo +# `store/.dmerge/b8bc5ecc…/usr/share/locale/az/LC_MESSAGES/kio6.mo`, con 95 G libres y un store +# remoto de 25 G. Dos causas que se multiplican: +# +# 1. **`rsync -a` NO preserva hardlinks** (eso es `-H`, y no está incluido en `-a`). El store es +# CAS y `.dmerge` hardlinkea los artefactos: en el worker, 25 G medidos con `du` —que deduplica +# por inodo— son MUCHOS más en el cable, porque cada enlace se transfiere y se escribe como una +# copia entera. El tamaño que informa `du` del origen NO es el que hace falta en el destino. +# 2. **`.dmerge` es CACHÉ PURA y no tiene por qué viajar.** Es la capa de merge que hammer +# reconstruye sola; copiarla no aporta un solo artefacto y es la parte más pesada del árbol +# (en el hub llegó a 132 G con 111,7 GiB exclusivos). Cosechar es traerse ARTEFACTOS. +# +# El fallo además es a mitad de camino: rsync limpia sus temporales al abortar, pero los directorios +# que ya creó quedan — y un directorio de artefacto sin ficheros es EXACTAMENTE un cache-hit +# envenenado (`mise` llegó así en esa pasada). Ver la regla 3 del CLAUDE.md. +RSOPTS=(-azH --exclude '/.dmerge') if [ -s "$LEDGER" ]; then echo " (excluyendo $(wc -l < "$LEDGER") artefactos ya podados — ver la nota del bucle de churn)" - rsync -az -e "$SSH" --exclude-from="$LEDGER" "$VPS:$REMOTE/store/" ./store/ + rsync "${RSOPTS[@]}" -e "$SSH" --exclude-from="$LEDGER" "$VPS:$REMOTE/store/" ./store/ else - rsync -az -e "$SSH" "$VPS:$REMOTE/store/" ./store/ + rsync "${RSOPTS[@]}" -e "$SSH" "$VPS:$REMOTE/store/" ./store/ fi +# ── GUARDIÁN: ningún artefacto cosechado puede llegar VACÍO ──────────────────────────────────── +# Un directorio sin ficheros no es un artefacto, es un nombre — y `hammer build` lo lee como +# presencia y sella sin construir. Barrerlos acá es barato; detectarlos tres eslabones más abajo +# (manifiesto → grafo → build) cuesta una campaña. +VACIOS=0 +for d in ./store/*/; do + case "$d" in ./store/.dmerge/) continue ;; esac + [ -d "$d" ] || continue + if [ -z "$(find "$d" -mindepth 1 -print -quit 2>/dev/null)" ]; then + echo " ⚠ artefacto VACÍO cosechado, lo quito: $d"; rmdir "$d" 2>/dev/null; VACIOS=$((VACIOS+1)) + fi +done +[ "$VACIOS" -gt 0 ] && echo " ($VACIOS vacíos barridos — la bajada quedó a medias, re-corré el sync)" + if [ "${2:-}" = "--no-promote" ]; then echo "✓ store sincronizado (sin promote)"; exit 0 fi