При настройке регулярного обмена данными между информационными базами 1С через планы обмена разработчики нередко сталкиваются с ситуацией, когда процесс выгрузки в интерактивном режиме выполняется без замечаний, но намертво зависает при запуске в фоновом или регламентном задании. При этом в журнале регистрации и протоколе обмена нет никаких сообщений об ошибках, а выполнение останавливается на попытке чтения очередного объекта.
Разберем детально, почему возникает подобное зависание, как локализовать проблемный объект метаданных и какими способами можно гарантированно обойти эту проблему.
Рассмотрим типовой фрагмент программного кода, который часто применяется в обработках выгрузки данных:
ВыборкаИзменений = ПланыОбмена.ВыбратьИзменения(ЗаписьСообщения.Получатель, ЗаписьСообщения.НомерСообщения, МассивВыгружаемыхМетаданных);
Пока ВыборкаИзменений.Следующий() Цикл
ЗаписатьВПротоколВыполнения("Метка 1");
Данные = ВыборкаИзменений.Получить();
ЗаписатьВПротоколВыполнения("Метка 2");
// Дальнейшая обработка и сериализация объекта
КонецЦикла;
Метод ВыборкаИзменений.Получить() возвращает объект данных целиком из информационной базы (например, ДокументОбъект со всеми его табличными частями). В фоновом сеансе регламентного задания этот вызов может приводить к бесконечному ожиданию по следующим причинам:
ПланыОбмена.ВыбратьИзменения() монопольно фиксирует номера сообщений в таблицах регистрации изменений. При последующем вызове Получить() платформа считывает данные из основных таблиц документа и его табличных частей. Если в ночное время параллельно выполняются другие регламентные задачи (индексация, пересчет итогов, фоновое проведение), возникает перекрестная блокировка (Deadlock или Lock Escalation) между таблицами изменений и физическими таблицами СУБД.rphost может блокировать поток при попытке десериализации тяжелого объекта в память.Для выявления конкретного типа данных, на котором останавливается выполнение, разобьем выгрузку на отдельные итерации по метаданным. Выясним, какой именно документ или справочник вызывает зависание:
ПланыОбмена.ВыбратьИзменения(), обрабатывая каждый объект метаданных по отдельности.Наиболее надежный и производительный способ решения проблемы — полный отказ от метода ВыборкаИзменений.Получить() в пользу выборки ссылок прямым запросом к виртуальной таблице изменений. Рассмотрим реализацию данного подхода по шагам:
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Изм.Ссылка КАК Ссылка
|ИЗ
| Документ.ПриходнаяНакладная.Изменения КАК Изм
|ГДЕ
| Изм.Узел = &УзелОбмена";
Запрос.УстановитьПараметр("УзелОбмена", ЗаписьСообщения.Получатель);
РезультатЗапроса = Запрос.Выполнить();
Выборка = РезультатЗапроса.Выбрать();
МассивСсылок = Новый Массив;
Пока Выборка.Следующий() Цикл
// Выгружаем данные по ссылке или сериализуем объект
ВыгрузитьПоПравилам(Выборка.Ссылка);
МассивСсылок.Добавить(Выборка.Ссылка);
КонецЦикла;
// После успешной выгрузки очищаем регистрацию изменений
Если МассивСсылок.Количество() > 0 Тогда
ПланыОбмена.УдалитьРегистрациюИзменений(ЗаписьСообщения.Получатель, МассивСсылок);
КонецЕсли;
Такой подход исключает неявные монопольные блокировки всей таблицы регистрации, не требует загрузки полных объектов в память без необходимости и стабильно отрабатывает в любых фоновых заданиях.
Если требуется сохранить стандартный механизм выгрузки по правилам обмена через объект ЗаписьСообщения, применим порционную выборку. Метод ПланыОбмена.ВыбратьИзменения() позволяет передавать массив конкретных ссылок третьим параметром:
// Считываем порцию ссылок (например, по 200 штук)
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ ПЕРВЫЕ 200
| Изм.Ссылка КАК Ссылка
|ИЗ
| Документ.ПриходнаяНакладная.Изменения КАК Изм
|ГДЕ
| Изм.Узел = &УзелОбмена
| И Изм.НомерСообщения ЕСТЬ NULL";
Запрос.УстановитьПараметр("УзелОбмена", ЗаписьСообщения.Получатель);
МассивСсылок = Запрос.Выполнить().Выгрузить().ВыгрузитьКолонку("Ссылка");
Если МассивСсылок.Количество() > 0 Тогда
ВыборкаИзменений = ПланыОбмена.ВыбратьИзменения(ЗаписьСообщения.Получатель, ЗаписьСообщения.НомерСообщения, МассивСсылок);
Пока ВыборкаИзменений.Следующий() Цикл
Данные = ВыборкаИзменений.Получить();
// Сериализация и запись в сообщение
КонецЦикла;
КонецЕсли;
Обработка данных небольшими порциями предотвращает эскалацию блокировок таблиц на стороне СУБД и снижает нагрузку на оперативную память рабочего процесса сервера 1С.
Чтобы минимизировать риски зависания фоновых обменов, выполним следующие профилактические шаги:
_ChngR в базе данных SQL).rphost.