Почему процедура ПриКомпоновкеРезультата в СКД выполняется несколько раз при отладке?

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

Разработка отчетов на схеме компоновки данных (СКД) с использованием внешних наборов данных — стандартная задача для разработчика 1С. Однако при обработке больших объемов информации в процедуре ПриКомпоновкеРезультата модуля объекта отчета можно столкнуться с крайне неприятным поведением платформы. В процессе отладки выполнение кода внезапно прерывается, и отладчик снова "выбрасывает" в начало процедуры ПриКомпоновкеРезультата, из-за чего тяжелый алгоритм начинает выполняться по кругу.

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

Почему отладчик возвращается в начало процедуры? Выясняем причины

Когда разработчик сталкивается с тем, что процедура ПриКомпоновкеРезультата начинает отрабатывать повторно (иногда по 3-5 раз или бесконечно), причин может быть несколько. Рассмотрим три основные технические причины, связанные с особенностями платформы 1С.

1. Специфика работы и отладки фоновых заданий (Переключение потоков)

В современных конфигурациях, разработанных на базе Библиотеки стандартных подсистем (БСП), отчеты формируются не в основном сеансе пользователя, а в фоновом режиме. Это необходимо для того, чтобы интерфейс программы не зависал во время формирования тяжелых отчетов.

Если в настройках отладки конфигуратора включено автоматическое подключение фоновых заданий, отладчик перехватывает их запуск. Если пользователь случайно нажал кнопку "Сформировать" дважды или логика формы предусматривает несколько параллельных запросов, платформа запускает несколько параллельных фоновых заданий. Отладчик будет поочередно останавливаться на точке останова в каждом из этих потоков. Разработчику при этом кажется, что выполнение одного и того же кода началось заново, хотя на самом деле произошло простое переключение сеансов отладки.

2. Превышение таймаута ожидания фонового задания (Механизм БСП)

Это наиболее частая причина при длительной отладке подсистем СКД "по шагам". Когда выполнение кода останавливается на точке останова внутри процедуры ПриКомпоновкеРезультата, поток на сервере полностью замораживается. В это время клиентское приложение (форма отчета) ожидает ответа в рамках механизма длительных операций:

  1. Клиентская форма делает периодические запросы к серверу, проверяя статус выполнения фонового задания.
  2. Если разработчик слишком долго стоит на точке останова (обычно более 20-40 секунд), клиент считает, что фоновое задание "зависло" или аварийно завершилось по таймауту.
  3. Система БСП инициирует принудительную отмену текущего фонового задания и автоматически запускает **повторную попытку** его выполнения.
  4. В этот момент старое фоновое задание уничтожается, запускается новое, и отладчик снова останавливается в начале процедуры ПриКомпоновкеРезультата.

3. Аварийное завершение рабочего процесса сервера (rphost) по превышению лимита памяти

Если объем обрабатываемых данных велик (например, таблица значений содержит более 20 000 строк) и при этом в коде допущена критическая архитектурная ошибка (запрос в цикле), серверный процесс rphost может исчерпать доступные лимиты оперативной памяти или процессорного времени. При превышении настроенных лимитов безопасности сервер 1С принудительно перезапускает рабочий процесс. Клиентское соединение при этом пытается восстановиться и перезапускает формирование отчета, что снова инициирует вызов процедуры компоновки.

Анализ проблемного кода: как делать нельзя

Рассмотрим типичный пример кода, который приводит к подобному поведению системы:


Процедура ПриКомпоновкеРезультата(ДокументРезультат, ДанныеРасшифровки, СтандартнаяОбработка)
    
    СтандартнаяОбработка = Ложь;
    
    // Получение тяжелого массива данных
    Данные = ПолучитьДанные(); 
    НаборыДанных = Новый Структура("ВходящиеДанные", Данные);
    
    Схема = ПолучитьМакет("ОсновнаяСхемаКомпоновкиДанных");
    
    КМ = Новый КомпоновщикМакетаКомпоновкиДанных;
    Макет = КМ.Выполнить(Схема, КомпоновщикНастроек.Настройки);
    
    ПК = Новый ПроцессорКомпоновкиДанных;
    ПК.Инициализировать(Макет, НаборыДанных);
    
    ПВ = Новый ПроцессорВыводаРезультатаКомпоновкиДанныхВТабличныйДокумент;
    ПВ.УстановитьДокумент(ДокументРезультат);
    ПВ.Вывести(ПК, Истина);
    
