При переходе от интерактивной работы в обработках к автоматическому выполнению кода через фоновые и регламентные задания разработчики часто сталкиваются с неожиданным изменением поведения программы. Код, который отлично отрабатывал при нажатии на кнопку в форме, в фоновом режиме может выдавать ошибку «Метод объекта не обнаружен» или аварийно завершаться с сообщением «Превышен максимальный расход памяти сервера за один вызов».
В этой статье мы подробно проанализируем причины возникновения обеих проблем и разберем практические способы их решения.
Рассмотрим ситуационную задачу: при выполнении выгрузки заказов через форму кнопкой вызов процедуры модуля объекта ЗаказОб.ПечатьСчетаЗаказа("Заказ") проходит успешно и возвращает ТабличныйДокумент. Однако при запуске той же логики в фоновом задании платформа выдает ошибку о том, что метод не найден.
Выясним причину такого поведения. Фоновое задание выполняется исключительно на стороне сервера 1С и не имеет клиентского контекста. Если процедура или весь модуль объекта (либо общего модуля) обернуты в инструкцию препроцессора #Если Клиент Тогда ... #КонецЕсли, то при компиляции модуля в контексте сервера эта процедура просто исключается из кода.
Посмотрим на пример кода, который приведет к ошибке в фоновом режиме:
#Если Клиент Тогда
Функция ПечатьСчетаЗаказа(Вид) Экспорт
// Формирование табличного документа на клиенте
Возврат ТабДок;
КонецФункции
#КонецЕсли
Так как клиентского контекста у фонового задания нет, попытка обратиться к методу ПечатьСчетаЗаказа с сервера приводит к ошибке «Метод объекта не обнаружен».
Для устранения этой проблемы выполним следующие шаги:
#Если Клиент Тогда, #Если КлиентУправляемоеПриложение Тогда и аналогичных.ТабличныйДокумент без непосредственного вывода на экран (без вызова клиентских форм и диалогов), перенесем ее из клиентского контекста в серверный или снимите ограничение препроцессора.#Если Сервер Или СерверАвтономный Сервер НаСервере Тогда или вовсе уберем клиентские обертки.Исправленный вариант объявления функции в модуле:
Функция ПечатьСчетаЗаказа(Вид) Экспорт
// Данный код доступен как при вызове с клиента, так и в фоновом задании на сервере
ТабДок = Новый ТабличныйДокумент;
// Логика заполнения табличного документа
Возврат ТабДок;
КонецФункции
Проанализируем вторую распространенную проблему. При обработке больших массивов данных (например, получение цен для 40 000 позиций товаров) фоновое задание может аварийно завершаться ошибкой превышения памяти сервера.
Эта ошибка порождается механизмом защиты кластера серверов 1С. Платформа отслеживает объем оперативной памяти, выделяемый рабочим процессом rphost под один вызов сервера. Когда пользователь работает интерактивно через форму, данные часто запрашиваются порциями или вызовы дробятся. В фоновом же задании весь цикл обработки 40 000 элементов проходит в рамках одного длительного серверного вызова. Оперативная память накапливает промежуточные коллекции, кэши, структурированные данные, и сборщик мусора не успевает её очистить, что приводит к превышению установленного лимита памяти.
Важное замечание: Добавление искусственных пауз с помощью функции Пауза() не очищает оперативную память рабочего процесса. Это лишь затянет время выполнения фонового задания, но лимит памяти за один вызов все равно будет превышен.
Разберем проверенные методики борьбы с повышенным потреблением памяти в фоновых заданиях:
Вместо обработки всех 40 000 позиций в одном цикле разделим массив на пакеты (например, по 500–1000 штук). Для сброса накопленной памяти обработку каждого пакета следует выносить в отдельную серверную процедуру или вызывать через отдельные серверные контексты.
После завершения работы с очередным пакетом данных необходимо явно обнулять переменные, содержащие большие коллекции данных или структуры. Для этого используем присвоение Переменная = Неопределено; или метод Очистить() для таблиц значений и массивов.
При получении цен или сведений о товарах необходимо избегать считывания объектов целиком (СправочникОбъект) внутри цикла. Получение цен следует выполнять одним запросом к регистру сведений с соединением по временной таблице ключей или товаров, считывая только необходимые числовые и ссылочные поля.
Большой объем данных можно разделить на несколько независимых частей и запустить их параллельно с помощью метода ФоновыеЗадания.Выполнить(). Каждое фоновое задание будет выполняться в своем отдельном потоке и при завершении полностью освободит всю выделенную ему оперативную память.
В консоли администрирования кластера 1С можно скорректировать параметры рабочего сервера: «Безопасный расход памяти за один вызов» и «Предельный объем памяти за один вызов» (увеличить значение лимита байт или установить -1 для снятия ограничений). Однако следует учитывать, что на современных версиях платформы изменение этих параметров может требовать лицензии уровня КОРП, а прямое снятие ограничений без оптимизации кода создает риск нестабильной работы всего рабочего процесса rphost.