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