Правила работы с транзакциями 1С

07.09.23

Разработка - Рефакторинг и качество кода

Список правил при работе с транзакциями из BSL Language Server и SonarQube 1C (BSL) Plugin. Переработка и осмысление материала.

Описание диагностик из проектов BSL Language Server и SonarQube 1C (BSL) Plugin 

 

Общепринятый паттерн

Описан в ИТС

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

 

Метод "НачатьТранзакцию" должен располагаться непосредственно перед оператором "Попытка"

Диагностика

Описание диагностики 

Метод НачатьТранзакцию должен быть за пределами блока Попытка-Исключение непосредственно перед оператором Попытка. (с) ИТС: Транзакции: правила использования, пункт 1.3

Начало транзакции и ее фиксация (отмена) должны происходить в контексте одного метода.

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

Примеры

Неправильно:

Процедура Пример2()
    НачатьТранзакцию(); // <-- Ошибка: код перед попыткой
    Метод();
    Попытка
        Метод2();
    Исключение
        ОтменитьТранзакцию();
        Возврат;
    КонецПопытки;
    ЗафиксироватьТранзакцию();
КонецПроцедуры

Процедура Пример3()
    Попытка
        НачатьТранзакцию(); // <-- Ошибка: в попытке
        Метод();
    Исключение
        Если ТранзакцияАктивна() Тогда
            ЗафиксироватьТранзакцию();
        Иначе
            ОтменитьТранзакцию();
        КонецЕсли;
        Возврат;
    КонецПопытки;
КонецПроцедуры

Источник: Стандарт: Транзакции: правила использования

Метод "ЗафиксироватьТранзакцию" должен идти последним в блоке "Попытка"

Диагностика

Описание диагностики

Метод 'ЗафиксироватьТранзакцию' должен идти последним в блоке 'Попытка' перед оператором 'Исключение', чтобы гарантировать, что после ЗафиксироватьТранзакцию не возникнет исключение.

Примеры

Неправильно:

Процедура Пример2()
    НачатьТранзакцию();
    Попытка
        Метод();
    Исключение
        ОтменитьТранзакцию();
        Возврат;
    КонецПопытки;
    ЗафиксироватьТранзакцию(); // <-- Ошибка: вне попытки
КонецПроцедуры

Процедура Пример3()
    НачатьТранзакцию();
    Попытка
        Метод();
    Исключение
        Если ТранзакцияАктивна() Тогда
            ЗафиксироватьТранзакцию(); // <-- Ошибка: в исключении
        Иначе
            ОтменитьТранзакцию();
        КонецЕсли;
        Возврат;
    КонецПопытки;
КонецПроцедуры

Тем более! неправильно:

НачатьТранзакцию();
ДокументОбъект.Записать(РежимЗаписиДокумента.Запись);
Попытка
	ЗафиксироватьТранзакцию();
Исключение
	ОтменитьТранзакцию();
	Инфо = ИнформацияОбОшибке();
	ВызватьИсключение ПодробноеПредставлениеОшибки(Инфо);
КонецПопытки;

Источник: Стандарт: Транзакции: правила использования

Метод "ОтменитьТранзакцию" должен идти первым в блоке "Исключение"

Диагностика

Описание диагностики

В блоке Исключение нужно сначала вызвать метод ОтменитьТранзакцию, а затем выполнять другие действия, если они требуются.

Такое правило необходимо, чтобы убрать потенциальную возможность выброса исключения в блоке "Исключение", что может привести к тому, что метод "ОтменитьТранзакцию" не будет вызван.

Если в Метод2() будет обращение к БД (явное или неявное) - это вызовет ошибку "В данной транзакции уже происходили ошибки"

Примеры

Неправильно:

Процедура ЗаписатьЭлемент()
    НачатьТранзакцию();
    Попытка
        Метод();
        ЗафиксироватьТранзакцию();
    Исключение
        Метод2(); // <-- Ошибка: код перед отменой
        ОтменитьТранзакцию();
    КонецПопытки;
КонецПроцедуры

Источник: Стандарт: Транзакции: правила использования

Необоснованное использование метода ТранзакцияАктивна()

При жестком соблюдении правил работы с транзакциями использование метода ТранзакцияАктивна() в блоке Исключение становится лишним.

Требует обоснования:

