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