🐛 la granja molía UNA receta por cola y por ciclo — el xargs -I{} recibía la cola entera junta

Medido hoy al encolar `lld21` junto a `rust`: el ciclo decía «2 recetas en recipes/incoming» y
construía UNA. La causa está en el filtro de «ya sellado en el hub»: acumula en `filtrada` uniendo
con ESPACIOS, y más abajo la cola se le pasa a `xargs -I{}`, que trata **una línea = un elemento**.
Unidas por espacios, N recetas llegan como UN SOLO argumento:

    printf '%s\n' " a.toml b.toml c.toml" | xargs -I{} sh -c 'echo [$1]' _ {}
      recibe: [a.toml b.toml c.toml]          ← uno solo

⇒ la fase paralela fallaba SIEMPRE con «No such file», y la fase 1b —el reintento serial— salvaba
exactamente UNA receta: la última, porque `basename` de esa ristra da el último nombre. El log
parecía normal, con su ✗ seguido de un ✓ (reintento serial), y ese patrón está en TODAS las colas
(yambar, spidermonkey, rust…). O sea que el reintento serial, que existe para conciliar colisiones
de concurrencia, venía tapando el bug desde que se escribió el filtro.

El arreglo son dos líneas: acumular con salto de línea y descartar la línea vacía con la que arranca
el acumulador (una línea vacía también es un elemento para `xargs -I{}`). Probado en chiquito, en
los dos sentidos: antes 1 elemento, después 3.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Sergio
2026-09-16 04:00:08 +00:00
co-authored by Claude Opus 5
parent 3f3bb2ed16
commit 9f325a3c05
+14 -2
View File
@@ -68,14 +68,26 @@ if [ -s "$SELLADOS" ]; then
[ -n "$n" ] || n=$(basename "$f" .toml)
H=$("$HAMMER" --store "$STORE" hash "$f" 2>/dev/null | tail -1 | sed 's/^b3://')
if [ -n "$H" ] && grep -qx "$H-$n" "$SELLADOS"; then saltadas=$((saltadas+1)); continue; fi
filtrada="$filtrada $f"
# ⚠ SALTO DE LÍNEA Y NO ESPACIO, y esto era un BUG que costó throughput en silencio
# (medido 2026-09-16): más abajo la cola se le pasa a `xargs -I{}`, que trata **una línea = un
# elemento**. Unidas por espacios, N recetas llegaban como UN SOLO argumento: la fase paralela
# fallaba siempre con «No such file» y la fase 1b —el reintento serial— salvaba EXACTAMENTE UNA,
# la última, porque `basename` de esa ristra da el último nombre. O sea que la granja molía
# **una receta por cola y por ciclo** en vez de la cola entera, y el log parecía normal: un ✗
# seguido de un ✓ (reintento serial). Se vio al encolar `lld21` junto a `rust` y descubrir que
# el ciclo decía «2 recetas» y construía una.
filtrada="$filtrada
$f"
done
[ "$cerradas" -gt 0 ] && echo " ($cerradas CERRADAS por decisión — ver la cabecera de cada receta)"
[ "$saltadas" -gt 0 ] && echo " (saltadas $saltadas ya selladas en el hub — ver la nota del sync unidireccional)"
queue="$filtrada"
[ -n "$(echo $queue)" ] || { echo "nada que hacer en $QUEUE: todo sellado en el hub"; exit 0; }
fi
n_total=$(printf '%s\n' "$queue" | wc -l | tr -d ' ')
# `grep -v` porque el acumulador del filtro arranca con una línea vacía, y una línea vacía es un
# elemento más para `xargs -I{}`: construiría la receta "" y ensuciaría el conteo.
queue=$(printf '%s\n' "$queue" | grep -v '^[[:space:]]*$' || true)
n_total=$(printf '%s\n' "$queue" | grep -c . || true)
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