Почему перезапускаются фоновые задания 1С и как обеспечить непрерывность длительных вычислений?

Программист 1С v8.3 (Управляемые формы) 1С:Зарплата и Управление Персоналом
← На главную

При работе с высоконагруженными системами на платформе 1С 8.3 программисты часто сталкиваются с ситуацией, когда длительные фоновые задания (выполняющиеся часы или даже сутки) внезапно прерываются или начинают свою работу заново. Это критично для тяжелых выгрузок или формирования регламентированной отчетности по большому количеству подразделений (например, отчет П-4 в ЗУП 3.1). Разберем причины этого поведения и способы создания отказоустойчивых алгоритмов.

Причина 1: Настройки перезапуска рабочих процессов в кластере

Первым делом проанализируем настройки консоли администрирования кластера серверов 1С. В свойствах рабочего сервера существуют параметры, управляющие «жизненным циклом» рабочих процессов rphost. Рассмотрим наиболее важные из них:

Когда rphost достигает критической отметки по памяти, он помечается как «выключенный», и платформа создает новый процесс для новых соединений. Старый процесс пытается завершить текущие задания, но если они длятся слишком долго, он принудительно завершается, обрывая все выполняющиеся в нем фоновые задания.

Причина 2: Механизм отказоустойчивости и повторный вызов

Выясним, почему задание может начаться «с начала». Если вы используете метод ФоновыеЗадания.ОжидатьЗавершения() в клиентском сеансе или в родительском фоновом задании, платформа при сбое рабочего процесса может попытаться восстановить состояние системы. В некоторых случаях, если клиентское соединение «потеряло» серверный контекст из-за падения rphost, повторная попытка обращения к серверу (автоматический реконнект) может привести к тому, что инициализирующий код запустится заново. Если в коде не предусмотрена проверка уже обработанных данных, задания будут запущены с исходными параметрами.

Решение 1: Оптимизация потребления памяти и производительности

Проанализируем ситуацию с точки зрения кода. Если фоновое задание формирует тяжелый отчет (например, П-4), оно активно работает с временными таблицами и кешем компоновки данных. Чтобы снизить нагрузку на память, выполним следующие шаги:

  1. Очистка временных таблиц: Всегда явно удаляйте временные таблицы в запросах, как только они перестают быть нужными.
  2. Индексация: Проверьте планы запросов. Если отчет по 4000 подразделениям работает медленно, возможно, не хватает индексов в регистрах расчета или накопления по измерениям «Организация» и «Подразделение».
  3. Отключение RLS: Если обработка запускается под пользователем с ограниченными правами, проверка прав доступа (RLS) для каждого из 4000 отчетов создаст колоссальную нагрузку. Попробуйте выполнять выгрузку в привилегированном режиме или под пользователем с полными правами.

Решение 2: Реализация архитектуры «Очередь заданий»

Для процессов, длящихся несколько суток, полагаться на стабильность одного фонового задания нельзя. Разберем самый надежный метод — использование вспомогательного регистра сведений для хранения статусов обработки. Рассмотрим пример реализации:

Создадим регистр сведений ОчередьОбработкиДанных с измерениями ОбъектОбработки (например, Ссылка на Подразделение) и ресурсами Статус (Перечисление: НеОбработано, ВРаботе, Завершено) и ДатаОбновления.

Алгоритм работы будет выглядеть следующим образом:


// 1. Заполнение очереди
Процедура ЗаполнитьОчередь(МассивПодразделений)
    Для Каждого Подразделение Из МассивПодразделений Цикл
        МенеджерЗаписи = РегистрыСведений.ОчередьОбработкиДанных.СоздатьМенеджерЗаписи();
        МенеджерЗаписи.ОбъектОбработки = Подразделение;
        МенеджерЗаписи.Статус = ПредопределенноеЗначение("Перечисление.СтатусыЗаданий.НеОбработано");
        МенеджерЗаписи.Записать();
    КонецЦикла;
КонецПроцедуры

