В процессе эксплуатации крупномасштабных систем на базе 1С:Предприятие, особенно в связке Linux и PostgreSQL, администраторы и разработчики часто сталкиваются с проблемой «утечки» или чрезмерного потребления оперативной памяти рабочими процессами rphost. Когда память на сервере заканчивается, это приводит к аварийному завершению работы (падению) сервера или срабатыванию механизма OOM Killer в операционной системе. Выяснить, какой именно код или какое конкретное фоновое задание является виновником, — задача нетривиальная, но решаемая. Разберем пошагово основные методы диагностики и локализации проблемы.
Проанализируем стандартную ситуацию: в консоли кластера 1С мы видим десятки фоновых заданий, запущенных от имени одного и того же пользователя (например, «Администратор»). В такой ситуации крайне сложно сопоставить конкретный сеанс с конкретным регламентным заданием. Рассмотрим самый эффективный превентивный метод диагностики — разделение прав.
Для каждого критически важного или потенциально «тяжелого» регламентного задания рекомендуется создать отдельного служебного пользователя в информационной базе (например, srv_ExchangeERP, srv_FullTextSearch, srv_CloseMonth). После этого в настройках регламентного задания необходимо указать соответствующего пользователя. Почему это удобно?
Рассмотрим стандартные инструменты платформы. В утилите «Администрирование серверов 1С» (или через ras/rac на Linux) для списка сеансов доступны важные показатели:
Если вы наблюдаете, что какой-то сеанс фонового задания планомерно увеличивает показатель текущей памяти, зафиксируйте Номер сеанса. С помощью обработки «Консоль заданий» (или инструментов ИР) можно сопоставить номер сеанса с конкретным регламентным заданием и перейти к анализу его кода.
Выясним причину чрезмерного потребления памяти на уровне кода. Самый точный метод — настройка Технологического журнала (ТЖ). Нам интересны два ключевых события: CALL и MEM. Рассмотрим пример настройки logcfg.xml для сбора информации о потреблении памяти свыше 1 ГБ (1 048 576 КБ):
<log location="/var/log/1c/logs/memory_leak" history="24">
<event>
<eq property="Name" value="CALL"/>
<gt property="MemoryPeak" value="1048576"/>
</event>
<property name="all"/>
</log>
Событие CALL с установленным свойством MemoryPeak покажет пиковое значение памяти, которое было достигнуто к моменту завершения вызова серверного метода. Однако стоит помнить: если процесс «упал» из-за нехватки памяти до завершения вызова, событие CALL может не записаться. В этом случае проанализируем событие MEM, которое фиксирует динамику распределения памяти в процессе работы кода.
Посмотрим на настройки рабочего сервера. В консоли кластера существует опция «Менеджер под каждый сервис». При ее включении система создает отдельные процессы для различных сервисов платформы. Это позволяет точнее определить, какой именно сервис (например, сервис полнотекстового поиска или сервис заданий) вызывает рост потребления ресурсов, наблюдая за процессами через системный монитор htop или top.
Дополнительно для серверов на Linux можно использовать переменные окружения, такие как SRV1CV8_MEM_LIMIT. Если задать лимит памяти для одного процесса rphost, то при достижении предела упадет только один рабочий процесс, а в журнале регистрации 1С появится запись об аварийном завершении конкретного фонового задания, что станет явным указателем на проблему.
Разберем ситуацию, когда прямое наблюдение затруднено. Тяжелые запросы в фоновых заданиях часто сопровождаются созданием огромных временных таблиц. В PostgreSQL рекомендуется мониторить размер папки base/pgsql_tmp. Если вы видите резкий рост объема временных файлов одновременно с ростом потребления RAM процессом rphost, значит, какое-то задание выполняет неоптимальную выборку данных (например, загружает миллионы строк в таблицу значений в памяти).
Если проблемное задание определено, но неясно, какая именно строка кода «ест» память, воспользуемся замером производительности. В современных версиях платформы (8.3.20+) можно программно включить замер для конкретного фонового сеанса. Обратите внимание на циклы, в которых создаются объекты, и на запросы, результат которых выгружается в память целиком (метод Выгрузить()). Попробуйте заменить выгрузку в таблицу значений на последовательную обработку выборки запроса:
// Неоптимально для памяти:
ТЗ = Запрос.Выполнить().Выгрузить();
Для Каждого Строка Из ТЗ Цикл
// Обработка
КонецЦикла;
// Оптимально для памяти:
Выборка = Запрос.Выполнить().Выбрать();
Пока Выборка.Следующий() Цикл
// Обработка каждой строки отдельно
КонецЦикла;
Проанализируем использование инструментов визуализации. Системы типа Prometheus в связке с Grafana и экспортерами для 1С позволяют строить графики потребления памяти каждым сеансом. На таких графиках хорошо видны «ступеньки» (резкие скачки), по времени которых в Журнале Регистрации можно безошибочно вычислить момент запуска регламентного задания.
Резюмируя: наиболее быстрый и эффективный путь — это комбинация Метода 1 (уникальные пользователи) и Метода 3 (мониторинг MemoryPeak в техжурнале). Это дает мгновенный ответ на вопрос «кто виноват», позволяя программисту сосредоточиться на оптимизации алгоритмов.