Как правильно реализовать фоновое проведение тяжелых документов в 1С

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

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

Проблема передачи объекта в фоновое задание

Рассмотрим базовую ситуацию: разработчик пытается передать ДокументОбъект напрямую в параметры фонового задания. Программа выдает ошибку, так как параметры фонового задания должны быть сериализуемыми. Мутабельные объекты, к которым относится ДокументОбъект, не могут быть переданы из одного сеанса в другой (фоновое задание запускается в отдельном сеансе на сервере).

Проанализируем причину этого ограничения. Объект в памяти сервера «привязан» к контексту текущего сеанса. Если бы платформа позволяла передавать ссылку на один и тот же экземпляр объекта в другой поток, это привело бы к непредсказуемым конфликтам при одновременном изменении свойств. Поэтому в фоновое задание мы можем передать только данные, которые можно преобразовать в поток байтов (сериализовать).

Способ 1: Передача ссылки (Самый простой и надежный)

Если документ уже записан в базу данных, нам нет необходимости передавать весь объект. Достаточно передать ДокументСсылка. Рассмотрим алгоритм действий:

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

Пример реализации в модуле менеджера или общем модуле:


Процедура ВыполнитьФоновоеПроведение(Параметры) Экспорт
    СсылкаНаДокумент = Параметры.Ссылка;
    ДокументОбъект = СсылкаНаДокумент.ПолучитьОбъект();
    
    Если ДокументОбъект <> Неопределено Тогда
        // Обязательно используем попытку, так как объект может быть заблокирован
        Попытка
            ДокументОбъект.Записать(РежимЗаписиДокумента.Проведение);
        Исключение
            ЗаписьЖурналаРегистрации("ФоновоеПроведение", УровеньЖурналаРегистрации.Ошибка, , , ПодробноеПредставлениеОшибки(ИнформацияОбОшибке()));
        КонецПопытки;
    КонецЕсли;
КонецПроцедуры

Способ 2: Сериализация объекта (Если документ еще не в базе)

Бывают случаи, когда нам нужно провести документ, данные которого еще не зафиксированы в базе (или мы не хотим их записывать в основной транзакции). Здесь поможет механизм сериализации в ДвоичныеДанные или использование XDTO. Как отмечалось в обсуждении, такой подход используется в некоторых типовых решениях, например в ЗУП.

Разберем, как это работает: мы превращаем объект в набор данных, передаем его в фон, а там «собираем» новый экземпляр объекта. Важно понимать: это будет другой экземпляр объекта, хотя и с теми же данными.


// На стороне клиента/основного сервера:
ДанныеОбъекта = СериализоватьОбъект(ДокументОбъект); // Используем средства платформы или БСП
ПараметрыЗадания = Новый Массив;
ПараметрыЗадания.Добавить(ДанныеОбъекта);
ФоновыеЗадания.Выполнить("МойМодуль.ПровестиИзДанных", ПараметрыЗадания);

// В фоновом задании:
Процедура ПровестиИзДанных(ДанныеОбъекта) Экспорт
    ДокументОбъект = ДесериализоватьОбъект(ДанныеОбъекта);
    ДокументОбъект.Записать(РежимЗаписиДокумента.Проведение);
КонецПроцедуры

Проблема транзакционного разрыва

Одной из самых коварных ошибок при фоновом проведении является ситуация, когда фоновое задание запускается до того, как основная транзакция записи документа завершена. Если вы вызываете фон в процедуре ПриЗаписи, фоновое задание запустится мгновенно в другом сеансе. Однако, пока ваша основная транзакция не сделала Commit, фоновое задание «не видит» созданный документ или видит его старую версию.

Выясним, как этого избежать. Рассмотрим два варианта:

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

Почему не записываются движения? Разбор ошибки

Рассмотрим ситуацию из сообщения №20: программист передал ссылку, получил объект, добавил записи в наборы, но в базе они не появились. Проанализируем возможные причины:

  1. Свойство Записывать: Для каждого регистра в коллекции Движения необходимо явно установить флаг: Объект.Движения.ИмяРегистра.Записывать = Истина;. Без этого флага платформа не сохранит изменения в этом регистре при вызове метода Записать().
  2. Режим записи документа: Если в фоновом задании вызывается просто Объект.Записать() без параметров, система отрабатывает в режиме Запись, при котором движения обычно не формируются (зависит от настроек проведения в метаданных). Всегда используйте РежимЗаписиДокумента.Проведение.
  3. Очистка движений: Если у документа в метаданных установлено «Удалять движения автоматически», то при начале проведения старые движения будут удалены. Если вы пишете свой алгоритм формирования движений, убедитесь, что он не конфликтует со стандартным механизмом ОбработкаПроведения.

Рекомендации по оптимизации: Паттерн «Очередь»

Для действительно тяжелых систем простого запуска фонового задания может быть недостаточно. Большое количество одновременно запущенных заданий может «положить» сервер приложений. Посмотрим на более продвинутый вариант — использование Очереди проведения.

Схема работы выглядит так:

Это позволяет сглаживать пиковые нагрузки и гарантирует, что система останется отзывчивой для пользователей даже в моменты массового ввода документов.

Использование Временного хранилища

Если вам нужно передать в фон очень большой объем данных, который не хочется «гонять» через параметры (чтобы не раздувать память сеанса), рассмотрим вариант с Временным хранилищем. Важно помнить, что по умолчанию временное хранилище привязано к сеансу. Чтобы фоновое задание могло получить к нему доступ, при помещении данных нужно использовать уникальный идентификатор фонового задания (или передавать адрес, созданный с привязкой к форме, но это сложнее).

Пример передачи через хранилище:


ИдентификаторЗадания = Новый УникальныйИдентификатор;
Адрес = ПоместитьВоВременноеХранилище(ТяжелаяСтруктураДанных, ИдентификаторЗадания);
// Теперь передаем в фоновое задание только Адрес

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

← На главную