В практике разработки на платформе 1С:Предприятие 8.3 часто возникает задача оперативной передачи данных между системами. Одним из первых приходящих на ум решений является использование механизма подписок на события. Однако неправильное использование этого инструмента может привести к трудноуловимым ошибкам, таким как «Ошибка SDBL: Открытых транзакций нет». Рассмотрим подробнее, почему это происходит, проанализируем типичные ошибки в коде и определим наиболее эффективные стратегии интеграции.
Ошибка SDBL: Открытых транзакций нет при выполнении запроса к ресурсу /e1cib/logForm (или любому другому) обычно сигнализирует о том, что транзакция, в рамках которой выполнялось действие, была аварийно завершена или «разрушена» на уровне сервера баз данных, хотя код приложения все еще пытается к ней обратиться. Выясним причину этого явления в контексте подписок на события.
Когда мы создаем подписку на событие ПриЗаписи, наш код выполняется внутри неявной транзакции записи объекта. Платформа ожидает, что все действия внутри этой транзакции будут быстрыми и надежными. Если внутри подписки вызывается сложный механизм, такой как обработка УниверсальныйОбменДаннымиXML, происходит следующее:
СтраныМира или АдресныеСведения), БСП запускает сложные фоновые процессы. Наложение вашей подписки на этот процесс создает конфликт блокировок, который система интерпретирует как потерю транзакционного контекста.Рассмотрим код, который часто становится причиной проблем. В данном примере разработчик пытается выполнить полную инициализацию правил обмена при каждой записи элемента:
Процедура ВыгрузкаВЕрпСтраныМира() Экспорт
Узел = ПланыОбмена.ВыгрузкаВЕрпСтраныМира.НайтиПоКоду("ЕРП");
Выборка = ПланыОбмена.ВыбратьИзменения(Узел, Узел.НомерОтправленного);
Если Выборка.Следующий() Тогда
// ... формирование пути к файлу ...
Обработка = Обработки.УниверсальныйОбменДаннымиXML.Создать();
Обработка.РежимОбмена = "Выгрузка";
Обработка.ИмяФайлаПравилОбмена = ПутьНаДиске + "ПравилаОбмена.xml";
// КРИТИЧЕСКАЯ ОШИБКА: Парсинг XML правил внутри транзакции записи
Обработка.ЗагрузитьПравилаОбмена();
// ... настройка правил выгрузки ...
Обработка.ВыполнитьВыгрузку();
КонецЕсли;
КонецПроцедуры
Почему такой подход считается «антипаттерном»? Выясним основные причины:
ЗагрузитьПравилаОбмена() заставляет сервер 1С каждый раз читать файл, разбирать его структуру и строить дерево объектов в памяти. Если 10 пользователей одновременно сохранят элементы, сервер может зависнуть из-за нехватки ресурсов.ВыбратьИзменения внутри подписки ПриЗаписи конкретного объекта — это логическая ошибка. Вместо того чтобы выгрузить одну записываемую страну, система пытается выгрузить все накопленные изменения по плану обмена, которые могли там накопиться за день.Это самый надежный и «каноничный» способ. Мы разделяем процесс записи данных и процесс их передачи. Посмотрим на алгоритм действий:
ПланОбмена с включенной Авторегистрацией для нужных справочников. Платформа сама будет помечать измененные объекты, что происходит мгновенно и без ошибок.РегламентноеЗадание, которое запускается с периодичностью (например, раз в минуту или раз в 10 секунд, если нужна высокая оперативность).Это исключает риск возникновения ошибки SDBL, так как выгрузка происходит в отдельном сеансе, не связанном с транзакцией записи пользователя.
Если бизнес-требования диктуют необходимость передать данные буквально в ту же секунду, используем асинхронный вызов через ФоновыеЗадания. Рассмотрим пример реализации в подписке на событие:
// Процедура-обработчик подписки на событие ПриЗаписи
Процедура ПриЗаписиСтраныМираПодписка(Источник, Отказ) Экспорт
Если Отказ Тогда Возврат; КонецЕсли;
// Вместо выполнения обмена здесь, запускаем фоновый поток
ПараметрыЗадания = Новый Массив;
ПараметрыЗадания.Добавить(Источник.Ссылка);
ФоновыеЗадания.Выполнить("МойОбщийМодуль.ВыполнитьВыгрузкуОбъектаФоново", ПараметрыЗадания);
КонецПроцедуры
В чем преимущество? Рассмотрим ситуацию: пользователь нажимает «Записать», транзакция в базе данных завершается за миллисекунды, управление возвращается в интерфейс. В это время на сервере создается отдельный поток, который спокойно (вне транзакции пользователя) подготавливает XML и отправляет его в ЕРП. Если в фоновом задании произойдет ошибка (например, занят файл), это никак не помешает пользователю работать дальше.
Если вы все же решили вызывать выгрузку программно (но желательно в фоновом режиме), проанализируем, как ускорить этот процесс. Главная «точка торможения» — это правила обмена. Посмотрим, как можно избежать постоянной загрузки правил:
Создадим в общем модуле переменную для хранения структуры правил или используем ПараметрыСеанса. Однако лучший способ — использование специализированных методов БСП, если конфигурация построена на ней. Если же используется чистая обработка, то никогда не вызывайте ЗагрузитьПравилаОбмена() внутри циклов или подписок. Постарайтесь вынести этот объект на уровень модуля с длительным временем жизни.
При возникновении ошибки SDBL: Открытых транзакций нет в подписках, первым делом проверьте, нет ли внутри кода обращений к внешним ресурсам или тяжелым обработкам. Для исправления ситуации следуйте правилам:
ПриЗаписи — только для проверки данных и их фиксации в базе.Соблюдение этих архитектурных принципов не только избавит вас от ошибок транзакций, но и сделает систему масштабируемой и устойчивой к высоким нагрузкам.