Как записать данные в базу 1С, если основная транзакция была отменена?

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

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

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

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

Прежде всего, проанализируем ситуацию с технической точки зрения. Когда мы работаем в подписке на событие или в модуле объекта ПередЗаписью, мы уже находимся внутри транзакции, открытой платформой. Любая попытка записи в базу данных (через НаборЗаписей или МенеджерЗаписи) становится частью этой же транзакции. Если выполнение кода прерывается установкой флага Отказ, СУБД возвращает состояние базы данных к моменту до начала транзакции. Именно поэтому стандартные методы записи внутри того же сеанса не работают.

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

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

Разберем по шагам, как это реализовать:

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

Рассмотрим пример кода для запуска такого задания:


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

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

Проанализируем структуру процедуры в общем модуле КонтрольДанных:


Процедура ЗаписатьОшибкиВРегистр(Знач МассивПараметров) Экспорт
    СсылкаНаОбъект = МассивПараметров[0];
    ОписаниеОшибки = МассивПараметров[1];
    
    МенеджерЗаписи = РегистрыСведений.ЛогОшибок.СоздатьМенеджерЗаписи();
    МенеджерЗаписи.Период = ТекущаяДата();
    МенеджерЗаписи.Объект = СсылкаНаОбъект;
    МенеджерЗаписи.ТекстОшибки = ОписаниеОшибки;
    МенеджерЗаписи.Записать();
КонецПроцедуры

Решение 2: Вызов собственного HTTP-сервиса (Self-call)

Для высоконагруженных систем или случаев, когда нужно гарантировать немедленную фиксацию данных, можно использовать механизм HTTP-сервисов. Вызов HTTP-сервиса «самого себя» создает новый независимый контекст выполнения.

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

  1. Полная изоляция: HTTP-запрос обрабатывается как внешний запрос, что гарантирует отсутствие связи с текущей транзакцией.
  2. Передача сложных структур: Данные можно упаковать в JSON, что позволяет передать гораздо больше информации о состоянии объекта в момент ошибки.

Выясним, как это выглядит в коде:


Соединение = Новый HTTPСоединение("localhost", , Логин, Пароль);
Запрос = Новый HTTPЗапрос("/ВашаБаза/hs/Logger/v1/WriteError");
Запрос.УстановитьТелоИзСтроки(ДанныеОшибкиВФорматеJSON);
Ответ = Соединение.ОтправитьДляОбработки(Запрос);

Решение 3: Журнал регистрации

Посмотрим на штатный механизм 1С — Журнал регистрации. Метод ЗаписатьСобытиеЖурналаРегистрации является одним из немногих, которые выполняются вне транзакции по определению. Если данные в регистре сведений вам нужны для последующего автоматизированного анализа, можно применить комбинированный подход:

  1. Записываем ошибку в Журнал регистрации в момент отката.
  2. Настраиваем регламентное задание, которое раз в 10–15 минут вычитывает новые записи из журнала и переносит их в специализированный регистр сведений.

Этот метод позволяет минимизировать нагрузку на сервер, так как запись в текстовый лог (которым по сути является Журнал регистрации) происходит быстрее, чем создание фонового задания или HTTP-вызов.

Решение 4: Запись во внешние файлы

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

Пример простой записи в файл:


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

Позже эти файлы можно обрабатывать отдельным скриптом или тем же регламентным заданием 1С.

Сравнительный анализ методов

Выберем подходящий вариант в зависимости от условий:

Подводя итог, отметим, что выбор метода ФоновыеЗадания.Выполнить является «золотым стандартом» для современного программиста 1С. Он позволяет сохранить логическую целостность системы, не перегружая её лишними внешними зависимостями, и при этом надежно сохранять информацию даже при возникновении критических ошибок бизнес-логики, приводящих к откату транзакции.

← На главную