КонецПроцедуры

В этом коде свойство СтандартнаяОбработка установлено в значение Ложь, что отменяет стандартный вывод отчета платформой, так как мы выводим его программно. Однако основная уязвимость кроется внутри функции ПолучитьДанные().

Предположим, что внутри этой функции выполняется запрос, который возвращает таблицу на 20 000 строк, а затем для заполнения недостающих реквизитов выполняется обход этой таблицы в цикле с обращением к общему серверному модулю:


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

При таком подходе платформа выполнит 20 000 отдельных обращений к базе данных! Время выполнения этой функции растянется на минуты, что гарантированно приведет к срабатыванию таймаутов фонового задания БСП или к падению процесса rphost по памяти.

Решения проблемы: пошаговое руководство

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

Шаг 1. Избавляемся от запросов в цикле (Пакетная обработка данных)

Самое важное правило разработки в 1С — никогда не выполнять запросы внутри циклов. Вместо построчного обращения к базе данных мы должны получить все необходимые цены одним пакетным запросом. Перепишем функцию ПолучитьДанные() с использованием временных таблиц:


Функция ПолучитьДанныеОптимизированная()
    
    Запрос = Новый Запрос;
    Запрос.Текст = 
        "ВЫБРАТЬ
        |   Реализация.Ссылка КАК Ссылка,
        |   Реализация.Номенклатура КАК Номенклатура
        |ПОМЕСТИТЬ ВТ_ИсходныеДанные
        |ИЗ
        |   Документ.РеализацияТоваровУслуг КАК Реализация
        |;
        |
        |////////////////////////////////////////////////////////////////////////////////
        |ВЫБРАТЬ
        |   ВТ.Ссылка КАК Ссылка,
        |   ВТ.Номенклатура КАК Номенклатура,
        |   ЕСТЬNULL(ЦеныНоменклатурыСрезПоследних.Цена, 0) КАК Цена
        |ИЗ
        |   ВТ_ИсходныеДанные КАК ВТ
        |       ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.ЦеныНоменклатуры.СрезПоследних(&Период, Номенклатура В (ВЫБРАТЬ ВТ_ИсходныеДанные.Номенклатура ИЗ ВТ_ИсходныеДанные)) КАК ЦеныНоменклатурыСрезПоследних
        |       ПО ВТ.Номенклатура = ЦеныНоменклатурыСрезПоследних.Номенклатура";
        
    Запрос.УстановитьПараметр("Период", ТекущаяДата());
    
    Возврат Запрос.Выполнить().Выгрузить();
    
КонецФункции

Этот запрос отработает за доли секунды вместо нескольких минут, так как выборка данных и их соединение с ценами происходят непосредственно на уровне СУБД.

Шаг 2. Отключаем фоновое выполнение отчета на время отладки

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

  1. Через параметры формы отчета: В модуле формы отчета (если используется управляемая форма) перед вызовом процедуры компоновки передайте параметр, отключающий фоновое выполнение. В типовых конфигурациях на БСП за это обычно отвечает параметр структуры параметров отчета ВыполнятьВФоновомРежиме = Ложь.
  2. Запуск в режиме Толстого клиента: Откройте базу данных под толстым клиентом в режиме обычного приложения. В этом режиме отчеты чаще всего формируются синхронно в основном потоке, что полностью решает проблему таймаутов при отладке.

Шаг 3. Контроль расхода оперативной памяти

Передача больших таблиц значений (ТЗ) в качестве внешнего набора данных СКД через параметр ВходящиеДанные требует размещения всей этой таблицы в оперативной памяти сервера приложений 1С. Если вы обрабатываете сотни тысяч строк, рассмотрите возможность полного переноса логики получения данных в саму схему компоновки (в конструктор запроса СКД). Использование встроенных запросов СКД позволяет платформе оптимизировать выборку, накладывать системные отборы на уровне базы данных и не загружать лишние данные в оперативную память сервера.

Проанализировав логику работы платформы и применив эти оптимизации, мы получим стабильно работающий отчет, который формируется быстро и предсказуемо ведет себя под отладчиком.

← На главную