Чрезмерное потребление памяти из-за предварительной компиляции ассетов

Всем привет,

Мы уже несколько лет запускаем собственные экземпляры Discourse в OpenShift, и за последние месяцы (примерно с января 2026 года, что примерно совпадает с новым подходом, описанным в Introducing pre-compiled JS assets for self-hosters и Introducing a new build system for plugins), мы наблюдаем следующую ситуацию:

При предварительной компиляции ресурсов на этапе сборки (bundle exec rake assets:precompile:build) эта операция теперь завершается с ошибкой и потребляет более 20 ГБ:

...
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...

# зависает здесь на долгое время

При анализе потребления памяти мы видим:

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

Ранее этот процесс выполнялся довольно быстро с умеренным потреблением памяти.

Мы пробовали устанавливать переменные окружения, такие как CI=1 и NODE_OPTIONS="--max-old-space-size=X", но ничего не помогает ограничить это потребление памяти.

Сталкивается ли кто-то с той же проблемой и, если да, как вы её решили?

Большое спасибо!

Исмаэль

Вы можете поделиться списком установленных вами плагинов?

Ваша общая память составляет 4 ГБ? Если да, то настроен ли у вас файл подкачки?

Можете ли вы поделиться характеристиками сервера?

Привет, Дэвид,

Список дополнительных плагинов выглядит так:

#   - Кнопки торговли (используются на Маркетплейсе)
          - git clone --depth=1 https://github.com/jannolii/discourse-topic-trade-buttons.git
          #   - Сохранённые поисковые запросы
          - 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
          #   - Документация Discourse
          - git clone --depth=1 https://github.com/discourse/discourse-docs.git
          #   - Опросы MSGraph
          - git clone --depth=1 https://github.com/CERN/msgraph-poll-discourse-plugin.git

С наилучшими пожеланиями,

Исмаэль

Привет, Heliosurge,

Узлы имеют 8 ядер CPU и 30 ГиБ оперативной памяти. В обычных условиях форум потреблял 1 ядро CPU и максимум 2–3 ГиБ оперативной памяти (включая предварительную компиляцию).

Swap не настроен. Я понимал, что swap используется здесь при нехватке памяти, но в данном случае это не должно быть проблемой. Однако меня больше беспокоит объем потребляемой памяти, так как ранее такого не наблюдалось.

С уважением,

Исмаэль

Что ж, вашему серверу своп, вероятно, не нужен. Скорее всего, лучше всего сможет помочь член команды Дэвид.

Уменьшается ли потребление памяти сразу после завершения задачи assets:build?

Привет, @david,

Освобождается ли потребление памяти сразу после завершения задачи assets:build?

Нет. Я покопался глубже, и обнаружил кое-что странное.

До выполнения precompiling:build список плагинов выглядел так:

/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

В ходе отладки кода я заметил следующее поведение:

/var/www/discourse$ script/rails runner "AssetProcessor.ember_version"
Имя плагина — 'msgraph-polling', но имя директории плагина — 'msgraph-poll-discourse-plugin'

# здесь зависает навсегда

Вызов AssetProcessor.ember_version соответствует строке discourse/lib/plugin/js_manager.rb at latest · discourse/discourse · GitHub.

Поэтому я внес некоторые изменения в этот файл (прикреплен), в основном для вывода информации о том, где именно происходит задержка при обработке, а также для удаления вызова AssetProcessor.ember_version в строке discourse/lib/plugin/js_manager.rb at latest · discourse/discourse · GitHub, заменив его на 5, чтобы продолжить генерацию hex-значения.

Затем я уменьшил параметр параллелизма до 1 в строке discourse/lib/plugin/js_manager.rb at latest · discourse/discourse · GitHub (parallel_count = [Etc.nprocessors, 1].min), чтобы упростить процесс.

После этого я выполнил команду bundle exec rake assets:precompile:build, что привело к следующему:

/var/www/discourse$ bundle exec rake assets:precompile:build
Имя плагина — 'msgraph-polling', но имя директории плагина — 'msgraph-poll-discourse-plugin'
[assemble_ember_build] Повторное использование существующей сборки core ember. Всё готово.
Имя плагина — 'msgraph-polling', но имя директории плагина — 'msgraph-poll-discourse-plugin'
[Plugin::JsManager] Компиляция 49 плагинов...
Компиляция automation...
конец сортировки файлов
конец сортировки файлов
        hex_digest 103dc9ebebb80a7065cb8dd41fb3356b30f151f7