НачатьТранзакцию();
Попытка
    ДелаемЧтоТо();
    ЗафиксироватьТранзакцию();
Исключение
    Если ТранзакцияАктивна() Тогда
        ОтменитьТранзакцию();
    КонецЕсли;
    ЗаписьЖурналаРегистрации();
КонецПопытки;

Использование данного паттерна нарушает инкапсуляцию и приводит к "размазыванию" логики управления транзакциями. 

На нашем уровне абстракции мы обязаны заботиться только о нашей транзакции. Все прочие должны быть нам неинтересны. Они чужие, мы не должны нести за них ответственность. Именно НЕ ДОЛЖНЫ. Нельзя предпринимать попыток выяснения реального уровня счетчика транзакций. (с) Вы не умеете работать с транзакциями

 
 В общем и целом, мнения мейнтейтеров здесь совпадают

Если считаем что это необходимо - просто игнорим срабатывание правила

Примечание: с помощью метода ТранзакцияАктивна() нельзя узнать что транзакция сломана

 

При обработке исключений необходимо использовать метод ЗаписьЖурналаРегистрации()

Диагностика

Недопустимо перехватывать любые исключения, бесследно для системного администратора.

Неправильно

Попытка 
    // код, приводящий к вызову исключения
    ....
Исключение // перехват любых исключений
КонецПопытки;

Как правило, подобная конструкция скрывает реальную проблему, которую впоследствии невозможно диагностировать.

Правильно


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

Источник: Перехват исключений в коде

Необходимо обязательно указывать 1, 2 и 5 параметр метода ЗаписьЖурналаРегистрации()

Диагностика

Нельзя пропускать 1й параметр. Нельзя указывать его и переменной строкой - это вызывает раздувание словаря ЖР (1Cv8.lgf) и, как следствие, зависание при его открытии.

Нельзя пропускать 2й параметр - Уровень журнала регистрации. Если его не указать, по умолчанию 1С применит уровень ошибки Информация, и данная запись может потеряться в потоке записей.

Нельзя пропускать и 5й параметр - комментарий к событию записи в журнал регистрации. При обработке исключений обязательно нужно выполнять запись в журнал регистрации с полным представлением ошибки.

Неправильно:

ЗаписьЖурналаРегистрации("Событие");// ошибка
ЗаписьЖурналаРегистрации("Событие" + Ссылка); // ошибка
ЗаписьЖурналаРегистрации("Событие", УровеньЖурналаРегистрации.Ошибка);// ошибка
ЗаписьЖурналаРегистрации("Событие", , , , ПодробноеПредставлениеОшибки(ИнформацияОбОшибке()));//ошибка

ЗаписьЖурналаРегистрации("Событие", УровеньЖурналаРегистрации.Ошибка, , , ОписаниеОшибки());//ошибка

ОписаниеОшибки() и КраткоеПредставлениеОшибки() не содержат текста строки, вызвавшей ошибку. Код может измениться к моменту анализа ошибки, и придется дополнительно исследовать, где же именно возникла ошибка.

ТекстОшибки = ОписаниеОшибки(); // <-- Ошибка: отсутствует текст строки, вызвавшей ошибку
// {ОбщийМодуль.ОбщегоНазначения.Модуль(2)}: Деление на 0

ТекстОшибки = ПодробноеПредставлениеОшибки(ИнформацияОбОшибке());
//{ОбщийМодуль.ОбщегоНазначения.Модуль(2)}: Деление на 0
//    й=1/0; <-- присутствует текст строки, вызвавшей ошибку

// для 8.3.15+ <-- присутствует стек
ТекстОшибки =ПодробноеПредставлениеОшибки(ИнформацияОбОшибке());
//Деление на 0
//{ОбщийМодуль.ОбщегоНазначения.Модуль(2)}:й=1/0;
//{ВнешняяОбработка.ВнешняяОбработка1.Форма.Форма.Форма(61)}:А = ОбщегоНазначения.ЗначениеРеквизитаОбъекта();

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

Диагностика

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

В качестве внешних ресурсов следует рассматривать любые ресурсы, которыми напрямую не управляет сервер 1С.

  • Файловая система
  • http-, web-сервисы
  • ftp
  • com-вызовы в Windows
  • обращения к сторонним СУБД
  • и т.п.

