gitea: GOMEMLIMIT, porque no había techo de memoria en NINGÚN nivel
Llevaba 10 días con 4,29 GB de RSS en una caja de 7,7 GB (54 %), swap en 3,7 GB y pico virtual de 12 GB. El sospechoso habitual estaba descartado: REPO_INDEXER_ENABLED no está puesto, así que el indexador de código —el que normalmente se come la RAM en gitea— está apagado. Sólo hay un indexador de issues en bleve: 96 KB en disco. Lo que había era ausencia de límite en los tres sitios posibles: · rlimits.mem_bytes = null en la card · el cgroup arje.slice/gitea declarado pero SIN crear en /sys/fs/cgroup · ni GOMEMLIMIT ni GOGC en el envp Sin techo, el runtime de Go conserva mapeada la arena que ya liberó: el RSS se queda en la marca de agua más alta y no vuelve. ⚠ Y la marca de agua la explica la CARGA, no un defecto de gitea. Medido sobre los logs de un día: 621.000 peticiones desde 66.401 IPs distintas con user-agents de navegador FALSOS, recorriendo combinatoriamente /src (274k), /commits (102k), /blame (81k) y /raw (56k) de tawasuyu/tawasuyu. 600.000 con status 200, o sea renderizadas — resaltado de sintaxis y blame, lo más caro que gitea sabe hacer. Es scraping de forja, no tráfico. GOMEMLIMIT es un límite BLANDO: el GC aprieta más y el proceso NO muere. Es deliberadamente lo contrario de rlimits.mem_bytes, que sería duro y haría que arje lo reiniciara en bucle cada vez que un blame grande tocara el techo — peor que el problema. 2 GiB deja ~5,7 GB para el resto (los builds piden 2-3 GB en pico). Verificado en el anfitrión: GOMEMLIMIT=2GiB presente en el proceso (no en el envoltorio sh -c, que ya hizo exec), RSS 3718 MB → 338 MB, y la caja pasó de 1,8 GB disponibles a 5,67 GB. ⚠ Detalle de arje que importa: `arjectl restart` reutiliza la MISMA Card en memoria y no habría releído el cambio. Lo que lee del disco es `start`, así que la secuencia correcta es stop + start. ⚠ Esto trata el SÍNTOMA. La causa es el saqueo y su sitio es el proxy (hoy saywa): limitar o cortar /blame, /commits y /raw a anónimos. Eso es política del sitio, no de esta receta.
Semantic delta
No definition changed: this change is text, documentation or data.
1 files · +29 −1
- recipes/gitea.toml +29 −1
diff --git a/recipes/gitea.toml b/recipes/gitea.toml
index a92973d7..d96b57f7 100644
--- a/recipes/gitea.toml
+++ b/recipes/gitea.toml
@@ -138,7 +138,35 @@ label = "gitea"
id = "01M2EKDA00NKYGE2BB5DFH1EFF"
exec = "/bin/busybox"
argv = ["sh", "-c", "test -f /etc/gitea/app.ini || { echo 'gitea: falta /etc/gitea/app.ini — es config del SITIO, viene con los datos' >&2; exit 78; }; /bin/grep -q '^gitea:' /etc/passwd || { echo 'gitea: no existe el usuario `gitea` en /etc/passwd y gitea NO corre como root' >&2; exit 78; }; cd /var/lib/gitea || exit 78; exec /usr/bin/setuidgid gitea /usr/bin/gitea web --config /etc/gitea/app.ini"]
-envp = [["PATH", "/usr/bin:/bin"], ["HOME", "/var/lib/gitea"], ["GITEA_WORK_DIR", "/var/lib/gitea"], ["USER", "gitea"]]
+# ⚠ **`GOMEMLIMIT` — medido el 2026-10-20, y la causa NO era un leak.**
+# gitea llevaba 10 días corriendo con **4,29 GB de RSS** en una caja de 7,7 GB (54 %), con el swap
+# en 3,7 GB y un pico virtual de 12 GB. El sospechoso habitual estaba descartado:
+# `REPO_INDEXER_ENABLED` NO está puesto, así que el indexador de código —el que normalmente se come
+# la RAM en gitea— está apagado.
+#
+# Lo que había era **ningún techo de memoria en ningún nivel**: `rlimits.mem_bytes` a `null`, el
+# cgroup `arje.slice/gitea` declarado pero SIN crear en `/sys/fs/cgroup`, y ni `GOMEMLIMIT` ni
+# `GOGC` en este `envp`. Sin límite, el runtime de Go conserva mapeada la arena que ya liberó: el
+# RSS se queda en la marca de agua más alta que tocó y no vuelve.
+#
+# ⚠ Y la marca de agua la explica la CARGA, no un defecto de gitea: medido sobre los logs de un día,
+# **621.000 peticiones desde 66.401 IPs distintas** con user-agents de navegador falsos, recorriendo
+# combinatoriamente `/src` (274k), `/commits` (102k), `/blame` (81k) y `/raw` (56k) de
+# `tawasuyu/tawasuyu`. 600.000 de ellas con status 200, o sea renderizadas: resaltado de sintaxis y
+# `blame`, que es lo más caro que gitea sabe hacer. Es scraping de forja, no tráfico.
+#
+# ⇒ `GOMEMLIMIT` es un límite **BLANDO**: el GC aprieta más para no pasarse, y el proceso **no
+# muere**. Es deliberadamente lo contrario de `rlimits.mem_bytes`, que sería duro y haría que arje
+# la reiniciara en bucle cada vez que un `blame` grande tocara el techo — peor que el problema.
+#
+# 2 GiB deja ~5,7 GB para el resto (los builds del corpus piden 2-3 GB en pico). El coste es más CPU
+# en GC bajo esta carga; es el intercambio correcto en una caja que, llena, **no se degrada: se cae
+# entera**.
+#
+# ⚠ Esto trata el SÍNTOMA. La causa es el saqueo, y su sitio es el proxy (hoy `saywa`): limitar o
+# cortar `/blame`, `/commits` y `/raw` a anónimos. Eso es decisión de política del sitio, no de esta
+# receta.
+envp = [["PATH", "/usr/bin:/bin"], ["HOME", "/var/lib/gitea"], ["GITEA_WORK_DIR", "/var/lib/gitea"], ["USER", "gitea"], ["GOMEMLIMIT", "2GiB"]]
networking = "full"
cgroup = "arje.slice/gitea"
restart = { initial_ms = 1000, max_ms = 30000 }