Как решить ошибку «В данной транзакции уже произошли ошибки» при получении сообщений через 1С:Шину?

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

Рассмотрим распространенную проблему, с которой сталкиваются разработчики при написании обменов между базами данных с использованием сервисов интеграции (в частности, 1С:Шина). Ситуация выглядит следующим образом: мы получаем сообщение с помощью обработчика ОбработкаПолученияСообщения, десериализуем данные в XDTO-объект и начинаем обходить пакет, создавая или изменяя объекты в базе. Так как при записи могут возникать ошибки, мы оборачиваем код в конструкцию Попытка ... Исключение ... КонецПопытки. Однако при первой же ошибке записи любого объекта все последующие обращения к базе данных завершаются критической ошибкой: «В данной транзакции уже произошли ошибки».

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

Почему возникает ошибка: механика «битых» транзакций в 1С

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

Когда мы пытаемся записать объект базы данных внутри этого обработчика, открывается так называемая «вложенная» транзакция (по сути, просто увеличивается счетчик вложенности единой транзакции). Если при записи объекта происходит ошибка уровня СУБД (например, нарушение уникальности, блокировка, или программный отказ в обработчике ПередЗаписью), платформа помечает всю глобальную транзакцию как «обреченную» (doomed).

Даже если мы перехватим ошибку с помощью блока Попытка:


Попытка
    ДокументОбъект.Записать(РежимЗаписиДокумента.Проведение);
Исключение
    // Логируем ошибку, пытаемся продолжить цикл
КонецПопытки;

Платформа уже зафиксировала факт фатального сбоя. Любая последующая попытка прочитать или записать данные в этой же транзакции приведет к сообщению «В данной транзакции уже произошли ошибки». 1С не поддерживает автономные (независимые) вложенные транзакции, поэтому падение одного объекта «отравляет» весь пакет.

Решение 1: Отключение параметра «В транзакции» в настройках канала

Самое быстрое и простое решение, которое заложено в функционал сервисов интеграции — это изменение настроек канала.

У канала сервиса интеграции в конфигураторе есть специальное свойство ВТранзакции (In Transaction). По умолчанию этот флаг установлен. Это означает, что платформа гарантирует доставку сообщения (принцип Exactly-once или At-least-once). Если в коде происходит ошибка, транзакция откатывается, сообщение не помечается как прочитанное и возвращается в очередь 1С:Шины для повторной попытки.

Посмотрим, что произойдет, если мы снимем этот флаг:

  1. Обработчик ОбработкаПолученияСообщения будет выполняться вне глобальной автоматической транзакции платформы.
  2. Мы сможем внутри цикла обработки пакета XDTO самостоятельно управлять транзакциями для каждого отдельного объекта.
  3. Ошибка при записи одного элемента не сломает процесс сохранения остальных элементов пакета.

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

Решение 2: Использование буферной таблицы (Staging Table)

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

Разберем алгоритм по шагам:

  1. Получение сообщения: В обработчике ОбработкаПолученияСообщения мы не парсим сложный XDTO и не создаем объекты. Мы просто сохраняем полученный пакет «как есть» в промежуточный регистр сведений (например, ОчередьВходящихСообщений), используя тип ХранилищеЗначения.
  2. Быстрая запись: Сохранение одной простой записи в регистр занимает доли секунды и практически никогда не вызывает ошибок блокировок или бизнес-логики. Транзакция Шины успешно завершается.
  3. Фоновая обработка: Настраиваем регламентное задание, которое регулярно проверяет наш буферный регистр.
  4. Создание объектов: Фоновое задание читает XDTO из регистра, десериализует его и пытается записать каждый объект. Здесь мы можем открывать отдельные транзакции для каждого элемента. Если какой-то документ не провелся, мы помечаем только эту запись регистра статусом «Ошибка» и выводим пользователю в специальное рабочее место для ручного разбора.

Дополнительные рекомендации по интеграции

Если вы все же решили обрабатывать данные на лету, обратите внимание на следующие нюансы:

1. Дробление сообщений. Если пакет содержит тысячи объектов, то при включенной настройке ВТранзакции сбой на 999-м объекте откатит все предыдущие. Рекомендуется дробить сообщения на стороне отправителя: отправляйте по одному логическому объекту или небольшими пакетами (по 10-50 элементов).

2. Использование режима загрузки. При создании объектов из обмена разработчики часто пренебрегают свойством ОбменДанными.Загрузка = Истина. Если это свойство не установить, при записи объекта будут срабатывать все обработчики, подписки на события и сложные проверки бизнес-логики (например, контроль остатков). Это многократно увеличивает риск прерывания транзакции. Если ваша архитектура это позволяет, используйте режим загрузки:


НовыйОбъект = Справочники.Номенклатура.СоздатьЭлемент();
// Заполнение реквизитов...
НовыйОбъект.ОбменДанными.Загрузка = Истина;
НовыйОбъект.Записать();

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

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

← На главную