Нужно учитывать

  • как явные транзакции - НачатьТранзакцию
  • так и неявные - внутри системных событий 1С
    • например, код внутри события ПередЗаписью, ОбработкаПроведения и т.п.

Неправильно:

// Подписка ПриЗаписи - транзакция открыта
Процедура усВыгрузитьДокументПриЗаписи(Источник, Отказ) Экспорт

    // ...
    // Подключение к внешнему веб-сервису  в транзакции - ошибка!
    // при недоступности которого транзакция зависнет
    WSПрокси = Новый WSПрокси(WSОпределение, URIПространстваИмен, ИмяСервиса);	// без таймаута - ошибка!

    // ... или
    Файл.Записать(); // обращение к файловой системе в транзакции - ошибка!

    // ... или
    Почта.Отправить(); // обращение к SMTP серверу в транзакции - ошибка!

КонецПроцедуры

Правильно:

Вынести обращение к внешним ресурсам за пределы транзакции

 
 Статья Ловля блокировок на связке "Microsoft SQL server - 1С" за авторством @fhqhelp

 Чего только не находилось за последние несколько лет в транзакциях проведения документов или записи набора регистров:

  - Предупреждение() или Вопрос() - это самое любимое

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

  - разнообразные файловые операции

  - обращения к другим базам через com-объекты

  - обращения к другим базам посредством Новый COMОбъект("ADODB.Connection")  (ага, а на соседнем сервере уже запрос через то самое ADODB... тоже висит на блокировке! и такое было..) 

 - работа с ftp

 - обращение к web-сервису

 - запуск на исполнение сторонних программ

Источники:

Внутри транзакции недопустимо подавлять ошибки, вызывающие событие SDBL Func='setRollbackOnly'

ИТС: Ошибки базы данных и транзакции

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

 
 Поясняющее видео от Апресова Игоря

Неправильно:

НачатьТранзакцию();
Попытка
    // ...
    Попытка
        Объект.Записать(); // ПриЗаписи Отказ = Истина - вызовет setRollbackOnly
    Исключение
         ЗаписьЖурналаРегистрации();
    КонецПопытки;
    ЗафискироватьТранзакцию(); // <-- Ошибка: транзакция завершится откатом, исключение выдано не будет
Исключение
    ОтменитьТранзакцию();
    // ...
КонецПопытки;

Метод ЗафиксироватьТранзакцию() при глубине = 1 не всегда фиксирует фактическую транзакцию, а закрывает ее путем отката если она сломана и путем фиксации если она не сломана.

Без проброса исключения либо команда "ЗафискироватьТранзакцию();" отменит транзакцию, либо следующее обращение к базе данных вызывает ошибку «В данной транзакции уже происходили ошибки». 

Неправильно:

Процедура ОбработкаПроведения()
    Попытка
        ...запись в базу с ошибкой
    Исключение
        //по стандарту должно быть исключение, т.к. есть внешняя транзакция 
        //но его не было
        //ВызватьИсключение; 
    КонецПопытки;
    // <-- Ошибка: "В данной транзакции уже происходили ошибки!"
КонецПроцедуры

В каких случаях подавление ошибок делает транзакцию "сломанной":

  • Вызов метода ОтменитьТранзакцию() внутри "вложенной" транзакции
  • Подавление ошибки или отказа в методе Записать() в коде в транзакции
  • Подавление ошибки при выполнении некорректного запроса, вида "ВЫБРАТЬ 1/0" в транзакции

Событие в технологическом журнале:
20:43.385010-1,SDBL,5,process=1CV8,OSThread=12256,Usr=DefUser,DBMS=DBV8DBEng, DataBase=InfoBase75, Trans=1, Func=setRollbackOnly - установка флага наличия в транзакции ошибки (ее можно только откатить)

 
 Примеры

Дополнение от 12.05.2023:

Неправильно:

// неявная транзакция
Процедура ОбработкаПроведения() // или ПриЗаписи() или ПередЗаписью()

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

КонецПроцедуры

Неправильно:

// явная транзакция
Процедура ЗагрузкаИзВМС()

    НачатьТранзакцию();
    Попытка

        Для Каждого КорректировкаРеализации Из МассивКорректировок Цикл
            Попытка
                КорректировкаРеализации.Записать(РежимЗаписиДокумента.Проведение);
            Исключение
                // ...
            КонецПопытки
        КонецЦикла;
        ЗафиксироватьТранзакцию();
    Исключение
        ОтменитьТранзакцию();
        // ...
    КонецПопытки;
