farm: lock compartido entre campana-deuda y el worker-loop
Los dos construyen sobre el MISMO work/, y `fetch` nombra el árbol de fuentes de forma determinista
(`work/sources/<receta>-<sha16>`, sin nada que dependa de QUIÉN construye) ⇒ dos procesos que
necesiten la misma DEP apuntan al mismo directorio: uno hace `remove_dir_all` mientras el otro
corre `tar -x`. El árbol queda a medias y devuelve "Directory not empty" (os error 39), y así se
queda hasta que alguien lo borra a mano.
No es teórico: la campaña de deuda y el loop pidieron `mesa` a la vez (el loop lo arrastraba desde
la cola KDE al invalidarse libdrm) y se llevó puesta media cascada GUI, con un error que no nombra
la causa. Casi borro ese árbol a mano antes de ver que había un bwrap montándolo.
Grano: la campaña toma el lock para toda su corrida; el loop, por ciclo de cola. Más fino rompería
el `xargs -P2` de build-farm.sh.
Dos honestidades en los comentarios, para no prometer de más:
- `flock` NO es FIFO. El loop vuelve a pedirlo enseguida y le gana a la campaña que espera:
medido, la campaña entra al terminar TODAS las colas, no entre dos. La ventana real es el
IDLE_SLEEP. Por eso espera con techo (LOCK_WAIT=7200) y sale limpia en vez de colgarse.
- Esto NO cierra la carrera del todo: el `-P2` interno del loop puede correr dos recetas de la
misma cola que compartan dep, y ésas se siguen pisando. El arreglo de fondo es un lock POR
ÁRBOL dentro de `fetch`, que cubriría los dos casos.
Verificado con flock real: exclusión mutua (la campaña entra justo cuando el loop suelta), salida
limpia por timeout con rc=0, y la no-equidad de flock medida en vez de supuesta.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -41,6 +41,34 @@ DEUDA="${DEUDA:-$*}"
|
||||
[ -n "$DEUDA" ] || { echo "uso: campana-deuda.sh <receta> [receta…] (o DEUDA=…)" >&2; exit 2; }
|
||||
|
||||
ts() { date -u +%FT%TZ; }
|
||||
|
||||
# LOCK COMPARTIDO CON EL WORKER-LOOP (2026-07-22). Esta campaña y `farm-worker-loop.sh` construyen
|
||||
# sobre el MISMO work/, y `fetch` nombra el árbol de fuentes de forma determinista
|
||||
# (`work/sources/<receta>-<sha16>`) ⇒ dos procesos que construyan la misma DEP apuntan al mismo
|
||||
# directorio, y uno hace `remove_dir_all` mientras el otro corre `tar -x`. Eso deja el árbol a medias
|
||||
# y devuelve "Directory not empty" (os error 39) — y el árbol roto se queda así hasta que alguien lo
|
||||
# borra a mano. Pasó de verdad: la campaña y el loop pidieron `mesa` a la vez (el loop lo arrastraba
|
||||
# desde la cola KDE) y se llevó puesta media cascada GUI.
|
||||
#
|
||||
# El lock es EXCLUSIVO y de grano grueso: la campaña lo toma para TODA su corrida y el loop lo toma
|
||||
# por cada ciclo de cola. No se puede afinar más sin romper el `xargs -P2` del loop.
|
||||
# `flock` NO es FIFO: como el loop vuelve a pedirlo enseguida, en la práctica la campaña entra en la
|
||||
# ventana del IDLE_SLEEP, no entre dos colas (medido, no supuesto). De ahí el techo de espera.
|
||||
#
|
||||
# OJO, lo que este lock NO cubre: el propio `-P2` del loop puede correr dos recetas que compartan
|
||||
# dep, y ésas se pisan igual. El arreglo de fondo es un lock POR ÁRBOL dentro de `fetch`.
|
||||
#
|
||||
# Espera bloqueante con techo: un ciclo de cola puede ser largo, pero colgarse para siempre sería
|
||||
# peor que no correr. Al vencer, sale limpio y lo dice; el próximo lanzamiento reintenta.
|
||||
LOCK_FARM="${LOCK_FARM:-$HAMMER_DIR/work/.farm-build.lock}"
|
||||
LOCK_WAIT="${LOCK_WAIT:-7200}"
|
||||
mkdir -p "$(dirname "$LOCK_FARM")"
|
||||
exec 9>"$LOCK_FARM"
|
||||
if ! flock -w "$LOCK_WAIT" 9; then
|
||||
echo "$(ts) campaña-deuda: el worker-loop no soltó el lock en ${LOCK_WAIT}s ⇒ salgo sin construir" | tee -a "$LOG"
|
||||
exit 0
|
||||
fi
|
||||
echo "$(ts) campaña-deuda: lock de build tomado (el worker-loop espera su turno)" | tee -a "$LOG"
|
||||
total=$(echo "$DEUDA" | wc -w); i=0; ok=0; fail=0; falladas=""
|
||||
echo "$(ts) campaña-deuda arranca: $total recetas | go=$(command -v go || echo AUSENTE)" | tee -a "$LOG"
|
||||
|
||||
|
||||
@@ -14,6 +14,9 @@ cd "$HAMMER_DIR"
|
||||
. "$HOME/.cargo/env" 2>/dev/null || true
|
||||
JOBS="${JOBS:-$(nproc)}"
|
||||
IDLE_SLEEP="${IDLE_SLEEP:-300}"
|
||||
# Fichero de lock compartido con campana-deuda.sh (mismo path en los dos, o no hay exclusión).
|
||||
LOCK_FARM="${LOCK_FARM:-$HAMMER_DIR/work/.farm-build.lock}"
|
||||
mkdir -p "$HAMMER_DIR/work"
|
||||
|
||||
# --- watchdog de disco: cada 3 min borra work/sources/* que ningún bwrap activo bind-monta
|
||||
# (seguro: sellada=store CAS, en-cola=se re-extrae sola). Evita que el vendoring llene el disco.
|
||||
@@ -95,7 +98,19 @@ while :; do
|
||||
[ "$n" -gt 0 ] || continue
|
||||
total=$((total + n))
|
||||
echo "$(date -u +%FT%TZ) ciclo: $n recetas en $Q"
|
||||
JOBS="$JOBS" PROMOTE=0 QUEUE="$Q" scripts/build-farm.sh 2>&1 | tail -50
|
||||
# LOCK COMPARTIDO CON campana-deuda.sh (2026-07-22): ambos construyen sobre el mismo work/, y
|
||||
# `fetch` nombra el árbol de fuentes de forma determinista (`work/sources/<receta>-<sha16>`) ⇒
|
||||
# dos procesos que construyan la misma DEP apuntan al mismo directorio y uno hace `remove_dir_all`
|
||||
# mientras el otro corre `tar -x`, dejándolo a medias con "Directory not empty" (os error 39) para
|
||||
# siempre. Se toma POR COLA y se suelta al terminarla, en vez de por bucle entero. Más fino no
|
||||
# se puede sin romper el `xargs -P2` de build-farm.sh.
|
||||
# OJO con la expectativa: `flock` NO es FIFO. Como el loop vuelve a pedirlo enseguida, suele
|
||||
# ganarle la carrera a la campaña que espera; medido, la campaña entra al terminar TODAS las
|
||||
# colas, no entre dos. La ventana real es el IDLE_SLEEP. Por eso la campaña espera con techo
|
||||
# (LOCK_WAIT) y sale limpia si no entra, en vez de colgarse.
|
||||
# NO cubre el `-P2` interno: dos recetas de la MISMA cola que compartan dep siguen pudiendo
|
||||
# pisarse. Ese es el arreglo de fondo, un lock por árbol dentro de `fetch`.
|
||||
( flock 9; JOBS="$JOBS" PROMOTE=0 QUEUE="$Q" scripts/build-farm.sh 2>&1 | tail -50 ) 9>"$LOCK_FARM"
|
||||
done
|
||||
if [ "$total" -gt 0 ]; then
|
||||
echo "$(date -u +%FT%TZ) ciclo terminado; store sellado disponible para el hub"
|
||||
|
||||
Reference in New Issue
Block a user