В практике разработки на платформе 1С:Предприятие 8.3 часто встречается коварная проблема: программный код, который идеально работает один или два раза, на третий запуск вызывает полное «оцепенение» системы. Особенно это характерно для сложных обработок заполнения, где задействованы одновременно и механизмы компоновки данных (СКД), и прямые запросы к базе данных с использованием временных таблиц. Рассмотрим подробнее, почему так происходит, и разберем по шагам, как диагностировать и устранить подобные зависания.
Типичная ситуация выглядит так: при нажатии на кнопку заполнения документ первый раз обрабатывается за 10 секунд, второй раз за 15, а на третий раз сеанс просто «виснет». При этом замеры производительности показывают странную картину: время выполнения самого отчета остается стабильным, а вот время выполнения последующего запроса растет лавинообразно. Проанализируем ситуацию: если исключить отчет — запрос работает быстро. Если исключить запрос — отчет работает быстро. Вместе они создают критическую нагрузку. Это явный признак того, что проблема кроется не в логике алгоритма, а в управлении ресурсами на стороне сервера приложений (rphost) или СУБД.
Первым делом нам необходимо выяснить, на какой именно строке кода происходит остановка. Для этого выполним следующие действия:
СкомпоноватьРезультат() уходит в бесконечность, значит, проблема в ресурсах сервера.Одной из наиболее вероятных причин является фрагментация оперативной памяти в процессе rphost. Рассмотрим, как это работает: при выполнении отчета СКД и загрузке его в ТаблицаЗначений, платформа выделяет значительные блоки памяти. Когда следом выполняется запрос, использующий МенеджерВременныхТаблиц, серверу СУБД и серверу 1С требуется выделить дополнительные непрерывные блоки памяти для передачи данных.
Даже если мы явно очищаем переменные и уничтожаем временные таблицы, платформа не всегда мгновенно возвращает память операционной системе. На третьей итерации серверу может просто не хватить «чистого» куска памяти, из-за чего система начинает бесконечно перераспределять (свопировать) данные, что и выглядит как зависание. Чтобы убедиться в этом, проанализируем счетчик Private Bytes процесса rphost через монитор ресурсов Windows. Если график растет ступенчато и не падает после завершения процедуры — мы имеем дело с удержанием памяти.
Использование МенеджерВременныхТаблиц напрямую задействует системную базу данных SQL Server — TempDB. Выясним причину возможного замедления на стороне базы данных:
TempDB изменился или таблицы не были корректно очищены на уровне СУБД, SQL Server может решить, что план устарел, и начать перестраивать его в момент выполнения.TempDB.Рекомендуется всегда явно уничтожать временные таблицы, как только они стали не нужны, не дожидаясь закрытия менеджера:
Запрос = Новый Запрос;
Запрос.МенеджерВременныхТаблиц = Новый МенеджерВременныхТаблиц;
// ... выполнение кода с ВТ ...
Запрос.Текст = "УНИЧТОЖИТЬ ВременнаяТаблицаДанных";
Запрос.Выполнить();
Запрос.МенеджерВременныхТаблиц.Закрыть();
Проанализируем ситуацию с точки зрения управляемых форм. Если процедура заполнения вызывается «на сервере» без контекста (&НаСервереБезКонтекста), то передаются только параметры. Но если используется процедура &НаСервере, то при каждом вызове вся форма (весь её контекст) сериализуется и передается между клиентом и сервером.
Если в процессе заполнения данные из отчета или запроса помещаются в реквизиты формы (например, в скрытую ТаблицаЗначений или ДеревоЗначений), «вес» формы растет. На третьей итерации объем передаваемых данных может достичь критического предела, когда процесс сериализации начинает занимать всё процессорное время, вызывая эффект зависания интерфейса. Проверьте, не накапливаются ли данные в реквизитах формы, которые не очищаются перед повторным заполнением.
Одним из самых надежных способов решения проблем с утечкой памяти и зависаниями является вынос тяжелых вычислений в фоновое задание. Разберем по шагам, почему это помогает:
TempDB) гарантированно освобождаются платформой.Пример структуры вызова:
ПараметрыЗадания = Новый Структура("Параметр1, Параметр2", Значение1, Значение2);
ФоновоеЗадание = ФоновыеЗадания.Выполнить("МойМодуль.ЗаполнитьДанныеНаСервере", ПараметрыЗадания);
ФоновоеЗадание.ОжидатьЗавершения();
// Получаем результат через временное хранилище
Если вышеперечисленные методы не помогли сразу, необходимо настроить Технологический Журнал (ТЖ). Рассмотрим, на какие события следует обратить внимание в конфигурационном файле logcfg.xml:
DBMSSQL — покажет реальные запросы к СУБД и время их выполнения. Мы увидим, зависает ли сам SQL.CALL — зафиксирует входящие и исходящие серверные вызовы. Поможет понять, на каком этапе «застревает» выполнение (до или после работы кода).WAITSETTLE — покажет ожидания на блокировках ресурсов.Важный совет: Попробуйте изменить порядок выполнения. Если зависание происходит на том объекте, который идет вторым (будь то отчет или запрос), значит, первый объект не освободил ресурсы. Попробуйте обнулять объект отчета СКД сразу после выгрузки в таблицу значений, присваивая ему Неопределено.
Подводя итог, можно сказать, что проблема «третьего раза» — это почти всегда проблема накопления неявных данных. Либо это разрастание TempDB, либо фрагментация памяти rphost, либо переполнение контекста формы. Использование фоновых заданий и строгий контроль за уничтожением временных таблиц — лучшие инструменты в борьбе с такой нестабильностью.