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