########### рекурсивно

# зависает здесь навсегда, потребляя всю доступную память

У меня есть подозрение, что проблема может быть связана со значением ulimit (которое у нас установлено в unlimited вместо чего-то вроде ulimit -n 1048576;), учитывая, что открывается довольно большое количество файлов, и их содержимое сохраняется в памяти (благодаря рекурсивным вызовам).

Дайте знать, если это вам что-то говорит, или если у вас есть другие идеи относительно возможной причины проблемы.

С уважением,

Исмаэль

js_manager.rb.txt (7.7 KB)

Всегда рекомендуется иметь файл подкачки (swap). Очень хорошая идея — разрешить ядру перерасход памяти (overcommit). Это может существенно снизить ваши пиковые потребности в памяти.

Сначала настройте эти два параметра, а затем попробуйте снова. По поводу перерасхода памяти см.:

Что касается диагностики, может быть полезно проверить dmesg на наличие событий OOM (нехватки памяти), что можно сделать после сбоя, а также запустить vmstat в момент зависания:

vmstat 5 5

Вот мои общие рекомендации по диагностике:

Привет @Ed_S,

Спасибо за ваше сообщение.

Я провёл диагностику, и вот что я получил:

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
# мы начинаем предварительную компиляцию...
 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

Потребление памяти резко возросло:

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

И процесс завис при компиляции плагинов:

/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...

# зависает здесь до получения ошибки OOMKilled

Можете ли вы изменить ulimit и проверить, воспроизводится ли описанное выше поведение?

С уважением,

Исмаэль

Ах, вы столкнулись с OOM — отлично. Это окончательный диагноз. ulimit здесь ни при чём.

Добавьте swap-раздел. Нет никаких причин не делать этого, кроме нехватки места на диске. Добавьте 8 ГБ или 16 ГБ и попробуйте снова. Ваша цель — добиться работоспособного состояния. Затем, если хотите, можно попытаться измерить, какой процесс вызывает перерасход памяти.

Настройте overcommit. Это хорошая практика, которая снижает проблемы с пиковым потреблением памяти. Вам не нужно понимать это или обосновывать — просто сделайте это. Это часть правильной настройки Linux. Сначала проверьте. Это очень просто:

# 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

Привет, ребята,

Большое спасибо за все советы, которые вы нам предоставили, мы очень ценим это. Мы считаем, что нам удалось выявить корневую причину недавних проблем с памятью.

Ранее запуск bundle exec rake assets:precompile:build во время сборки (от имени root) не требовал наличия Redis или подключения к базе данных. Это поведение изменилось (см.: Introducing pre-compiled JS assets for self-hosters и Introducing a new build system for plugins).

Чтобы учесть это, мы перенесли шаг bundle exec rake assets:precompile:build в инициализационный контейнер во время выполнения (перед выполнением db:migrate и т. д.). Это позволяет запускать его от имени пользователя discourse с необходимым доступом к сервисам Redis и базы данных.

Однако во время выполнения процесс попадает в цикл в lib/plugin/js_manager.rb. Просмотрев вывод ps -fe, мы видим, что pnpm неоднократно пытается добавить сам себя, что приводит к исчерпанию памяти:

...
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
...
# и список начинает расти и продолжает расти, вызывая исчерпание памяти

В наших тестах мы обнаружили, что запуск инициализационного контейнера от имени root, а затем выполнение npm uninstall -g pnpm, за которым следует npm install -g pnpm@10.28.0, устраняет цикл и позволяет завершить компиляцию плагинов успешно:

...
[Plugin::JsManager] Compiling 49 plugins...
[Plugin::JsManager] Finished initial compilation of plugins in 5.82s

Поэтому, прежде чем усложнять нашу инфраструктуру и, возможно, менять наш дизайн, я думаю, что этот вопрос больше для @david: есть ли планы восстановить предыдущее поведение для assets:precompile:build, чтобы он мог работать без подключения к Redis или базе данных (аналогично тому, что вы делаете с потоком DISCOURSE_DOWNLOAD_PRE_BUILT_ASSETS: 0)?

Кстати, и из любопытства: почему запуск процесса node от имени не root-пользователя вызывает этот рекурсивный цикл установки pnpm, тогда как запуск от root, похоже, его избегает?

С уважением,
Исмаэль