Затем основная обработка запускает несколько «воркеров» (фоновых заданий), которые берут задачи из этой очереди:


// 2. Код внутри фонового задания (Воркера)
Процедура ВыполнитьЗаданиеИзОчереди()
    Пока Истина Цикл
        // Используем объектную блокировку или транзакцию для захвата задачи
        НачатьТранзакцию();
        Запрос = Новый Запрос("ВЫБРАТЬ ПЕРВЫЕ 1 ОбъектОбработки ИЗ РегистрСведений.ОчередьОбработкиДанных 
                              ГДЕ Статус = &Статус СУПОРЯДОЧИТЬ ПО ДатаОбновления ДЛЯ ИЗМЕНЕНИЯ");
        Запрос.УстановитьПараметр("Статус", ПредопределенноеЗначение("Перечисление.СтатусыЗаданий.НеОбработано"));
        Выборка = Запрос.Выполнить().Выбрать();
        
        Если Выборка.Следующий() Тогда
            ТекущийОбъект = Выборка.ОбъектОбработки;
            // Помечаем как "В работе"
            МенеджерЗаписи = РегистрыСведений.ОчередьОбработкиДанных.СоздатьМенеджерЗаписи();
            МенеджерЗаписи.ОбъектОбработки = ТекущийОбъект;
            МенеджерЗаписи.Статус = ПредопределенноеЗначение("Перечисление.СтатусыЗаданий.ВРаботе");
            МенеджерЗаписи.Записать();
            ЗафиксироватьТранзакцию();
            
            // Выполняем полезную работу (например, формирование отчета)
            Результат = СформироватьОтчетПоПодразделению(ТекущийОбъект);
            
            // Фиксируем завершение
            МенеджерЗаписи.Статус = ПредопределенноеЗначение("Перечисление.СтатусыЗаданий.Завершено");
            МенеджерЗаписи.Записать();
        Иначе
            ОтменитьТранзакцию();
            Прервать; // Очередь пуста
        КонецЕсли;
    КонецЦикла;
КонецПроцедуры

Преимущества такой схемы:

  1. Устойчивость к сбоям: если рабочий процесс упадет, статус задания в регистре останется «В работе» или «Не обработано». При перезапуске системы новые фоновые задания просто подхватят недоделанную работу.
  2. Масштабируемость: вы можете запустить любое количество фоновых заданий (потоков) в зависимости от мощности процессора.
  3. Контроль прогресса: в любой момент пользователь может открыть форму регистра и увидеть, сколько подразделений уже обработано и сколько осталось.

Решение 3: Передача данных через Временное хранилище

Рассмотрим ситуацию, когда вы передаете список подразделений напрямую в параметры метода ФоновыеЗадания.Выполнить(). Если объектов тысячи, структура параметров может стать слишком тяжелой. Вместо передачи огромного массива ссылок, используйте ПоместитьВоВременноеХранилище(). Передавайте в фоновое задание только АдресХранилища. Это снижает нагрузку на сериализацию данных при вызове серверного метода.

Выясним причины изменения данных при перезапуске

Проанализируем интересный эффект: почему при автоматическом перезапуске задания в него попадают измененные данные (как в сообщении №41)? Это происходит потому, что при «мягком» перезапуске кластер фактически инициирует выполнение задания заново. Если ваш код формирования списка задач (запрос по подразделениям) находится внутри процедуры, вызываемой как фоновое задание, то каждый новый старт будет выполнять этот запрос к текущему состоянию базы данных. Чтобы этого избежать, фиксируйте состав пакета данных (Snapshot) до момента запуска параллельных процессов, например, записывая его в промежуточную таблицу или файл.

Резюмируем: Для стабильной работы десятидневных процессов необходимо отказаться от линейного выполнения в пользу архитектуры с фиксацией состояний. Используйте регистры сведений для очереди задач, следите за лимитами памяти в консоли кластера и не бойтесь оптимизировать типовые запросы 1С, если они становятся узким местом в вашей инфраструктуре.

← На главную