При настройке или доработке правил обмена данными разработчики нередко сталкиваются с ситуацией, когда ошибка времени выполнения падает на совершенно корректном и типовом участке кода — например, при обычном чтении регистра сведений или системном запросе к версии конфигурации. При этом в журнале регистрации или окне сообщений фиксируется статус: «В данной транзакции уже происходили ошибки».
Разберем детально, почему возникает это поведение, как устроена работа с транзакциями в платформе 1С:Предприятие и как правильно организовать загрузку и проведение документов при обмене.
Проанализируем архитектуру работы с транзакциями в 1С. В платформе используется модель плоских (псевдовложенных) транзакций. Когда вызывается метод НачатьТранзакцию(), а внутри него повторно открывается еще одна транзакция, счетчик уровня вложенности (функция ТранзакцияАктивна()) увеличивается, однако на уровне СУБД физическая транзакция открыта ровно одна.
В типовых механизмах обмена (включая подсистемы БСП и универсальную обработку обмена данными) чтение и запись порций объектов по умолчанию выполняются внутри общей внешней транзакции. Если при записи объекта происходит программная ошибка, поведение платформы следующее:
Объект.Записать(РежимЗаписиДокумента.Проведение) инициирует внутреннюю транзакцию проведения.Попытка ... Исключение перехватывает само исключение встроенного языка, но не восстанавливает транзакцию СУБД. Снять признак ошибки транзакции программно невозможно.Запрос.Выполнить(), чтение системных регистров или обращение к другим объектам) в рамках незакрытой внешней транзакции немедленно падает с аварийной ошибкой времени выполнения.Посмотрим на пример кода в обработчике правил конвертации («После загрузки»), который приводит к этой проблеме:
Попытка
Объект.Записать(РежимЗаписиДокумента.Проведение);
Исключение
Объект.Проведен = Ложь;
Сообщить("Документ: " + Объект + " не проведен. Ошибка ПКО: РТУ-ПослеЗагрузки");
КонецПопытки;
В приведенном фрагменте попытка перехватить ошибку проведения создает иллюзию безопасной обработки. На практике же вся внешняя транзакция обмена становится заблокированной, и следующий же системный вызов падает.
Рассмотрим правильные варианты архитектурной реализации записи и проведения документов при обмене данными.
В обработчиках конвертации (например, «После загрузки» или «При загрузке») не следует вызывать проведение документов напрямую через РежимЗаписиДокумента.Проведение. Загрузку необходимо выполнять исключительно в режиме записи данных:
// В обработчике "После загрузки" выполняем только запись объекта
Объект.Записать(РежимЗаписиДокумента.Запись);
Если документ при загрузке не прошел критические проверки и не должен оставаться в базе данных, следует передать платформе отказ от обработки, установив флаг Отказ = Истина. Это прервет обработку текущего объекта штатным образом, не разрушая целостность транзакционного контекста.
В современных конфигурациях для минимизации блокировок и предотвращения транзакционных сбоев реализован механизм отложенного проведения:
Если обмен настраивается вручную или через собственные алгоритмы, разделим процесс на два изолированных этапа:
Посмотрим на пример такого алгоритма:
// Этап 1: Фиксация транзакции загрузки объектов
ЗафиксироватьТранзакцию();
// Этап 2: Проведение каждого документа в изолированном контексте
Для Каждого СсылкаНаДокумент Из МассивЗагруженныхДокументов Цикл
ДокументОбъект = СсылкаНаДокумент.ПолучитьОбъект();
Если ДокументОбъект <> Неопределено Тогда
Попытка
ДокументОбъект.Записать(РежимЗаписиДокумента.Проведение);
Исключение
// Здесь транзакция проведения изолирована, ошибка не нарушает работу цикла
ЗаписьЖурналаРегистрации("ОбменДанными.Проведение",
УровеньЖурналаРегистрации.Предупреждение,
ДокументОбъект.Метаданные(),
СсылкаНаДокумент,
ОписаниеОшибки());
КонецПопытки;
КонецЕсли;
КонецЦикла;
Когда первопричина сбоя маскируется последующим падением запросов, используем следующие методы поиска истинного источника ошибки:
Попытка, до того как транзакция будет помечена испорченной.Количество объектов в одной транзакции = 1. Это локализует конкретный объект и строку файла обмена, вызывающую сбой.EXCP и EXCPCNTX. В логе технологического журнала будет отражен точный контекст и текст первого возникшего исключения.