Статья
В данной транзакции уже происходили ошибки
К сожалению, сообщение "В данной транзакции уже происходили ошибки" - довольно частая ошибка, но далеко не все специалисты знают почему она возникает и как решить данную проблему.
Ошибка "В данной транзакции уже происходили ошибки"
В данной статье рассмотрим в каких случаях появляется данное сообщение, почему так происходит, а главное - как найти проблемный участок кода и исправить его.

Рассмотрим пример. Допустим, есть следующий код:
Данный код не соответствует стандартам разработки 1С, никогда не следует так писать!
Если в процессе записи возникает исключение, происходит запись в журнал регистрации. Кажется, что всё сделано правильно, но на самом деле здесь есть грубые нарушения стандартов разработки.
Если при записи данных возникло исключение, то при чтении константы появится ошибка "В данной транзакции уже происходили ошибки", хотя ошибка была ранее при записи, и константа не имеет к ней никакого отношения.
Окно с сообщением об ошибке
Дело в том, что если внутри транзакции есть Попытка-Исключение, и в попытке возникла ошибка, связанная с базой данных (например, в момент записи), сама транзакция не отменяется, но ей присваивается метка "только для отмены".
Это означает, что в данной транзакции больше нельзя сделать никаких действий с базой данных - ни прочитать, ни записать - её можно только отменить. Транзакция еще активна, но не жизнеспособна.
Любая попытка обратиться к базе данных из такой транзакции будет приводить к лаконичному "В данной транзакции уже происходили ошибки".
И самое неприятное в том, что в текст ошибки попадает строка кода с обращением к базе данных, а не строка с реальной ошибкой, что сильно затрудняет поиск первопричины.

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

Решение проблемы можно разделить на две большие части:
1) Найти проблемное место
2) Исправить код согласно стандартам 1С

Найти проблемное место можно вручную с помощью тех. журнала, либо использовать Монитор. Рассмотрим оба способа.

Способ 1. Ручная настройка тех. журнала.
Необходимо настроить технологический журнал следующим образом:
Настройка технологического журнала (logcfg.xml)
<?xml version="1.0" encoding="UTF-8"?>
<config xmlns="http://v8.1c.ru/v8/tech-log">
	<log location="d:\Excp_Sdbl" history="48">
		<event>
			<eq property="name" value="EXCP"/>
			<eq property="p:processName" value="Имя базы"/>
			<eq property="Descr" value="В данной транзакции уже происходили ошибки!"/>
		</event>
		<event>
			<eq property="name" value="SDBL"/>
			<eq property="p:processName" value="Имя базы"/>
			<eq property="Func" value="setRollbackOnly"/>
		</event>
		<property name="all"/>
	</log>
</config>
Событие EXCP с фильтром по тексту ошибки позволяет зафиксировать все подобные ситуации в логе.
Но самое интересное - это событие SDBL с фильтром Func=setRollbackOnly. Таким образом можно определить код, в котором у транзакции установился признак "только для отмены".
Объединив эти события по времени и полю t:connectID, можно понять где находится Попытка-Исключение, из которой была вызвана процедура, приведшая к ошибке.

Пример лога тех. журнала:
Фрагмент лога технологического журнала
49:06.002036-1,SDBL,5,process=rphost,p:processName=Demo_Lock,OSThread=28092,t:clientID=973,t:applicationName=1CV8C,t:computerName=Srv,t:connectID=4163,SessionID=7,Usr=Администратор,AppID=1CV8C,DBMS=DBMSSQL,DataBase=Srv\Demo_Lock,Trans=1,Func=setRollbackOnly,Context='Форма.Вызов : ВнешняяОбработка.Тест.Форма.Форма.Модуль.ПроцедураНаСервере
ВнешняяОбработка.Тест.Форма.Форма.Форма : 30 : Данные.Записать();'
49:06.002043-0,EXCP,6,process=rphost,p:processName=Demo_Lock,OSThread=28092,t:clientID=973,t:applicationName=1CV8C,t:computerName=Srv,t:connectID=4163,SessionID=7,Usr=Администратор,AppID=1CV8C,DBMS=DBMSSQL,DataBase=Srv\Demo_Lock,Exception=DataBaseException,Descr=В данной транзакции уже происходили ошибки!,Context='Форма.Вызов : ВнешняяОбработка.Тест.Форма.Форма.Модуль.ПроцедураНаСервере
ВнешняяОбработка.Тест.Форма.Форма.Форма : 39 : Константа1 = Константы.Константа1.Получить();'
В логах у события SDBL в свойстве Context будет указан стек вызова, который и привел к ошибке. Благодаря этому стеку можно относительно просто найти место в коде, где используется Попытка-Исключение, и переписать этот блок. Как именно его нужно переписать обсудим ниже.

Способ 2. Используем Монитор.
Для анализа данной проблемы в Мониторе необходимо включить показатель "Ошибка ТЖ" и сохранить настройки. Данный показатель доступен в бесплатной версии Монитора.
Настройка показателя "Ошибка ТЖ"
Далее Монитор сам настроит тех. журнал, сопоставит события, загрузит логи и присвоит каждой ошибке свою категорию.
Текст ошибки отображается на соответствующей вкладке.
Отображение основных сведений об ошибке
Но самое важное в нашем случае отображается на вкладке Доп. сведения.
Тут отображены события, которые связаны с ошибкой, в нашем случае это SDBL.
Здесь видны все значения всех свойств события, в частности свойство Context, содержащее нужные нам сведения.
Отображение дополнительных сведений об ошибке
В итоге, какой бы способ мы не использовали, получаем строку кода:

ВнешняяОбработка.Тест.Форма.Форма.Форма : 30 : Данные.Записать();

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

Согласно стандартам разработки 1С, необходимо фиксировать транзакцию внутри попытки и отменять при возникновении исключения.
Корректный код должен выглядеть следующим образом:
Корректный код с обработкой исключения внутри транзакции
В таком случае при возникновении исключения при записи полное описание ошибки попадет в журнал регистрации, а при чтении константы ошибки не будет.
Ошибка при записи
В данном случае причина была в делении на ноль, но из-за некорректного кода эта ошибка скрывалась за ширмой "В данной транзакции уже происходили ошибки".
Используя показатель "Ошибка ТЖ" из Монитора, можно довольно легко выявить и устранить эту проблему.
Показатель "Ошибка ТЖ", описанный в данной статье, доступен в бесплатной версии Монитора.
Подпишитесь на наш канал в Telegram чтобы не пропускать новые материалы
Если вы хотите поделиться своим кейсом, напишите нам на support@1smonitor.ru