При работе с высоконагруженными системами (например, в производственных или крупных торговых конфигурациях) часто возникает проблема длительного проведения документов. Когда документ делает движения по десяткам регистров, пользователь вынужден ждать завершения операции, что снижает общую производительность труда. Логичным решением является вынос процесса проведения в фоновое задание. В этой статье мы подробно разберем, как реализовать этот механизм, какие существуют технологические ограничения и как избежать типичных ошибок при передаче данных между сеансами.
Рассмотрим базовую ситуацию: разработчик пытается передать ДокументОбъект напрямую в параметры фонового задания. Программа выдает ошибку, так как параметры фонового задания должны быть сериализуемыми. Мутабельные объекты, к которым относится ДокументОбъект, не могут быть переданы из одного сеанса в другой (фоновое задание запускается в отдельном сеансе на сервере).
Проанализируем причину этого ограничения. Объект в памяти сервера «привязан» к контексту текущего сеанса. Если бы платформа позволяла передавать ссылку на один и тот же экземпляр объекта в другой поток, это привело бы к непредсказуемым конфликтам при одновременном изменении свойств. Поэтому в фоновое задание мы можем передать только данные, которые можно преобразовать в поток байтов (сериализовать).
Если документ уже записан в базу данных, нам нет необходимости передавать весь объект. Достаточно передать ДокументСсылка. Рассмотрим алгоритм действий:
Записать(РежимЗаписиДокумента.Проведение).Пример реализации в модуле менеджера или общем модуле:
Процедура ВыполнитьФоновоеПроведение(Параметры) Экспорт
СсылкаНаДокумент = Параметры.Ссылка;
ДокументОбъект = СсылкаНаДокумент.ПолучитьОбъект();
Если ДокументОбъект <> Неопределено Тогда
// Обязательно используем попытку, так как объект может быть заблокирован
Попытка
ДокументОбъект.Записать(РежимЗаписиДокумента.Проведение);
Исключение
ЗаписьЖурналаРегистрации("ФоновоеПроведение", УровеньЖурналаРегистрации.Ошибка, , , ПодробноеПредставлениеОшибки(ИнформацияОбОшибке()));
КонецПопытки;
КонецЕсли;
КонецПроцедуры
Бывают случаи, когда нам нужно провести документ, данные которого еще не зафиксированы в базе (или мы не хотим их записывать в основной транзакции). Здесь поможет механизм сериализации в ДвоичныеДанные или использование XDTO. Как отмечалось в обсуждении, такой подход используется в некоторых типовых решениях, например в ЗУП.
Разберем, как это работает: мы превращаем объект в набор данных, передаем его в фон, а там «собираем» новый экземпляр объекта. Важно понимать: это будет другой экземпляр объекта, хотя и с теми же данными.
// На стороне клиента/основного сервера:
ДанныеОбъекта = СериализоватьОбъект(ДокументОбъект); // Используем средства платформы или БСП
ПараметрыЗадания = Новый Массив;
ПараметрыЗадания.Добавить(ДанныеОбъекта);
ФоновыеЗадания.Выполнить("МойМодуль.ПровестиИзДанных", ПараметрыЗадания);
// В фоновом задании:
Процедура ПровестиИзДанных(ДанныеОбъекта) Экспорт
ДокументОбъект = ДесериализоватьОбъект(ДанныеОбъекта);
ДокументОбъект.Записать(РежимЗаписиДокумента.Проведение);
КонецПроцедуры
Одной из самых коварных ошибок при фоновом проведении является ситуация, когда фоновое задание запускается до того, как основная транзакция записи документа завершена. Если вы вызываете фон в процедуре ПриЗаписи, фоновое задание запустится мгновенно в другом сеансе. Однако, пока ваша основная транзакция не сделала Commit, фоновое задание «не видит» созданный документ или видит его старую версию.
Выясним, как этого избежать. Рассмотрим два варианта:
ПослеЗаписи на клиенте. В этот момент транзакция уже завершена, и данные гарантированно доступны в базе.Рассмотрим ситуацию из сообщения №20: программист передал ссылку, получил объект, добавил записи в наборы, но в базе они не появились. Проанализируем возможные причины:
Движения необходимо явно установить флаг: Объект.Движения.ИмяРегистра.Записывать = Истина;. Без этого флага платформа не сохранит изменения в этом регистре при вызове метода Записать().Объект.Записать() без параметров, система отрабатывает в режиме Запись, при котором движения обычно не формируются (зависит от настроек проведения в метаданных). Всегда используйте РежимЗаписиДокумента.Проведение.ОбработкаПроведения.Для действительно тяжелых систем простого запуска фонового задания может быть недостаточно. Большое количество одновременно запущенных заданий может «положить» сервер приложений. Посмотрим на более продвинутый вариант — использование Очереди проведения.
Схема работы выглядит так:
Это позволяет сглаживать пиковые нагрузки и гарантирует, что система останется отзывчивой для пользователей даже в моменты массового ввода документов.
Если вам нужно передать в фон очень большой объем данных, который не хочется «гонять» через параметры (чтобы не раздувать память сеанса), рассмотрим вариант с Временным хранилищем. Важно помнить, что по умолчанию временное хранилище привязано к сеансу. Чтобы фоновое задание могло получить к нему доступ, при помещении данных нужно использовать уникальный идентификатор фонового задания (или передавать адрес, созданный с привязкой к форме, но это сложнее).
Пример передачи через хранилище:
ИдентификаторЗадания = Новый УникальныйИдентификатор;
Адрес = ПоместитьВоВременноеХранилище(ТяжелаяСтруктураДанных, ИдентификаторЗадания);
// Теперь передаем в фоновое задание только Адрес
Подводя итог, отметим: фоновое проведение — это мощный инструмент, но он требует внимательного отношения к блокировкам данных и понимания того, как 1С работает с сеансами и транзакциями. Всегда обрабатывайте исключения, связанные с объектными блокировками, так как пользователь может открыть форму документа именно в тот момент, когда фоновое задание пытается его записать.