Tooprogram.ru

Компьютерный справочник
0 просмотров
Рейтинг статьи
1 звезда2 звезды3 звезды4 звезды5 звезд
Загрузка...

В транзакции уже происходили ошибки

Ошибки базы данных и транзакции

При работе с базой данных могут происходить ошибки. В 1С:Предприятии 8 ошибки базы данных подразделяются на следующие две категории:

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

При возникновении восстановимой ошибки считается, что серьезных нарушений в работе 1С:Предприятия 8 не произошло и работа может быть продолжена, но сама вызвавшая ошибку операция прекращается, и вызывается исключение, которое может быть перехвачено и обработано средствами встроенного языка. Казалось бы, все понятно. Но есть тонкость. Если восстановимая ошибка базы данных произошла в процессе выполнения транзакции, то, вне зависимости от того, было исключение, вызванное этой ошибкой, перехвачено и обработано или нет, транзакция уже не может быть продолжена или зафиксирована. Единственная операция с базой данных, которую можно произвести в такой ситуации — это отмена транзакции. Таким образом, приведенный фрагмент кода не вполне корректен:

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

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

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

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

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

Читать еще:  Ошибка java lang illegalargumentexception

Аналогично, ошибка базы данных, произошедшая при значении счетчика транзакций большем 1, приведет к невозможности продолжения и фиксации всей транзакции в целом.

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

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

Доброго времени суток!

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

Я сталкивалась с подобной ошибкой при выполнении кода на клиенте, и в этом случае спасало просто перезайти в базу — дальше все работало. А если на стороне сервера возникает эта ошибка? Каждый раз перезагружать сервер никто не даст.

Как сбросить эту ошибку, чтобы продолжить функционирование регламентного задания, почему ошибка могла возникнуть и как избежать ее повторения?

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

Вывод — либо отказаться от транзакции, либо от обработки ошибок. Я выбрал отказ от транзакции в таких случаях или закрываю транзакцию до записи объектов (особенно если они типовые).

> продолжить функционирование регламентного задания
Продолжить никак. Лучше всего его полностью остановить и запустить снова.

«Продолжить — я имела ввиду не текущее выполнение регламентного задания, а работу рег.задания при последующих запусках. Кого остановить? Рег.задание?

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

НачатьТранзакцию();
Попытка
// проводим докумены
Исключение
// обработка исключения
КонецПопытки;
ЗафиксироватьТранзакцию();

И что значит «отказаться от отработки ошибок»? То есть, убрать из этой конструкции Попытку-Исключение?»

«> Поясните, пожалуйста, подробней.
> Почему нельзя Зафиксировать транзакцию, если, к примеру, используется конструкция:
>
> НачатьТранзакцию();
> Попытка
> // проводим докумены
> Исключение
> // обработка исключения
> КонецПопытки;
> ЗафиксироватьТранзакцию();
>
> И что значит «отказаться от отработки ошибок»? То есть, убрать из этой конструкции Попытку-Исключение?

Читать еще:  Не устанавливается приложение ошибка 24

Представть, что ваша конструкция теперь выглядит так (просто есть кусок которого вы не видите — не вы его писали):

НачатьТранзакцию();
Попытка
//Вот этот код где-то там — далеко в каком-нибудь модуле, да и 1С, что-то такое ведет в случае проведения
НачатьТранзакцию();
Попытка
//Здесь возникает ошибка
Исключение
ОтменитьТранзакцию();
КонецПопытки;
ЗафиксироватьТранзакцию();
Исключение
// обработка исключения
КонецПопытки;
ЗафиксироватьТранзакцию();

Так вот, если я правильно понимаю, то если вложенная транзакция закрывается, то транзакция верхнего уровня это видит и отказывается закрываться (возможно она уже тоже закрытая). В общем — кроме отмены транзакции уже ничего не сделать.

Вот и получается, что несмотря на обработку ошибки (исключения) — ошибка уже была и ничего с этим не сделать. Значит надо от чего-то отказаться. Или увидеть ошибку или убрать транзакцию.
«

> Кого остановить? Рег.задание?
А что у вас показывает «Консоль Заданий » по поводу вашего зависшего задания?

«1> НачатьТранзакцию();
2> Попытка
3> //Вот этот код где-то там — далеко в каком-нибудь модуле, да и 1С, что-то такое ведет в случае проведения
4> НачатьТранзакцию();
5> Попытка
6> //Здесь возникает ошибка
7> Исключение
8> ОтменитьТранзакцию();
9> КонецПопытки;
10> ЗафиксироватьТранзакцию();
11> Исключение
12> // обработка исключения
13> КонецПопытки;
14> ЗафиксироватьТранзакцию();

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

