Etapa G (granja): concilia la colisión de concurrencia — reintento serial de fallos por dep compartida

build-farm.sh asumía 'sin colisión entre recetas distintas', falso para deps transitivas
compartidas no selladas: dos consumidores (p.ej. libadwaita/gtksourceview) construyen gtk4 a
la vez en el mismo sources/gtk4-<sha>/output ⇒ 'Some other Meson process is already using this
build directory'. Fase 1b: tras el pool paralelo, reintenta EN SERIE los fallos con firma de
colisión — la dep ya la selló el ganador ⇒ cache-hit y compila. Un pase basta (cada build
arrastra sus deps). Sólo reintenta colisiones, no MSRV/dep-faltante reales.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-02 11:25:19 -04:00
co-authored by Claude Opus 4.8
parent a3732fc964
commit 823c7e1171
+23 -2
View File
@@ -36,8 +36,10 @@ n_total=$(printf '%s\n' "$queue" | wc -l | tr -d ' ')
echo "==> GRANJA: $n_total recetas en $QUEUE, $JOBS workers en paralelo"
# ── Fase 1: BUILD en paralelo. Cada worker construye una receta y deja su status (.ok con hash | .fail
# con motivo). El store sella atómico y cada build usa sources/<name>-<sha> + out/<hash>-<name> propios
# ⇒ sin colisión entre recetas distintas. `xargs -P` es el pool.
# con motivo). El store sella atómico y cada RECETA usa sources/<name>-<sha> + out/<hash>-<name> propios.
# CAVEAT: dos recetas DISTINTAS que comparten una dep transitiva NO sellada (p.ej. gtk4 bajo libadwaita
# y gtksourceview) la construyen en el MISMO sources/<dep>-<sha>/output ⇒ chocan (meson/tar). Eso lo
# concilia la Fase 1b (reintento serial). `xargs -P` es el pool.
printf '%s\n' "$queue" | xargs -P"$JOBS" -I '{}' sh -c '
f="$1"; n=$(basename "$f" .toml); log="'"$FARM"'/log/$n.log"
H=$("'"$HAMMER"'" --store "'"$STORE"'" build "$f" 2>"$log" | tail -1 || true)
@@ -49,6 +51,25 @@ printf '%s\n' "$queue" | xargs -P"$JOBS" -I '{}' sh -c '
fi
' _ '{}'
# ── Fase 1b: REINTENTO SERIAL de fallos por COLISIÓN de concurrencia. El pool paralelo puede hacer que
# dos recetas construyan la MISMA dep transitiva compartida (gtk4/cairo/pango bajo varios consumidores)
# a la vez en sources/<dep>-<sha>/output ⇒ meson/tar chocan ("Some other Meson process is already using
# this build directory", "Directory not empty", "No such file"). Tras la Fase 1 la dep ya la selló el
# ganador de la carrera ⇒ reconstruir el fallo EN SERIE ahora cache-hitea la dep y compila sin colisión.
# Un solo pase basta: cada `hammer build` arrastra sus deps, así que aunque el orden no sea topológico,
# la dep se construye/sella dentro del propio reintento. Sólo se reintenta lo que tiene FIRMA de colisión
# (no MSRV/dep-faltante reales, que re-fallarían y sólo gastarían tiempo).
for s in "$FARM"/status/*.fail; do
[ -f "$s" ] || continue
n=$(basename "$s" .fail); log="$FARM/log/$n.log"; f="$QUEUE/$n.toml"
[ -f "$f" ] || continue
grep -qiE "Some other Meson process|already using this build directory|Directory not empty|No such file or directory|gdkconfig\.h" "$log" 2>/dev/null || continue
H=$("$HAMMER" --store "$STORE" build "$f" 2>"$log" | tail -1 || true)
if [ "${H#b3:}" != "$H" ] && [ -n "$H" ]; then
rm -f "$s"; echo "$H" > "$FARM/status/$n.ok"; printf " ✓ (reintento serial) %-18s %s\n" "$n" "$H"
fi
done
# ── Fase 2: PROMOTE + PUBLISH en serie (índice del repo = recurso mutable compartido).
ok=$(ls "$FARM"/status/*.ok 2>/dev/null | wc -l | tr -d ' ')
fail=$(ls "$FARM"/status/*.fail 2>/dev/null | wc -l | tr -d ' ')