Consumo excesivo de memoria debido a la precompilación de activos

Hola a todos,

Hemos estado ejecutando nuestras propias instancias de Discourse en OpenShift durante los últimos años y, en los últimos meses (aproximadamente desde enero de 2026, y más o menos alineados con el nuevo enfoque explicado en Introducing pre-compiled JS assets for self-hosters y Introducing a new build system for plugins), hemos observado el siguiente escenario:

Al precompilar los activos durante la compilación (bundle exec rake assets:precompile:build), esta operación ahora falla y consume más de 20 GB:

...
gem install prometheus_exporter -v 2.2.0 -i /var/www/discourse/plugins/discourse-prometheus/gems/3.4.7 --no-document --ignore-dependencies --no-user-install
Successfully installed prometheus_exporter-2.2.0
1 gem installed
Plugin name is 'msgraph-polling', but plugin directory is named 'msgraph-poll-discourse-plugin'
[assemble_ember_build] No existing build info file found.
Fetching and extracting https://get.discourse.org/discourse-assets/2026.5.0-latest-03484cbd/production.tar.gz...
  % Total    % Received % Xferd  Average Speed   Time   Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
  0     0    0     0    0     0      0      0 --:--:-- --:--:-- --:--:--     0
  0     0    0     0    0     0      0      0 --:--:-- --:--:-- --:--:--     0
100 20.1M  100 20.1M    0     0  22.5M      0 --:--:-- --:--:-- --:--:-- 22.5M
Prebuilt assets downloaded and extracted successfully.
[assemble_ember_build] Reusing existing core ember build. All done.
Plugin name is 'msgraph-polling', but plugin directory is named 'msgraph-poll-discourse-plugin'
[Plugin::JsManager] Compiling 49 plugins...

# se queda bloqueado aquí durante mucho tiempo

Al observar el consumo de memoria, vemos:

Every 1.0s: free -h                             webapp-test-discourse-689b5fcb4d-fd2dp-debug-b7nn2: Mon May  4 14:15:57 2026

               total        used        free      shared  buff/cache   available
Mem:            28Gi        26Gi       596Mi       524Mi       2.1Gi       1.7Gi
Swap:             0B          0B          0B

mientras que antes era bastante rápido y con un consumo moderado de memoria.

Hemos intentado establecer variables de entorno como CI=1 y NODE_OPTIONS="-–max-old-space-size=X", pero nada parece ayudar a contener este consumo de memoria.

¿Alguien más está enfrentando el mismo problema y, de ser así, cómo lo solucionaron?

¡Muchas gracias!

Ismael

¿Podrías compartir una lista de los plugins que has instalado?

¿Es tu memoria total de 4 GB? Si es así, ¿tienes configurado un archivo de intercambio?

¿Puedes compartir las especificaciones del servidor?

Hola David,

La lista de plugins adicionales es:

#   - Botones de intercambio (utilizados en el Mercado)
          - git clone --depth=1 https://github.com/jannolii/discourse-topic-trade-buttons.git
          #   - Búsquedas guardadas
          - git clone --depth=1 https://github.com/discourse/discourse-saved-searches.git
          #   - Discourse Akismet
          - git clone --depth=1 https://github.com/discourse/discourse-akismet.git
          #   - Prometheus
          - git clone --depth=1 https://github.com/discourse/discourse-prometheus.git
          #   - Documentación de Discourse
          - git clone --depth=1 https://github.com/discourse/discourse-docs.git
          #   - Encuesta MSGraph
          - git clone --depth=1 https://github.com/CERN/msgraph-poll-discourse-plugin.git

Saludos,

Ismael

Hola Heliosurge,

Los nodos tienen 8 CPU y 30 GiB de RAM. En condiciones normales, un foro solía consumir 1 CPU y un máximo de 2-3 GB de RAM (incluyendo la precompilación).

No hay swap configurado. Entendí que el swap se utiliza aquí cuando hay restricciones de memoria, lo cual no debería ser el caso aquí. Sin embargo, estoy más “preocupado” por la cantidad de memoria consumida, ya que esto nunca había sido un problema.

Saludos,

Ismael

Bueno, las especificaciones de tu servidor no deberían necesitar memoria de intercambio. El miembro del equipo David probablemente pueda ayudarte mejor.

¿El uso de memoria disminuye tan pronto como se completa la tarea assets:build?

Hola @david,

¿El uso de memoria disminuye tan pronto como termina la tarea assets:build?

No. Investigué más a fondo y hay algo extraño.

Antes de ejecutar precompiling:build, esta es la lista de plugins:

/var/www/discourse$ ls plugins/
automation           discourse-akismet           discourse-data-explorer  discourse-hcaptcha           discourse-microsoft-auth  discourse-post-voting  discourse-saved-searches       discourse-user-notes           styleguide
chat                 discourse-apple-auth        discourse-details        discourse-lazy-videos        discourse-narrative-bot   discourse-presence     discourse-solved               discourse-zendesk-plugin
checklist            discourse-assign            discourse-docs           discourse-local-dates        discourse-oauth2-basic    discourse-prometheus   discourse-subscriptions        footnote
discourse-adplugin   discourse-cakeday           discourse-gamification   discourse-login-with-amazon  discourse-openid-connect  discourse-reactions    discourse-templates            msgraph-poll-discourse-plugin
discourse-affiliate  discourse-calendar          discourse-github         discourse-lti                discourse-patreon         discourse-rewind       discourse-topic-trade-buttons  poll
discourse-ai         discourse-chat-integration  discourse-graphviz       discourse-math               discourse-policy          discourse-rss-polling  discourse-topic-voting         spoiler-alert

He notado el siguiente comportamiento después de depurar el código.

/var/www/discourse$ script/rails runner "AssetProcessor.ember_version"
Plugin name is 'msgraph-polling', but plugin directory is named 'msgraph-poll-discourse-plugin'

# se cuelga aquí para siempre

AssetProcessor.ember_version corresponde a la línea discourse/lib/plugin/js_manager.rb at latest · discourse/discourse · GitHub.

Así que hice algunas modificaciones en este archivo (adjunto), básicamente para imprimir dónde se queda atascado mientras procesa, y para eliminar AssetProcessor.ember_version en discourse/lib/plugin/js_manager.rb at latest · discourse/discourse · GitHub, simplemente poniendo 5 para continuar con la generación del hash hexadecimal.

Luego, reduje el paralelismo a 1 para facilitar las cosas en discourse/lib/plugin/js_manager.rb at latest · discourse/discourse · GitHub (parallel_count = [Etc.nprocessors, 1].min).

Después de esto, ejecuté bundle exec rake assets:precompile:build, obteniendo el siguiente resultado:

/var/www/discourse$ bundle exec rake assets:precompile:build
Plugin name is 'msgraph-polling', but plugin directory is named 'msgraph-poll-discourse-plugin'
[assemble_ember_build] Reutilizando la compilación central de Ember existente. Todo listo.
Plugin name is 'msgraph-polling', but plugin directory is named 'msgraph-poll-discourse-plugin'
[Plugin::JsManager] Compilando 49 plugins...
Compilando automation...
fin de files.sort
fin de files.sort
        hex_digest 103dc9ebebb80a7065cb8dd41fb3356b30f151f7
########### recursivo

# se cuelga aquí para siempre, consumiendo toda la memoria

Sospecho que esto puede estar relacionado con el valor de ulimit (que tenemos configurado como unlimited en lugar de algo como ulimit -n 1048576;), dado que se abren un número considerable de archivos y se almacena su contenido en memoria (mediante las llamadas recursivas).

Avísame si esto te suena familiar o si tienes alguna otra pista sobre cuál podría ser el problema.

Saludos,

Ismael

js_manager.rb.txt (7.7 KB)

Siempre es aconsejable tener espacio de intercambio (swap). Es una muy buena idea permitir que el kernel realice sobrecompromiso de memoria. Esto puede reducir sustancialmente tus necesidades máximas de memoria.

Primero soluciona esas dos cosas y luego inténtalo de nuevo. Para el sobrecompromiso, consulta:

En cuanto al diagnóstico, podría ser útil revisar dmesg en busca de eventos de OOM (falta de memoria), lo cual puedes hacer después, y también ejecutar vmstat en el momento del bloqueo.

vmstat 5 5

Aquí está mi consejo general de diagnóstico:

Hola @Ed_S,

Gracias por tu mensaje.

He realizado el diagnóstico y esto es lo que obtengo:

