Как залогировать ошибку из прерванной транзакции проведения?

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

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

Рассмотрим ситуацию: документ должен отправить данные во внешнюю систему (например, в Oracle или через API), и в случае неудачи мы хотим зафиксировать ошибку в специальном регистре «Логирование», но при этом само проведение документа должно прерваться. Разберем по шагам, как решить эту задачу, проанализируем различные подходы и выясним, какой из них наиболее надежен в промышленной эксплуатации.

Проблема «Обреченной транзакции»

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

Даже если мы перехватим ошибку, выполнение команды НаборЗаписей.Записать() внутри блока Исключение либо ничего не даст (так как в конце всё равно произойдет ОтменитьТранзакцию()), либо вызовет новую критическую ошибку.

Решение 1: Использование фоновых заданий

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

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


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

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

Решение 2: Журнал регистрации (ЖР)

Самый простой и встроенный в платформу способ логирования, который часто недооценивают. Метод ЗаписьЖурналаРегистрации() по своей природе является не транзакционным. Это означает, что если вы вызываете запись в ЖР внутри транзакции, и эта транзакция впоследствии откатывается, запись в журнале все равно остается.

Рассмотрим преимущества этого подхода:

  1. Простота: Не нужно создавать новые сеансы (фоновые задания) и тратить лицензии.
  2. Надежность: Запись в ЖР практически никогда не блокирует основную работу.
  3. Стандарт: Это штатный механизм 1С для фиксации событий.

Если вам обязательно нужно иметь данные в регистре сведений для удобства построения отчетов, вы можете использовать комбинированную схему: записывать ошибку в ЖР, а специальным регламентным заданием раз в час «перекладывать» новые записи из ЖР в ваш регистр логов.


Исключение
    ОписаниеОшибки = ОписаниеОшибки();
    ЗаписьЖурналаРегистрации("ОбменДанными", УровеньЖурналаРегистрации.Ошибка, , Ссылка, ОписаниеОшибки);
    Отказ = Истина;
КонецПопытки;

Решение 3: Параметры сеанса (отложенное логирование)

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

Проанализируем алгоритм:

  1. В блоке Исключение обработчика проведения мы записываем структуру с данными ошибки в параметр сеанса (например, в заранее созданный параметр БуферОшибокПроведения типа ХранилищеЗначения).
  2. После того как транзакция завершится и управление вернется на форму документа или в процедуру, вызвавшую запись, мы проверяем этот параметр.
  3. Если параметр не пуст, мы вызываем процедуру записи в регистр. В этот момент транзакция проведения уже закрыта, и новая транзакция записи лога пройдет успешно.

Решение 4: HTTP-сервисы (Внутреннее API)

Для высоконагруженных систем (Enterprise-уровня) хорошим тоном считается использование внешнего или внутреннего HTTP-сервиса для сбора логов. Проанализируем, почему это выгодно:

Когда вы выполняете HTTP-запрос (через HTTPСоединение) к стороннему ресурсу или даже к этой же базе 1С, этот запрос выполняется вне контекста текущей транзакции. Вы передаете JSON с описанием ошибки, и удаленный сервис сохраняет его. Даже если ваша база данных «упадет» сразу после этого, информация об ошибке уже будет зафиксирована на другом сервере или в другом процессе.

Выводы и рекомендации

Подводя итоги, выберем оптимальный путь в зависимости от задачи:

Важное предостережение: Будьте осторожны с фоновыми заданиями при групповом перепроведении документов. Если вы попытаетесь перепровести 10 000 документов и в каждом возникнет ошибка, вызывающая фоновое задание, сервер может временно перестать отвечать из-за лавинообразного создания новых сеансов.

← На главную