Ок, в этой последовательности понятно, спасибо. Только будет очень трудно найти, где же оно в реальном коде ломается.

>> Кого остановить? Рег.задание?
>А что у вас показывает «Консоль Заданий » по поводу вашего зависшего задания?

Вот «КонсольЗаданий» как раз и показывала в графе Ошибка «В данной транзакции уже происходили ошибки!». Правда, потом убрали Использование Рег.задания, а сегодня опять его запустили — ошибка прошла. Но есть вероятность, что сервер перезагружали, узнать пока точно не могу — это сервер нашего филиала в другом городе. «

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

Собственно, сабж описан на ИТС: Ошибки базы данных и транзакции. Здесь хочу дать пример воспроизведения, и пару рекомендаций как делать «не надо».

Создам простенькую тестовую конфу:

Имеем два документа:

  • Досье контрагентов, реквизиты Контрагент (Ссылка), Адрес (Строка), при проведении создает движение по подчиненному регистру сведений, в котором контрагент является измерением, а адрес — ресурсом

  • Поступление ТМЦ, реквизиты Поставщик (Ссылка), СуммаДокумента (Число), при проведении создает движение по регистру накопления «Взаиморасчеты».

Теперь предположим такое пожелание: пользователь хочет, чтобы при проведении поступления ТМЦ автоматически регистрировался адрес поставщика в документе «Досье контрагента». Программист добавляет в поступление реквизит «Адрес», и пишет следующий код в модуле объекта:

Тут надо конечно сделать поиск по ранее созданным документам, но это просто пример, я не буду углубляться. Что здесь происходит? Вложенная транзакция, которая может привести к т.н. «невосстановимой» ошибке базы данных. К примеру, если на дату записи поступления ТМЦ будут существовать записи в регистре сведений по тому же контрагенту, то произойдет дублирование по ключу измерения — это сделает невозможным проведение досье из поступления, вызвав ту самую ошибку, которой посвящена эта статья. То-же самое будет, если в самом досье в обработке проведения поставить «Отказ = Истина» — например адрес не выдержал каких-то проверок, и алгоритм досье не позволяет записывать в базу эти данные. Все, что связано с изменением данных внутри транзакции — может привести к такой ошибке.

Читать еще:  Ошибка инициализации ядра перевода

А есть еще вторая категория ошибок — «Восстановимые». Где-то в интернете видел такой пример:

Это никогда к такой ошибке не проведет, т.к. данные базы внутри транзакции не изменяются. Но это не значит что Попытка-Исключение это хорошо 🙂 Хочется процитировать фразу одного опытного программиста: «..когда я вижу в коде обработку исключений, я понимаю что программист просто сдался, и не стал решать проблему..» — полностью солидарен.

Осталось обратить внимание на такой вопрос: а в какой именно момент возникает ошибка? Правильный ответ — при последующем чтении данных (об этом где-то рассказано на ИТС, я не могу сейчас найти статью — если кто-нибудь скинет, буду благодарен). Действительно, в ситуации на скриншоте ниже ошибка уже произошла, но терминирующего сообщения в точке останова еще нет. Оно появится когда начнет работать обработчик проведения — который первым делом считывает движения объекта, поэтому в точку останова 2 мы не попадаем:

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

Надеюсь, кому-то это будет полезным — по крайней мере за 15 минут гугления, подобного описания я не встретил. Только вводящий в заблуждение пример обработки исключения внутри транзакции, о котором упоминалось выше.

— Непродуманная запись одних объектов внутри других — это зло. Даже если на первый взгляд все работает, небольшое изменение архитектуры может привести к проблемам. К примеру — если бы поступление ТМЦ из моего примера не делало бы никаких движений в базе, то и ошибки бы не возникло — ведь тогда бы не происходило обращения к базе после отката проведения вложенной транзакции. Но скажем, если бы это «решение» просуществовало пару месяцев, а потом потребовалось добавить движения к документу — сразу бы напоролись на сабж, в самый неподходящий момент. С другой стороны ключи аналитики в типовых конфигурациях создаются именно так. 7 раз отмерь.

— Следует помнить что существуют две категории ошибок БД — восстановимые и невосстановимые (об этом явно сказано на ИТС, в самом начале статьи). Можно использовать попытки, в т.ч. внутри транзакции. Но следует помнить, что попытка — это по-сути тоже транзакция, ее использование тормозит систему, кроме этого затрудняет отладку (при включенной остановке на ошибках, если есть частый выброс исключения). И если уж все-таки никак без нее не обойтись, то применять ее следует правильно — ИТС: Перехват исключений в коде.

Ссылка на основную публикацию
Adblock
detector