Разработка отчетов на схеме компоновки данных (СКД) с использованием внешних наборов данных — стандартная задача для разработчика 1С. Однако при обработке больших объемов информации в процедуре ПриКомпоновкеРезультата модуля объекта отчета можно столкнуться с крайне неприятным поведением платформы. В процессе отладки выполнение кода внезапно прерывается, и отладчик снова "выбрасывает" в начало процедуры ПриКомпоновкеРезультата, из-за чего тяжелый алгоритм начинает выполняться по кругу.
В этой статье мы подробно проанализируем, почему происходит такое зацикливание, разберем внутренние механизмы платформы 1С:Предприятие 8.3, связанные с фоновыми заданиями и стабильностью рабочих процессов, и рассмотрим практические варианты решения этой проблемы.
Когда разработчик сталкивается с тем, что процедура ПриКомпоновкеРезультата начинает отрабатывать повторно (иногда по 3-5 раз или бесконечно), причин может быть несколько. Рассмотрим три основные технические причины, связанные с особенностями платформы 1С.
1. Специфика работы и отладки фоновых заданий (Переключение потоков)
В современных конфигурациях, разработанных на базе Библиотеки стандартных подсистем (БСП), отчеты формируются не в основном сеансе пользователя, а в фоновом режиме. Это необходимо для того, чтобы интерфейс программы не зависал во время формирования тяжелых отчетов.
Если в настройках отладки конфигуратора включено автоматическое подключение фоновых заданий, отладчик перехватывает их запуск. Если пользователь случайно нажал кнопку "Сформировать" дважды или логика формы предусматривает несколько параллельных запросов, платформа запускает несколько параллельных фоновых заданий. Отладчик будет поочередно останавливаться на точке останова в каждом из этих потоков. Разработчику при этом кажется, что выполнение одного и того же кода началось заново, хотя на самом деле произошло простое переключение сеансов отладки.
2. Превышение таймаута ожидания фонового задания (Механизм БСП)
Это наиболее частая причина при длительной отладке подсистем СКД "по шагам". Когда выполнение кода останавливается на точке останова внутри процедуры ПриКомпоновкеРезультата, поток на сервере полностью замораживается. В это время клиентское приложение (форма отчета) ожидает ответа в рамках механизма длительных операций:
ПриКомпоновкеРезультата.3. Аварийное завершение рабочего процесса сервера (rphost) по превышению лимита памяти
Если объем обрабатываемых данных велик (например, таблица значений содержит более 20 000 строк) и при этом в коде допущена критическая архитектурная ошибка (запрос в цикле), серверный процесс rphost может исчерпать доступные лимиты оперативной памяти или процессорного времени. При превышении настроенных лимитов безопасности сервер 1С принудительно перезапускает рабочий процесс. Клиентское соединение при этом пытается восстановиться и перезапускает формирование отчета, что снова инициирует вызов процедуры компоновки.
Рассмотрим типичный пример кода, который приводит к подобному поведению системы:
Процедура ПриКомпоновкеРезультата(ДокументРезультат, ДанныеРасшифровки, СтандартнаяОбработка)
СтандартнаяОбработка = Ложь;
// Получение тяжелого массива данных
Данные = ПолучитьДанные();
НаборыДанных = Новый Структура("ВходящиеДанные", Данные);
Схема = ПолучитьМакет("ОсновнаяСхемаКомпоновкиДанных");
КМ = Новый КомпоновщикМакетаКомпоновкиДанных;
Макет = КМ.Выполнить(Схема, КомпоновщикНастроек.Настройки);
ПК = Новый ПроцессорКомпоновкиДанных;
ПК.Инициализировать(Макет, НаборыДанных);
ПВ = Новый ПроцессорВыводаРезультатаКомпоновкиДанныхВТабличныйДокумент;
ПВ.УстановитьДокумент(ДокументРезультат);
ПВ.Вывести(ПК, Истина);
КонецПроцедуры
В этом коде свойство СтандартнаяОбработка установлено в значение Ложь, что отменяет стандартный вывод отчета платформой, так как мы выводим его программно. Однако основная уязвимость кроется внутри функции ПолучитьДанные().
Предположим, что внутри этой функции выполняется запрос, который возвращает таблицу на 20 000 строк, а затем для заполнения недостающих реквизитов выполняется обход этой таблицы в цикле с обращением к общему серверному модулю:
// Пример антипаттерна проектирования в 1С
Функция ПолучитьДанные()
ТаблицаРезультат = Новый ТаблицаЗначений;
// ... Инициализация колонок ...
Запрос = Новый Запрос("ВЫБРАТЬ Ссылка, Номенклатура ИЗ Документ.РеализацияТоваровУслуг");
ТаблицаРезультат = Запрос.Выполнить().Выгрузить();
// Критическая ошибка: запрос в цикле через общий модуль
Для Каждого СтрокаТЗ Из ТаблицаРезультат Цикл
СтрокаТЗ.Цена = ОбщиеПроцедурыИФункции.ПолучитьЦенуНоменклатуры(СтрокаТЗ.Номенклатура, ТекущаяДата());
КонецЦикла;
Возврат ТаблицаРезультат;
КонецФункции
При таком подходе платформа выполнит 20 000 отдельных обращений к базе данных! Время выполнения этой функции растянется на минуты, что гарантированно приведет к срабатыванию таймаутов фонового задания БСП или к падению процесса rphost по памяти.
Давайте разберем по шагам, как оптимизировать архитектуру отчета и избавиться от циклического выполнения процедуры компоновки данных.
Самое важное правило разработки в 1С — никогда не выполнять запросы внутри циклов. Вместо построчного обращения к базе данных мы должны получить все необходимые цены одним пакетным запросом. Перепишем функцию ПолучитьДанные() с использованием временных таблиц:
Функция ПолучитьДанныеОптимизированная()
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Реализация.Ссылка КАК Ссылка,
| Реализация.Номенклатура КАК Номенклатура
|ПОМЕСТИТЬ ВТ_ИсходныеДанные
|ИЗ
| Документ.РеализацияТоваровУслуг КАК Реализация
|;
|
|////////////////////////////////////////////////////////////////////////////////
|ВЫБРАТЬ
| ВТ.Ссылка КАК Ссылка,
| ВТ.Номенклатура КАК Номенклатура,
| ЕСТЬNULL(ЦеныНоменклатурыСрезПоследних.Цена, 0) КАК Цена
|ИЗ
| ВТ_ИсходныеДанные КАК ВТ
| ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.ЦеныНоменклатуры.СрезПоследних(&Период, Номенклатура В (ВЫБРАТЬ ВТ_ИсходныеДанные.Номенклатура ИЗ ВТ_ИсходныеДанные)) КАК ЦеныНоменклатурыСрезПоследних
| ПО ВТ.Номенклатура = ЦеныНоменклатурыСрезПоследних.Номенклатура";
Запрос.УстановитьПараметр("Период", ТекущаяДата());
Возврат Запрос.Выполнить().Выгрузить();
КонецФункции
Этот запрос отработает за доли секунды вместо нескольких минут, так как выборка данных и их соединение с ценами происходят непосредственно на уровне СУБД.
Чтобы избежать переключения потоков отладчика и таймаутов длительных операций во время пошаговой отладки, рекомендуется временно отключить фоновое формирование отчета. Сделать это можно следующими способами:
ВыполнятьВФоновомРежиме = Ложь.Передача больших таблиц значений (ТЗ) в качестве внешнего набора данных СКД через параметр ВходящиеДанные требует размещения всей этой таблицы в оперативной памяти сервера приложений 1С. Если вы обрабатываете сотни тысяч строк, рассмотрите возможность полного переноса логики получения данных в саму схему компоновки (в конструктор запроса СКД). Использование встроенных запросов СКД позволяет платформе оптимизировать выборку, накладывать системные отборы на уровне базы данных и не загружать лишние данные в оперативную память сервера.
Проанализировав логику работы платформы и применив эти оптимизации, мы получим стабильно работающий отчет, который формируется быстро и предсказуемо ведет себя под отладчиком.