vmstat 5 200
procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
 r  b   swpd   free   buff  cache   si   so    bi    bo   in   cs us sy id wa st
 3  0      0 19595924    104 3919416    0    0  4173    32  439 1040  5  1 93  0  0
 1  0      0 19595924    104 3919416    0    0     0   154 4249 6449  1  1 98  0  0
 1  0      0 19595924    104 3919416    0    0     0    39 4399 6778  1  1 98  0  0
 1  0      0 19595924    104 3919416    0    0    12    75 5414 8640  1  1 98  0  0
 1  0      0 19595924    104 3919416    0    0    51    69 4248 6637  1  1 99  0  0
 1  0      0 19595924    104 3919416    0    0     0    83 4441 6784  1  1 98  0  0
 1  0      0 19595924    104 3919416    0    0     9    53 6111 9254  2  1 97  0  0
 1  0      0 19595924    104 3919416    0    0     0   887 4854 7373  1  1 98  0  0
 1  0      0 19595924    104 3919416    0    0     0    40 4705 7319  1  1 98  0  0
 1  0      0 19595924    104 3919416    0    0     0    37 4701 7305  1  1 98  0  0
# iniciamos la precompilación...
 3  0      0 19595924    104 3919416    0    0   124   902 8292 10254 19  5 75  0  0
 2  0      0 19595924    104 3919416    0    0 43073  6829 13702 16200 11  4 82  4  0
 2  0      0 19595924    104 3919416    0    0 19624   815 12340 15581 10  4 83  3  0
 2  0      0 19595924    104 3919416    0    0  1818  3953 7554 9248 13  3 84  0  0
 2  0      0 19595924    104 3919416    0    0     0    99 7475 8661 16  2 82  0  0
 2  0      0 19595924    104 3919416    0    0     0    52 7634 9084 13  2 84  0  0
 2  0      0 19595924    104 3919416    0    0   115   585 6843 8121 13  2 85  0  0
 2  0      0 19595924    104 3919416    0    0     0 13139 7254 8444 13  2 84  0  0
 2  0      0 19595924    104 3919416    0    0     3  1305 8740 11091 14  2 83  0  0
procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
 r  b   swpd   free   buff  cache   si   so    bi    bo   in   cs us sy id wa st
 5  0      0 19595924    104 3919416    0    0   465  9798 8403 9279 13  2 85  0  0
 3  0      0 19595924    104 3919416    0    0     6    99 7264 8993 13  2 85  0  0
 2  0      0 19595924    104 3919416    0    0     0    96 7190 8627 13  2 85  0  0
 2  0      0 19595924    104 3919416    0    0     0    66 6869 8299 13  2 85  0  0
 2  0      0 19595924    104 3919416    0    0     0   109 7075 8521 13  2 85  0  0
 2  0      0 19595924    104 3919416    0    0     3    78 8763 11295 14  2 83  0  0
 2  0      0 19595924    104 3919416    0    0     0  3075 7337 8358 13  2 85  0  0
 4  0      0 19595924    104 3919416    0    0     6   133 7016 8697 13  2 85  0  0
 3  0      0 19595924    104 3919416    0    0     0    45 7005 8370 13  2 85  0  0
 2  0      0 19595924    104 3919416    0    0     0   134 7330 9011 13  2 85  0  0
 2  0      0 19595924    104 3919416    0    0    26    86 7239 8747 13  2 85  0  0
 2  0      0 19595924    104 3919416    0    0     0   127 8809 11618 15  3 83  0  0
 2  0      0 19595924    104 3919416    0    0     6  1473 7142 8352 13  2 85  0  0
 2  0      0 19595924    104 3919416    0    0  2021   136 8041 10138 13  3 84  0  0
 2  0      0 19595924    104 3919416    0    0  4457   664 6913 7927 12  3 84  0  0

El consumo de memoria se disparó:

               total        used        free      shared  buff/cache   available
Mem:            28Gi        26Gi       460Mi       518Mi       2.3Gi       1.8Gi
Swap:             0B          0B          0B

Y se quedó bloqueado mientras compilaba los plugins:

/var/www/discourse$ bundle exec rake assets:precompile:build

gem install prometheus_exporter -v 2.2.0 -i /var/www/discourse/plugins/discourse-prometheus/gems/3.4.7 --no-document --ignore-dependencies --no-user-install
Successfully installed prometheus_exporter-2.2.0
1 gem installed

A new release of RubyGems is available: 3.6.9 → 4.0.11!
Run `gem update --system 4.0.11` to update your installation.

Plugin name is 'msgraph-polling', but plugin directory is named 'msgraph-poll-discourse-plugin'
[assemble_ember_build] No existing build info file found.
Fetching and extracting https://get.discourse.org/discourse-assets/2026.5.0-latest-6b98fe35/production.tar.gz...
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
  0     0    0     0    0     0      0      0 --:--:-- --:--  0     0    0     0    0     0      0      0 --:--:-- --:--  0     0    0     0    0     0      0      0 --:--:-- --:--:-- --:--:--     0
  0     0    0     0    0     0      0      0 --:--:-- --:--:-- --:--:--     0
