fix(granja): dos recetas bloqueadas por el límite de DESCRIPTORES, no por un bug suyo
`obs-studio` y `spectacle` fallaban las dos con la misma línea: ``` error: failed to open object libavcodec/x86/rv34dsp_init.o: ProcessFdQuotaExceeded make: *** [ffbuild/library.mak:119: libavcodec/libavcodec.so.61] Error 1 ``` No es un fallo de ffmpeg: **libavcodec tiene 1.093 ficheros `.o` y el límite BLANDO era 1024**. El enlazador los abre a la vez y se pasa por 69. El límite DURO es 524288, así que subir el blando no necesita privilegios — nadie lo había subido porque 1024 alcanza para todo lo demás del corpus. **Es el tercer miembro de la misma familia, y conviene verlo junto:** bytes (disco lleno ⇒ ✗ en recetas sanas), inodos (`df -h` al 90 % con cero inodos ⇒ «0 fallan» mentiroso) y ahora fds. Los tres son límites de la MÁQUINA que el informe presenta como defecto de la RECETA, y los tres cuestan el mismo rato: alguien se sienta a leer `obs-studio.toml` buscando un bug que no está ahí. Se sube en los dos puntos de entrada de la granja —el loop y `build-farm.sh`— y no en la unidad de systemd, porque estos guiones VIAJAN con el código: una unidad que vive sólo en el worker se pierde el día que la caja se rehaga, y eso ya pasó con otras cosas. Se clampa al límite duro y **se dice lo que se hizo**: un límite que cambia en silencio es un límite que nadie sabe que existe hasta que vuelve a estorbar. Comprobado en el worker: `==> fds: límite blando 1024 → 65536 (duro: 524288)`. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Semantic delta
No definition changed: this change is text, documentation or data.
2 files · +64 −0
- scripts/build-farm.sh +32 −0
- scripts/farm/farm-worker-loop.sh +32 −0
diff --git a/scripts/build-farm.sh b/scripts/build-farm.sh
index 8c0e8ca1..2767ab4d 100755
--- a/scripts/build-farm.sh
+++ b/scripts/build-farm.sh
@@ -87,6 +87,38 @@ fi
# `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)
+# ── ⚠ EL LÍMITE DE DESCRIPTORES, QUE SE LEE COMO RECETA ROTA (2026-10-11) ────────────────────
+# Medido en el worker: `obs-studio` y `spectacle` fallaban las dos con
+#
+# error: failed to open object libavcodec/x86/rv34dsp_init.o: ProcessFdQuotaExceeded
+# make: *** [ffbuild/library.mak:119: libavcodec/libavcodec.so.61] Error 1
+#
+# y no es un fallo de ffmpeg: **libavcodec tiene 1.093 ficheros `.o` y el límite BLANDO era 1024**.
+# El enlazador los abre a la vez y se pasa por 69. El límite DURO es 524288, así que subir el blando
+# no necesita privilegios — nadie lo había subido porque el default de 1024 alcanza para todo lo
+# demás del corpus.
+#
+# Es el tercer miembro de la misma familia, y conviene verlo junto: **bytes** (disco lleno ⇒ ✗ en
+# recetas sanas), **inodos** (`df -h` al 90 % y cero inodos ⇒ «0 fallan» mentiroso) y ahora **fds**.
+# Los tres son límites de la MÁQUINA que el informe presenta como defecto de la RECETA, y los tres
+# cuestan el mismo rato: alguien lee `obs-studio.toml` buscando un bug que no está ahí.
+#
+# Se sube acá y no en la unidad de systemd porque este guion VIAJA con el código: una unidad que
+# vive sólo en el worker se pierde el día que la caja se rehaga, y eso ya pasó con otras cosas.
+_subir_fds() {
+ local duro blando quiero=65536
+ duro=$(ulimit -Hn 2>/dev/null) || return 0
+ blando=$(ulimit -Sn 2>/dev/null) || return 0
+ [ "$duro" = "unlimited" ] && duro=$quiero
+ [ "$quiero" -gt "$duro" ] && quiero=$duro
+ if [ "$blando" -lt "$quiero" ] 2>/dev/null; then
+ # Se DICE lo que se hizo: un límite que cambia en silencio es un límite que nadie sabe que
+ # existe hasta que vuelve a estorbar.
+ ulimit -n "$quiero" 2>/dev/null && echo "==> fds: límite blando $blando → $quiero (duro: $duro)"
+ fi
+}
+_subir_fds
+
n_total=$(printf '%s\n' "$queue" | grep -c . || true)
echo "==> GRANJA: $n_total recetas en $QUEUE, $JOBS workers en paralelo"
diff --git a/scripts/farm/farm-worker-loop.sh b/scripts/farm/farm-worker-loop.sh
index 273cacba..17b2f14d 100755
--- a/scripts/farm/farm-worker-loop.sh
+++ b/scripts/farm/farm-worker-loop.sh
@@ -22,6 +22,38 @@ cd "$HAMMER_DIR"
# El reintento serial de build-farm tampoco las salva, porque el árbol ya quedó inconsistente.
# Serializar es la única mitigación correcta hasta que el ADR 0012 elija (lock por árbol / árbol
# privado / caché inmutable + copia). Subirlo otra vez sin resolver eso reintroduce el bug.
+# ── ⚠ EL LÍMITE DE DESCRIPTORES, QUE SE LEE COMO RECETA ROTA (2026-10-11) ────────────────────
+# Medido en el worker: `obs-studio` y `spectacle` fallaban las dos con
+#
+# error: failed to open object libavcodec/x86/rv34dsp_init.o: ProcessFdQuotaExceeded
+# make: *** [ffbuild/library.mak:119: libavcodec/libavcodec.so.61] Error 1
+#
+# y no es un fallo de ffmpeg: **libavcodec tiene 1.093 ficheros `.o` y el límite BLANDO era 1024**.
+# El enlazador los abre a la vez y se pasa por 69. El límite DURO es 524288, así que subir el blando
+# no necesita privilegios — nadie lo había subido porque el default de 1024 alcanza para todo lo
+# demás del corpus.
+#
+# Es el tercer miembro de la misma familia, y conviene verlo junto: **bytes** (disco lleno ⇒ ✗ en
+# recetas sanas), **inodos** (`df -h` al 90 % y cero inodos ⇒ «0 fallan» mentiroso) y ahora **fds**.
+# Los tres son límites de la MÁQUINA que el informe presenta como defecto de la RECETA, y los tres
+# cuestan el mismo rato: alguien lee `obs-studio.toml` buscando un bug que no está ahí.
+#
+# Se sube acá y no en la unidad de systemd porque este guion VIAJA con el código: una unidad que
+# vive sólo en el worker se pierde el día que la caja se rehaga, y eso ya pasó con otras cosas.
+_subir_fds() {
+ local duro blando quiero=65536
+ duro=$(ulimit -Hn 2>/dev/null) || return 0
+ blando=$(ulimit -Sn 2>/dev/null) || return 0
+ [ "$duro" = "unlimited" ] && duro=$quiero
+ [ "$quiero" -gt "$duro" ] && quiero=$duro
+ if [ "$blando" -lt "$quiero" ] 2>/dev/null; then
+ # Se DICE lo que se hizo: un límite que cambia en silencio es un límite que nadie sabe que
+ # existe hasta que vuelve a estorbar.
+ ulimit -n "$quiero" 2>/dev/null && echo "==> fds: límite blando $blando → $quiero (duro: $duro)"
+ fi
+}
+_subir_fds
+
JOBS="${JOBS:-1}"
IDLE_SLEEP="${IDLE_SLEEP:-300}"
# Fichero de lock compartido con campana-deuda.sh (mismo path en los dos, o no hay exclusión).