КонецПроцедуры

Использование такого рода кода приводит к ошибке "В данной транзакции уже происходили ошибки!"

нельзя подавлять ошибки при работе внутри транзакции!

Ещё один вариант этого правила:

При использовании вложенных транзакций в конце блока Исключение рекомендуется добавить оператор ВызватьИсключение

Источник: ИТС: Транзакции: правила использования

Как известно, «1С:Предприятие 8» не поддерживает вложенных транзакций. 

Это значит что, фактически, поддерживается только один уровень транзакции. То есть не существует возможности отменить действие транзакции некоторого уровня, не отменяя транзакции вышестоящего уровня.

Менеджер транзакции содержит признак “Отменена”. Если он установлен, то транзакция считается сломанной и фактическая транзакция подлежит отмене при ее любом завершении. Устанавливается он при возникновении ошибки базы данных и при вызове ОтменитьТранзакцию(). Явно получить значение признака “Отменена” менеджера транзакции во встроенном языке нельзя. (с) Безопасная работа с транзакциями во встроенном языке

Правильно:

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

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

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

 
 Настольная книга 1С.Эксперта по технологическим вопросам, стр. 48

 

Дополнительные параграфы:

При использовании оператора ВызватьИсключение необходимо сохранять стек ошибок

О вложенных попытках, исключениях и о представлении ошибок

 

Объектное чтение набора записей в транзакции устанавливает неявную управляемую разделяемую блокировку

Это может вызвать взаимоблокировку в параллельных транзакциях

Какие бывают блокировки в 1С?

 

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

ИТС: Ответственное чтение данных

 

 
 upd 13/03/2023 Упражнения для освоения материала
 
Пример 1. Исправить код наиболее оптимальным образом

Диагностики взяты с сайтов: 

 

Для контроля ошибок рекомендую использовать:

 

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

Статья написана для облегчения онбординга новых сотрудников без опыта решения проблем с транзакциями, является частью соглашений по стайл-гайду (зачем нужен code style).

транзакция исключение попытка происходили ошибки правила НачатьТранзакцию ОтменитьТранзакцию ЗафиксироватьТранзакцию ТранзакцияАктивна работы с транзакциями

См. также

Чистый код. Мой взгляд на жизнь в макаронных джунглях. Часть 1

Рефакторинг и качество кода Платформа 1С v8.3 Конфигурации 1cv8 Россия Бесплатно (free)

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

19.09.2023    1406    Lemmonbri    7    

7

5 подходов при доработке конфигурации 1С, чтобы в будущем не было мучительно больно её обновлять

Анализ и проектирование ИТ-систем Рефакторинг и качество кода Обновление 1С Платформа 1С v8.3 Бесплатно (free)

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

10.08.2023    7403    1c-izhtc    36    

16

Задача на ошибки и неоптимальности при проведении приходной накладной

Рефакторинг и качество кода Платформа 1С v8.3 Конфигурации 1cv8 Бесплатно (free)

Задачу эту дают на собеседованиях, видимо, те франчи, которые не в состоянии оценить человека по резюме и в ходе беседы. По идее задачи, подобные этой, должны давать начинающим студентам. Но дают всем подряд. Итак: мои 5 копеек. Критика приветствуется.

11.07.2023    1593    magic1s    31    

9

Тормозит на ровном месте, или на чем может споткнуться PostgreSQL

HighLoad оптимизация Рефакторинг и качество кода Платформа 1С v8.3 Бесплатно (free)

Прилетела интересная задача с примером, когда одно и то же действие выполняется на MS SQL за 1 минуту и около часа на Postgre SQL 14. Вот и решил поделиться занимательным опытом расследования причин вышеупомянутой проблемы. Ну и посмотреть вскользь на модуль ERP "1С:Хлебобулочное и кондитерское производство".

30.06.2023    2213    zeltyr    14    

13

Нестандартные приемы безопасной разработки и эксплуатации ПО на платформе 1С, категория "18+"

HighLoad оптимизация Рефакторинг и качество кода Администрирование СУБД Бесплатно (free)

