Как в фоновом задании дождаться завершения транзакции проведения документа

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

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

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

Решение 1: Контроль изменения версии данных

Наиболее надежный и технически элегантный способ — это использование системного реквизита ВерсияДанных. Этот реквизит представляет собой BinaryData, который автоматически обновляется платформой при любой записи объекта в базу данных. Основная идея заключается в том, чтобы передать текущую (новую) версию данных документа в фоновое задание и заставить его ждать, пока в основной базе версия не станет соответствовать переданной.

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

  1. В подписке на событие ПриЗаписи (или в обработке проведения) мы получаем актуальную версию данных объекта.
  2. Запускаем фоновое задание, передавая ему ссылку на документ и эту версию.
  3. Внутри фонового задания в цикле опрашиваем базу данных.

Рассмотрим пример кода для реализации этой логики. Сначала подготовим процедуру в фоновом задании:


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

Важный нюанс: метод ЗначениеРеквизитаОбъекта должен выполнять запрос к базе данных без использования кэша, чтобы каждый раз получать актуальное состояние из SQL-хранилища. В условиях уровня изоляции Read Committed (который используется в 1С по умолчанию) фоновое задание не увидит новую версию, пока основная транзакция не завершится командой COMMIT.

Решение 2: Использование управляемых блокировок для ожидания

Проанализируем альтернативный метод, который позволяет избежать циклов с паузами. Мы можем использовать механизм управляемых блокировок платформы 1С. Когда документ проводится, платформа устанавливает на него исключительную управляемую блокировку. Если фоновое задание попытается установить блокировку на этот же объект, оно будет переведено сервером в состояние ожидания.

Рассмотрим, как реализовать этот подход:


Блокировка = Новый БлокировкаДанных;
ЭлементБлокировки = Блокировка.Добавить("Документ.РеализацияТоваровУслуг");
ЭлементБлокировки.УстановитьПараметр("Ссылка", СсылкаНаДокумент);
ЭлементБлокировки.Режим = РежимБлокировкиДанных.Исключительный;

Попытка
    // Метод Заблокировать() "подвиснет" до тех пор, пока транзакция в другом сеансе не завершится
    Блокировка.Заблокировать();
    // Как только мы здесь — значит, блокировка в основном сеансе снята (транзакция завершена)
Исключение
    // Ошибка может возникнуть по таймауту (установка блокировки превысила время ожидания)
    Возврат; 
КонецПопытки;

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

Решение 3: Использование «Очереди заданий» (БСП)

Если ваша конфигурация построена на базе Библиотеки Стандартных Подсистем (БСП), наиболее правильным архитектурным решением будет использование регистра сведений ОчередьЗаданий. Вы просто записываете структуру с данными для выгрузки в этот регистр внутри транзакции проведения.

Проанализируем преимущества этого метода:

Почему нельзя просто использовать «ПослеЗаписи»?

Многие начинающие разработчики полагают, что вызов фонового задания из события формы ПослеЗаписиНаСервере решит проблему. Но это заблуждение. Во-первых, документ может проводиться программно (обработкой, через групповое перепроведение), и тогда события формы не сработают. Во-вторых, даже в форме событие ПослеЗаписиНаСервере все еще может находиться внутри неявной транзакции, инициированной платформой.

Выясним причину, по которой «онлайн» выгрузка иногда вредна. Если в системе одновременно проводятся десятки документов (массовое поступление или реализация), запуск фонового задания на каждый «чих» может привести к взрывному росту нагрузки на сервер и блокировкам во внешней системе. В таких случаях мы рекомендуем использовать регламентное задание с частотой раз в 5–10 секунд, которое будет собирать все изменения из плана обмена или специального регистра и отправлять их «пачкой».

Резюме и рекомендации

Подведем итоги нашего исследования:

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

Используя предложенные методы, вы сможете построить надежную систему интеграции, которая будет оперативно обновлять данные во внешних системах, не создавая при этом проблем с блокировками и производительностью основной базы 1С.

← На главную