100 20.1M  100 20.1M    0     0  20.8M      0 --:--:-- --:--:-- --:--:-- 20.8M
Prebuilt assets downloaded and extracted successfully.
[assemble_ember_build] Reusing existing core ember build. All done.
Plugin name is 'msgraph-polling', but plugin directory is named 'msgraph-poll-discourse-plugin'
[Plugin::JsManager] Compiling 49 plugins...

# se queda colgado aquí hasta recibir un OOMKilled

¿Podrías modificar ulimit y ver si se produce el comportamiento descrito anteriormente?

Saludos,

Ismael

Ah, viste un OOM, genial. Eso es definitivo. ulimit no tiene nada que ver con esto.

Añade swap. No hay razón para no hacerlo, salvo la falta de espacio en disco. Añade 8 GB o 16 GB e inténtalo de nuevo. Lo importante es llegar a un estado funcional. Luego, si lo deseas, puedes intentar medir qué proceso está causando el problema.

Configura el overcommit. Es una buena práctica que reduce los problemas de pico de memoria. No necesitas entenderlo ni justificarlo, simplemente hazlo. Es parte de una configuración adecuada de Linux. Verifica primero. Es tan sencillo:

# uname -a
Linux ubuntu-4gb-hel1-1 6.8.0-110-generic #110-Ubuntu SMP PREEMPT_DYNAMIC
 Thu Mar 19 17:16:23 UTC 2026 aarch64 aarch64 aarch64 GNU/Linux
# cat /proc/sys/vm/overcommit_memory
1

Hola a todos,

Muchas gracias por todos los consejos que nos habéis proporcionado, los agradecemos mucho. Creemos que hemos identificado la causa raíz de los problemas de memoria recientes.

Anteriormente, ejecutar bundle exec rake assets:precompile:build en tiempo de compilación (como root) no requería tener Redis ni conexión a la base de datos. Este comportamiento ha cambiado (ref: Introducing pre-compiled JS assets for self-hosters y Introducing a new build system for plugins).

Para adaptarnos a esto, movimos el paso bundle exec rake assets:precompile:build a un contenedor de inicialización en tiempo de ejecución (antes de ejecutar db:migrate, etc.). Esto permite que se ejecute como el usuario discourse con el acceso a los servicios necesario tanto para Redis como para la base de datos.

Sin embargo, durante la ejecución, el proceso entra en un bucle en lib/plugin/js_manager.rb. Al observar ps -fe, vemos que pnpm intenta repetidamente agregarse a sí mismo, lo que lleva a la saturación de memoria:

...
discour+     704     688  5 11:00 pts/0    00:00:00 node /usr/bin/pnpm -C=frontend/asset-processor node build.js
discour+     718     704  5 11:00 pts/0    00:00:00 node /usr/bin/pnpm add pnpm@10.28.0 --loglevel=error --allow-build=@pnpm
discour+     729     718  6 11:00 pts/0    00:00:00 node /usr/bin/pnpm add pnpm@10.28.0 --loglevel=error --allow-build=@pnpm
discour+     740     729  6 11:00 pts/0    00:00:00 node /usr/bin/pnpm add pnpm@10.28.0 --loglevel=error --allow-build=@pnpm
discour+     754     740  7 11:00 pts/0    00:00:00 node /usr/bin/pnpm add pnpm@10.28.0 --loglevel=error --allow-build=@pnpm
...
# y la lista empieza a crecer y sigue y sigue, provocando la saturación de memoria

En nuestras pruebas, descubrimos que ejecutar el contenedor de inicialización como root en su lugar, y ejecutar npm uninstall -g pnpm seguido de npm install -g pnpm@10.28.0, resuelve el bucle y permite que la compilación del complemento finalice con éxito:

...
[Plugin::JsManager] Compilando 49 complementos...
[Plugin::JsManager] Compilación inicial de complementos finalizada en 5.82s

Por lo tanto, antes de sobreingenierizar nuestra infraestructura y probablemente cambiar nuestro diseño, creo que esta pregunta es más para @david: ¿hay planes de restaurar el comportamiento anterior para assets:precompile:build para que pueda ejecutarse sin Redis ni conexión a la base de datos (similar a lo que estás haciendo con el flujo DISCOURSE_DOWNLOAD_PRE_BUILT_ASSETS: 0)?

Por cierto, y por curiosidad: ¿por qué ejecutar el proceso node como un usuario no root desencadena este bucle de instalación recursiva de pnpm, mientras que ejecutarlo como root parece evitarlo?

Saludos,
Ismael