Почему в фоновом задании возникает ошибка «Объект не найден» при передаче ссылки из транзакции и как это исправить?

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

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

Причина возникновения проблемы: Механизм изоляции транзакций

Для того чтобы понять суть ошибки, нам необходимо рассмотреть, как 1С взаимодействует с системой управления базами данных (СУБД). Любая запись данных в 1С (будь то метод Записать() или выполнение кода внутри блока НачатьТранзакцию() ... ЗафиксироватьТранзакцию()) подчиняется правилам ACID (атомарность, согласованность, изолированность, долговечность).

Разберем по шагам, что происходит в системе:

  1. Основной сеанс: Открывает транзакцию и записывает документ. В этот момент данные попадают в таблицы СУБД, но они имеют статус «грязных» или незафиксированных данных.
  2. Запуск фонового задания: Команда ФоновыеЗадания.Выполнить() порождает новый поток на сервере приложений 1С. Важно понимать: фоновое задание — это абсолютно другой сеанс со своим уникальным номером и отдельным соединением с базой данных.
  3. Уровень изоляции: По умолчанию 1С работает с уровнем изоляции Read Committed (чтение зафиксированных данных). Это означает, что любой сеанс (в нашем случае — фоновое задание) не имеет права видеть данные, которые были изменены или созданы в другой транзакции, пока та не будет завершена командой ЗафиксироватьТранзакцию().

Таким образом, когда фоновое задание пытается получить объект по переданной ссылке ДокументОбъект.Ссылка, СУБД честно отвечает, что такой записи в таблице еще нет (она скрыта для всех, кроме владельца транзакции). В результате 1С формирует битую ссылку с текстом <Объект не найден>.

Почему один документ «виден», а другой — нет?

В исходной ситуации мы видим, что первый документ в фоновом задании не находится, а второй — обрабатывается нормально. Это классический пример состояния гонки (race condition). Проанализируем временную шкалу событий:

Фоновое задание инициализируется не мгновенно. Платформе требуется время на запуск нового сеанса, проверку прав доступа и загрузку конфигурации в память. Пока создавалось и запускалось первое задание, транзакция в основном сеансе еще продолжалась. Но к моменту, когда инициализировалось второе фоновое задание, основной сеанс мог уже дойти до конца кода и выполнить автоматическую фиксацию транзакции. Именно поэтому второе задание «успело» увидеть данные, а первое — нет.

Опасность кэширования «битых» ссылок

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

Разбор неэффективных методов решения

Рассмотрим популярные, но ошибочные попытки решить проблему:

Рекомендуемое решение №1: Вынос запуска за пределы транзакции

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


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

Рекомендуемое решение №2: Использование обработчика «ПослеЗаписи»

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

Рекомендуемое решение №3: Передача данных через структуру

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

Однако, как мы проанализировали ранее, если цель фонового задания — проведение документа, этот метод не сработает, так как процедура проведения обязана работать с объектом, хранящимся в БД.

Особенности работы с PostgreSQL

Стоит отметить, что на СУБД PostgreSQL проблема «невидимости» ссылок проявляется более стабильно и жестко, чем на MS SQL Server. Это связано с механизмом MVCC (Multiversion Concurrency Control). PostgreSQL создает «снимок» данных (snapshot) для нового сеанса, и пока транзакция в первом сеансе не завершена, второй сеанс даже при повторных обращениях может не увидеть изменений. Поэтому в средах с PostgreSQL строгое соблюдение порядка «сначала фиксация — потом фоновый запуск» является критически важным.

Подведем итог: всегда разделяйте во времени процессы записи данных и процессы их параллельной обработки в других сеансах. Это избавит вас от трудноуловимых ошибок и обеспечит стабильную работу системы.

← На главную