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