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