Готовы погрузиться в недетское программирование и шКОДИТЬ по-взрослому? О том, как повысить безопасность разработки и эксплуатации ПО через изощренные способы подключения к платформе 1С, на конференции Infostart Event 2022 Saint Petersburg рассказал Юрий Лазаренко.

19.06.2023    1870    TitanLuchs    6    

20

Шаблоны для применения cтандартов и методик разработки конфигураций 1С

Рефакторинг и качество кода Платформа 1С v8.3 Абонемент ($m)

Готовые шаблоны текста для применения cтандартов и методик разработки конфигураций 1С, инструментарий. Версия платформы 8.3.22.1851.

2 стартмани

04.06.2023    7265    47    improg    34    

39

Ревьювер. Инструмент для проведения code review

Рефакторинг и качество кода Платформа 1С v8.3 Конфигурации 1cv8 Бесплатно (free)

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

16.05.2023    3588    leobrn    12    

50

Применение cтандартов и методик разработки конфигураций на практике

Рефакторинг и качество кода Платформа 1С v8.3 Бесплатно (free)

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

15.05.2023    6326    improg    56    

67
Комментарии
В избранное Подписаться на ответы Сортировка: Древо развёрнутое
Свернуть все
1. artbear 1437 01.12.22 22:08 Сейчас в теме
Хорошая подборка!
Значит, мы не зря придумывали и реализовывали эти правила.

Есть замечания
>ОписаниеОшибки() и КраткоеПредставлениеОшибки() не содержат текста строки, вызвавшей ошибку. Код может измениться к моменту анализа ошибки, и придется дополнительно исследовать, где же именно возникла ошибка.

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

именно наличие стека вызовов важно, а он есть только в ПодробноеПредставлениеОшибки, поэтому и нужно использовать данный метод.
ubnkfl; frkbvfnjh; Артано; user953800; PrinzOfMunchen; +5 Ответить
2. EliasShy 48 02.12.22 09:58 Сейчас в теме
Описания есть на соответствующих площадках, и лучше их из Сонара настроенного и смотреть. Правила живые, оптимизируются, дополняются. Ваша статья таким свойством не обладает.

Дополнения (ссылки на статьи) - полезные, но для внутреннего ресурса управления знаниями.

Зачем (ради чего) личные заметки публиковать на ресурсе?
4. artbear 1437 02.12.22 12:38 Сейчас в теме
(2) Считаю данную подборку полезной, проголосовал плюсом!

Напоминания разработчикам о полезном, обучение тех, кто не знает о важных особенностях.
18. EliasShy 48 06.12.22 10:07 Сейчас в теме
(4)
зном, обучение

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

Сколько подобных устаревших и вредных статей по оптимизации? Вячеслав устал уже опровергать предложенные методы.
44. antonio_i 76 07.07.23 15:00 Сейчас в теме
(18)
А какие методы актуальны?
Может "Вячеслав" где-то описал? Дайте ссылочку, будьте добры, или расскажите, если не секрет.
Не помню кардинальных изменений в платформе в ближайшие пару лет, которые кардинально поменяли подход к работе с транзакциями. Что поменялось?
3. ardn 579 02.12.22 11:22 Сейчас в теме
Отличная статья!
kuzyara; Krotov_Valery; +2 Ответить
5. kser87 2389 02.12.22 12:45 Сейчас в теме
трудно анализируемым ошибкам времени выполнения типа "В этой транзакции уже происходили ошибки." - анализируются средствами ТЖ.

Про ТранзакцияАктивна() - "я не понимаю, зачем это нужно, поэтому не используйте". Подход убийственный. ответ на это правильный такой: Правильно писать не "ОтменитьТранзакцию()", а

Если ТранзакцияАктивна() Тогда
ОтменитьТранзакцию()
КонецЕсли;

Всегда.

Почему? Потому, что 1С не поддерживает вложенные транзакции. Вы никогда не знаете наверняка, является ли ваша транзакция вложенной или ее саму куда-то вложили. никакие сонары это не показывают
6. gybson 02.12.22 14:52 Сейчас в теме
(5) Афигеть аргументация : "Потому что гладиолус"

