[Unit] Description=takana build farm worker (loop autónomo) After=network-online.target Wants=network-online.target # ── FRENO AL CRASHLOOP (2026-08-31) ────────────────────────────────────────────────────────────── # Sin esto, `Restart=always` + una receta que no entra en RAM = bucle infinito. Medido en el LXC # dev.gioser.net: `clang18` disparó el OOM killer, systemd relanzó a los 30 s, el loop volvió a la # misma receta (build-farm recorre la cola por glob alfabético y clang18 sale primera), OOM otra vez. # **30 HORAS así**: la caja quedó con presión de I/O `full avg300=43` —todo bloqueado en disco casi # la mitad del tiempo— y sshd sin poder ni completar el banner, o sea imposible de diagnosticar # desde fuera. Dos `oom-kill` en el journal fue todo lo que quedó como rastro. # # 3 arranques en 1 h ⇒ systemd se rinde y deja la unidad en `failed`, VISIBLE en `systemctl status`. # Un worker parado y diagnosticable vale más que uno que se reinicia para siempre: el hub ya tolera # workers ausentes (cosecha-cron avisa "siembra falló" y sigue), así que rendirse no pierde nada. StartLimitIntervalSec=3600 StartLimitBurst=3 [Service] Type=simple WorkingDirectory=/opt/takana Environment=HOME=/root # ADR 0016: se fijan LAS DOS mientras dure el renombre. Los scripts leen # ${TAKANA_DIR:-${HAMMER_DIR:-…}}, pero un script VIEJO —o una copia del repo # que todavía no sincronizó— sólo mira HAMMER_DIR. Medido el 2026-09-09: esta # unit era el ÚNICO sitio fuera del repo que traía una variable vieja puesta, y # es lo que hoy impide retirar la caída. Cuando el worker corra con TAKANA_DIR # y el resto de la flota también, se borra la segunda línea y recién ahí se # puede retirar la caída del código (etapa 6). Environment=TAKANA_DIR=/opt/takana Environment=HAMMER_DIR=/opt/takana # JOBS no se fija: el loop usa nproc por defecto (cada build usa 1 core por codegen-units=1). # Así, al REPOTENCIAR la caja (CCX13→CCX23/33), el worker auto-escala los slots sin tocar config. # La línea CCX da ~4GB RAM/core ⇒ nproc slots quedan con margen para builds Rust + swap. ExecStart=/opt/takana/scripts/farm/farm-worker-loop.sh Restart=always RestartSec=30 # El OOM del worker NO se reintenta como si fuera un fallo transitorio: si la receta no entra en la # caja, no va a entrar en 30 segundos. `stop` deja la unidad detenida y contable por StartLimitBurst. OOMPolicy=stop # deja respirar al sistema en una caja chica Nice=10 IOSchedulingClass=idle [Install] WantedBy=multi-user.target