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