А что взорвется, если эту ересь не писать?
8. kser87 2389 02.12.22 15:50 Сейчас в теме
(6) взорвется код если вызвать ОтменитьТранзакцию() вне активной транзакции. Такое бывает, когда базу дорабатывает большое количество людей.
9. пользователь 02.12.22 16:03
Сообщение было скрыто модератором.
...
11. nicxxx 252 02.12.22 16:41 Сейчас в теме
(8) Для этого есть тесты
EliasShy; +1 Ответить
12. kser87 2389 02.12.22 16:48 Сейчас в теме
(11) и код ревю и надо вообще смотреть когда вызываете чужие методы. На практике часто не работает по объективным причинам.
13. buganov 196 03.12.22 09:00 Сейчас в теме
(5)А еще лучше управлять исключительными ситуациями на том слое, на котором они возникли, и тогда ге придется ломать голову, а выше открыта транзакции или нет, есть ли там попытка или нет. Например, через проброс исключения наверх.
Общий модуль.ОбщийСервер.КакойтоМетод()
НачатьТранзакцию();
Попытка
    Док.Записать();
    ЗафиксироватьТранзакцию();
Исключение
    ОтменитьТранзакцию();
    ВызватьИсключение;
КонецПопытки;


И плевать, что там в вызывающем модуле, транзакции, попытка или еще что.
rozer; kuzyara; +2 Ответить
20. frkbvfnjh 765 06.12.22 14:51 Сейчас в теме
(13) Вот это вообще не понимаю зачем делать - делать в попытке, что бы не было исключения, и принудительно его (исключение) генерировать
22. frkbvfnjh 765 06.12.22 15:12 Сейчас в теме
(20) Для меня попытка исключение и транзакция вещи не совместимые - ты либо делаешь в транзакции, либо в попытке, как можно вообще смешивать это? А главное зачем?
41. kuzyara 1765 02.05.23 07:29 Сейчас в теме
(22) Необходимость выполнить какой-то код перед пробросом исключения.

//Например, вернуть счетчик транзакций в начальное значение (до нашего вызова):
Исключение
	ОтменитьТранзакцию();
	ВызватьИсключение; // <- если это вложенная попытка (транзакция)
КонецПопытки;

//Например,освободить файл:
Попытка
	РезультатПроверки = ПроверитьКонфигурациюВыгрузкиВнутр(ЧтениеДанныхАрхива)
Исключение
	ЧтениеДанныхАрхива.Закрыть();
	ВызватьИсключение;
КонецПопытки;

//Например, отключить монопольный режим:
Исключение
	УстановитьМонопольныйРежим(Ложь);
	ВызватьИсключение;
КонецПопытки;

//Например, закрыть форму:
Исключение
	ДлительныеОперацииКлиент.ЗакрытьФормуДлительнойОперации(ФормаДлительнойОперации);
	ВызватьИсключение;
