Почему при выполнении запроса возникает ошибка «В данной транзакции уже происходили ошибки»?

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

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

Ошибка выполнения запроса: В данной транзакции уже происходили ошибки!

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

В чем заключается суть механизма сбойной транзакции?

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

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

Даже если разработчик перехватил это первичное исключение с помощью конструкции Попытка ... Исключение, статус сбойной транзакции никуда не исчезает! Платформа блокирует любые последующие операции взаимодействия с СУБД в рамках этого соединения. Первое же последующее обращение к данным — будь то Запрос.Выполнить(), чтение константы через Константы.ИмяКонстанты.Получить() или установка блокировок — будет немедленно прервано исключением «В данной транзакции уже происходили ошибки!».

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

Как найти первичное место падения: пошаговая диагностика

Разберем по шагам, как выявить строку кода, из-за которой ломается транзакция:

  1. Включение остановки по ошибке в конфигураторе. Это самый быстрый и надежный способ локализации проблемы. В меню конфигуратора откроем «Отладка» → «Остановка по ошибке...». Установим флаг «Останавливаться по ошибке», а также обязательно включим признак остановки по ошибкам, обрабатываемым конструкцией Попытка ... Исключение. Запустим проведение документа в режиме отладки. Отладчик прервет выполнение ровно в том месте, где возникает самое первое исключение, еще до того, как оно попадет в блок перехвата.
  2. Анализ технологического журнала. Если воспроизвести проблему в файловой базе или под отладчиком невозможно (например, ошибка воспроизводится только на рабочем сервере), настроим сбор событий технологического журнала EXCP и EXCPCNTX. В журнале будет зафиксирован полный стек вызовов первого падения в момент аварийного состояния транзакции.
  3. Логирование в блоках перехвата. Если в коде конфигурации уже используются блоки Попытка ... Исключение, проверим, чтобы в секции исключения информация о сбое фиксировалась в журнале регистрации с помощью метода ИнформацияОбОшибке() или ПодробноеПредставлениеОшибки().

Разбор реального примера ошибки

Посмотрим на практический пример из темы форума. В процессе проведения документа вызывалась процедура очистки движений:


УдалитьДвиженияРегистратора(ЭтотОбъект, Отказ, Ложь, 
    ВыборочноОчищатьРегистры И НЕ ЭтотОбъект.ДополнительныеСвойства.ДатаДокументаСдвинутаВперед);

В этом коде происходило прямое обращение к свойству ДатаДокументаСдвинутаВперед через точку от структуры ДополнительныеСвойства. Однако у нового или повторно проводимого документа это свойство не было инициализировано в обработчиках событий. В результате платформа выбросила исключение «Поле объекта не обнаружено (ДатаДокументаСдвинутаВперед)». Так как этот вызов находился внутри скрытой попытки, ошибка была проигнорирована, но транзакция пометилась как ошибочная. Следующий вызов функции ОбщегоНазначения.ЗначениеРеквизитаОбъекта() привел к падению запроса.

Способы устранения проблемы

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

Способ 1. Безопасное чтение значений структуры

Никогда не обращайтесь к необязательным ключам структуры через точку напрямую. Используйте метод Свойство(), который не вызывает исключений при отсутствии ключа:


ДатаСдвинута = Ложь;
ЭтотОбъект.ДополнительныеСвойства.Свойство("ДатаДокументаСдвинутаВперед", ДатаСдвинута);

УдалитьДвиженияРегистратора(ЭтотОбъект, Отказ, Ложь, 
    ВыборочноОчищатьРегистры И НЕ ДатаСдвинута);

Способ 2. Корректная инициализация свойств

В обработчике ПередЗаписью модуля объекта документа гарантируем наличие необходимых служебных ключей перед проведением:


Если НЕ ЭтотОбъект.ДополнительныеСвойства.Свойство("ДатаДокументаСдвинутаВперед") Тогда
    ЭтотОбъект.ДополнительныеСвойства.Вставить("ДатаДокументаСдвинутаВперед", Ложь);
КонецЕсли;

Способ 3. Правильная работа с конструкцией Попытка — Исключение в транзакции

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


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

Соблюдение этих правил позволит избежать скрытых сбоев и гарантирует корректную работу механизмов проведения документов в 1С.

← На главную