КонецПопытки;
Показать
43. frkbvfnjh 765 02.05.23 08:29 Сейчас в теме
(41) Хммммм, это же получается что то типа try ... finally в Delphi. Спасибо, возьму на заметку!
23. buganov 196 06.12.22 18:14 Сейчас в теме
(20)чтобы обогатить данные об ошибке, например
27. gybson 08.12.22 11:04 Сейчас в теме
(20) Обработка исключения это не только * транзакции. Например там можно сделать запись в журнал и Отказ = Истина, прочие обработки потом пробросить его наверх
28. kuzyara 1765 08.12.22 11:41 Сейчас в теме
(27)
Например там можно сделать запись в журнал и Отказ = Истина, прочие обработки потом пробросить его наверх
вот как раз с записью в журнал в сломанной транзакции есть проблемы:
При обработке исключения в сломанной транзакции часто разумно писать диагностическую
информацию в журнал регистрации. При этом метод ЗаписьЖурналаРегистрации() неявно берет
представление от ссылки, используя кэш представлений ссылок, и помещает его
в поле "Представление данных" события журнала. Обращение к этому кэшу в сломанной
транзакции несет риск невосстановимой ошибки. (с) https://infostart.ru/1c/articles/1026771/
поэтому правильным считаю либо использовать СсылкаДляПередачиВЖурналРегистрации() из той статьи, либо писать ВызватьИсключение; // есть внешняя транзакция сразу после отмены транзакции (оставив работу по записи в журнал вызывающему методу) если внешняя транзакция конечно же есть
31. gybson 08.12.22 12:09 Сейчас в теме
(28) В журнал регистрации можно записать и просто строку с нужным видом события.
7. Sashares 34 02.12.22 14:52 Сейчас в теме
Ошибка в коде - должно быть КонецПопытки; - см.файл.
Прикрепленные файлы:
10. gybson 02.12.22 16:09 Сейчас в теме
Код, который начинает транзакцию, обязан завершить или откатить ее. В случаях когда метод НачатьТранзакцию() находится внутри блока Попытка-Исключение есть риск нарушения парности вызовов НачатьТранзакцию()-ЗафиксироватьТранзакцию(


Вот это что-то из области древних знаний, истоки которых утеряны. Как этот риск возникает? Почему? Никто уже не помнит.

И ссылка на источник, в котором написано ровно то же слово в слово. Вы че серьезно собираетесь ссылаться друг на друга и на том основании считать, что так и надо?

Риск точно такой же, как если кто-то впишет код между началом транзакции и попыткой.
14. naf2000 05.12.22 17:39 Сейчас в теме
Да, пункт про НачатьТранзакцию() до попытки как-то слабо обоснован.
15. AndreyKN 05.12.22 23:07 Сейчас в теме
(14)Наверное, можно предположить обоснование следующее. А что, если выскочит ошибка на строке НачатьТранзакцию(), то, соответственно, попадаем в исключение, а там ОтменитьТранзакцию(), которую так и не смогли начать.

Правда не знаю, может ли возникнуть ситуация, чтобы НачатьТранзакцию() вызвало исключение.
24. buganov 196 06.12.22 18:18 Сейчас в теме
(15) Это из разряда фантастики. Как на открытии транзакции может завалиться, и при этом все еще пытаться обработать исключение?
16. kuzyara 1765 06.12.22 05:03 Сейчас в теме
(14) тоже так думаю, всегда писал в попытке - но есть ИТС https://its.1c.ru/db/v8std/content/783/hdoc и там в пункте 1.3 четко сказано что вне. Так что считаю за формальность
Прикрепленные файлы:
17. quazare 3423 06.12.22 08:35 Сейчас в теме
Понравилась вот эта фраза "Статья написана в личных целях для онбординга новых сотрудников, является частью соглашений по стилю кода."

т.е. приходит новый сотрудник - а ему еще и стиль кода вы устанавливаете?
19. naf2000 06.12.22 12:18 Сейчас в теме
(17) ну... Это вообще нормальные практики в командах
kuzyara; AllexSoft; +2 Ответить
21. frkbvfnjh 765 06.12.22 14:54 Сейчас в теме
Не понимаю, почему данный код считается ошибочным:
Процедура Пример2()
    НачатьТранзакцию();
    Попытка
        Метод();
    Исключение
        ОтменитьТранзакцию();
        Возврат;
    КонецПопытки;
    ЗафиксироватьТранзакцию(); // <-- Ошибка: вне попытки
КонецПроцедуры
Показать

Если после отмены транзакции мы вызываем Возврат - это как раз и есть гарантия того, что после отмены транзакции уже ничего не выполнится в методе. Разве нет? Или просто потому что код не такой красивый как хотелось бы автору данной рекомендации?
25. buganov 196 06.12.22 18:20 Сейчас в теме
(21)написание кода регламентирует стандартами разработки. Да, это не свод правил, но они предельно логичны, читаемы и определенно в большинстве своем оптимальны.
26. kuzyara 1765 07.12.22 05:31 Сейчас в теме
(21) такие конструкции сложно вкладывать друг в друга
29. gybson 08.12.22 12:06 Сейчас в теме
(21) Если в обработку исключения еще что-то не впишут
30. artbear 1437 08.12.22 12:09 Сейчас в теме
(21) да, код в целом нормальный.
фактически правило бсл лс здесь ложно срабатывает.
ИМХО проблема уже зарегистрирована в репозитории бсл лс
32. kuzyara 1765 09.12.22 06:21 Сейчас в теме
(30)
да, код в целом нормальный.
а такой код нормальный?
НачатьТранзакцию();
Попытка
    // нечто
    Если Истина Тогда
        ЗафиксироватьТранзакцию();
        Возврат;
    КонецЕсли;
    // Еще нечто
    ЗафиксироватьТранзакцию();
Исключение
    ОтменитьТранзакцию();
    ВызватьИсключение;
КонецПопытки
Показать
33. artbear 1437 09.12.22 11:31 Сейчас в теме
(32) да, я считаю, что верный.

в некоторых случаях не удается упростить код и приходится в соседних ветках кода указывать ЗафиксироватьТранзакцию
34. apic 12 13.01.23 14:21 Сейчас в теме
Я что то не понял, а если я хочу совместить транзакции, попытку и запись в журнал регистрации, то какой паттерн то в итоге использовать?
Как правильно? Так:
НачатьТранзакцию();
Попытка
... // чтение или запись данных
ЗафиксироватьТранзакцию();
Исключение
// И запись события в журнал регистрации для системного администратора.
ЗаписьЖурналаРегистрации(НСтр("ru = 'Выполнение операции'"),
УровеньЖурналаРегистрации.Ошибка,,,
ПодробноеПредставлениеОшибки(ИнформацияОбОшибке()));
ОтменитьТранзакцию();
... // дополнительные действия по обработке исключения
КонецПопытки;

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

Что первым делать, в журнал писать или транзакцию откатывать? Или все таки правильней получить описание ошибки в переменную, потом откатить транзакцию, а потом записать в журнал. Описание ошибки же в первую очередь нужно, но это противоречит тому, что в первую очередь нужно откатывать транзакцию... По сути все рекомендации бессмысленны, если их применять все вместе, а если применять их по отдельности, то тоже бессмысленны...
36. artbear 1437 13.01.23 14:47 Сейчас в теме
(34) правильно делать, как на сайте ИТС написано. Ссылка или в моих комментариях выше или в статье есть.
Там валидный пример, аналог вашего варианта №2
смотрите, шаблон-то простой

только зачем у вас Зафиксировать, а потом почти сразу же ОтменитьТранзакцию?
38. apic 12 13.01.23 15:59 Сейчас в теме
(36) Спасибо, да вижу, что во многих конфах используется вариант №2, но я думал, что вызов ОтменитьТранзакцию стирает описание об ошибке
40. artbear 1437 13.01.23 16:50 Сейчас в теме
(38) Повторю, ваш вариант 2 неверен, правильный вариант в стандартах
35. apic 12 13.01.23 14:26 Сейчас в теме
И объясните на кой черт использовать НСтр всегда и везде, если в параметры передаем только один язык? Я понимаю если бы было еще на английском, или это типа на будущее, что вдруг будем портировать на английский рынок и придется добавить на английском что ли?
37. artbear 1437 13.01.23 14:48 Сейчас в теме
(35) Если нет планов переходить на другой язык, конечно, НСтр можно не юзать.
Мне также не нравится его использование, только засоряет код и очень легко пропустить ошибки разного рода.
39. apic 12 13.01.23 15:59 Сейчас в теме
(37) Ну значит я все правильно понял, спасибо
42. tormozit 7046 02.05.23 07:49 Сейчас в теме
(35) Вообще может быть польза и без цели перевода на другие языки. Нстр() обозначает строковый литерал на естественном языке. Таким образом он делит все строковые литералы на программный и естественный языки. А далее среда разработки может это использовать чтобы в глобальном поиске дать флажки
- искать в строковых литералах на естественных языках
- искать в строковых литералах на программных языках
Конечно это все будет иметь смысл, если программисты соблюдают правило "Нстр() всегда на нижнем уровне выражения", т.е. правильно СтрШаблон(Нстр("Текст пуст")) и НЕ правильно Нстр(СтрШаблон("Текст пуст")).
Но пока таких флажков нет даже в EDT. Даже флажка "искать в строковых литералах".
voneska7; +1 Ответить
45. RocKeR_13 1249 24.08.23 17:42 Сейчас в теме
я не пользую ТранзакцияАктивна т.к. не понял зачем оно =) Я транзакцию начал, я же отменил, а вские там маскировки того что внутри имхо зло


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

Или если текущая вложенная транзакция критична в контексте вышестоящей. Если мы в текущей транзакции попадем в исключение и отменим текущую транзакцию, то код пойдет дальше. С помощью ТранзакцияАктивна() мы можем проверить, была ли уже открыта транзакция; при возникновении исключения в текущей транзакции и активности внешней транзакции мы можем вызвать метод ВызватьИсключение, чтобы прервать внешнюю транзакцию.
46. kuzyara 1765 07.09.23 06:51 Сейчас в теме
(45) покажите реальные примеры, пожалуйста. На слух сложно воспринимается.
Оставьте свое сообщение