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

07.12.23

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

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

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

Содержание:


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

Описан в ИТС (п.1.3)

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

 

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

Диагностика

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

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

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

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

Примеры

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

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

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

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

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

Диагностика

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

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

Примеры

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

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

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

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

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

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

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

Диагностика

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

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

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

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

Примеры

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

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

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

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

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

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

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

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

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

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

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

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

 

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

Диагностика

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

Неправильно

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

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

Правильно


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

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

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

Диагностика

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

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

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

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

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

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

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

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

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

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

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

Диагностика

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

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

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

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

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

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

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

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

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

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

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

Правильно:

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

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

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

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

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

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

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

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

 - работа с ftp

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

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

Источники:

8. Внутри транзакции недопустимо подавлять ошибки, вызывающие событие 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:

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Правильно:

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

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

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

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

 

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

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

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

 

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

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

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

 

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

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

 

10.4 Почему не рекомендуется передавать текст ошибки вместо непосредственной записи в ЖР?

 
 Это приводит к нарушению пункта 3.4 ИТС: Перехват исключений в коде

 

10.5 Когда нужно использовать оператор ВызватьИсключение в блоке Исключение...КонецПопытки без параметра?

 
 Когда нет необходимости переопределять текст ошибки

 

10.6 Почему нужно использовать блокировки при многопоточной загрузке данных?

 
 Чтобы не было ошибки "нарушение целостности чтения объекта"

 

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

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

 

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

 

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

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

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

См. также

Планы обмена VS История данных

Обмен между базами 1C Механизмы платформы 1С Платформа 1С v8.3 Бесплатно (free)

Вы все еще регистрируете изменения только на Планах обмена и Регистрах сведений?

11.12.2023    6409    dsdred    36    

111

1С-ная магия

Механизмы платформы 1С Бесплатно (free)

Язык программирования 1С содержит много нюансов и особенностей, которые могут приводить к неожиданным для разработчика результатам. Сталкиваясь с ними, программист начинает лучше понимать логику платформы, а значит, быстрее выявлять ошибки и видеть потенциальные узкие места своего кода там, где позже можно было бы ещё долго медитировать с отладчиком в поисках источника проблемы. Мы рассмотрим разные примеры поведения кода 1С. Разберём результаты выполнения и ответим на вопросы «Почему?», «Как же так?» и «Зачем нам это знать?». 

06.10.2023    18473    SeiOkami    46    

118

Дефрагментация и реиндексация после перехода на платформу 8.3.22

Механизмы платформы 1С Платформа 1С v8.3 Бесплатно (free)

Начиная с версии платформы 8.3.22 1С снимает стандартные блокировки БД на уровне страниц. Делаем рабочий скрипт, как раньше.

14.09.2023    12087    human_new    27    

74

Валидация JSON через XDTO (включая массивы)

WEB-интеграция Универсальные функции Механизмы платформы 1С Платформа 1С v8.3 Конфигурации 1cv8 Бесплатно (free)

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

28.08.2023    8820    YA_418728146    6    

141

Внешние компоненты Native API на языке Rust - Просто!

Механизмы платформы 1С Платформа 1С v8.3 Конфигурации 1cv8 Бесплатно (free)

Внешние компоненты для 1С можно разработывать очень просто, пользуясь всеми преимуществами языка Rust - от безопасности и кроссплатформенности до удобного менеджера библиотек.

20.08.2023    6279    sebekerga    54    

94

Все скопируем и вставим! (Буфер обмена в 1С 8.3.24)

Механизмы платформы 1С Платформа 1С v8.3 Конфигурации 1cv8 Бесплатно (free)

Рассмотрим новую возможность 8.3.24 и как её можно эффективно использовать

27.06.2023    15986    SeiOkami    31    

103

Методы работы с универсальным отчетом в подсистеме "Варианты отчетов" на БСП

Механизмы платформы 1С Платформа 1С v8.3 Конфигурации 1cv8 Бесплатно (free)

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

30.05.2023    5005    quazare    4    

87

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

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

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

15.05.2023    8975    improg    57    

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

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

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

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

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

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

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

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

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

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

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

Всегда.

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

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


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

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

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

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

//Например, закрыть форму:
Исключение
	ДлительныеОперацииКлиент.ЗакрытьФормуДлительнойОперации(ФормаДлительнойОперации);
	ВызватьИсключение;
КонецПопытки;
Показать
43. frkbvfnjh 785 02.05.23 08:29 Сейчас в теме
(41) Хммммм, это же получается что то типа try ... finally в Delphi. Спасибо, возьму на заметку!
23. buganov 200 06.12.22 18:14 Сейчас в теме
(20)чтобы обогатить данные об ошибке, например
27. gybson 08.12.22 11:04 Сейчас в теме
(20) Обработка исключения это не только * транзакции. Например там можно сделать запись в журнал и Отказ = Истина, прочие обработки потом пробросить его наверх
28. kuzyara 1900 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 200 06.12.22 18:18 Сейчас в теме
(15) Это из разряда фантастики. Как на открытии транзакции может завалиться, и при этом все еще пытаться обработать исключение?
16. kuzyara 1900 06.12.22 05:03 Сейчас в теме
(14) тоже так думаю, всегда писал в попытке - но есть ИТС https://its.1c.ru/db/v8std/content/783/hdoc и там в пункте 1.3 четко сказано что вне. Так что считаю за формальность
Прикрепленные файлы:
17. quazare 3586 06.12.22 08:35 Сейчас в теме
Понравилась вот эта фраза "Статья написана в личных целях для онбординга новых сотрудников, является частью соглашений по стилю кода."

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

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

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

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

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

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


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

Или если текущая вложенная транзакция критична в контексте вышестоящей. Если мы в текущей транзакции попадем в исключение и отменим текущую транзакцию, то код пойдет дальше. С помощью ТранзакцияАктивна() мы можем проверить, была ли уже открыта транзакция; при возникновении исключения в текущей транзакции и активности внешней транзакции мы можем вызвать метод ВызватьИсключение, чтобы прервать внешнюю транзакцию.
46. kuzyara 1900 07.09.23 06:51 Сейчас в теме
(45) покажите реальные примеры, пожалуйста. На слух сложно воспринимается.
47. Flextor74 07.12.23 08:55 Сейчас в теме
48. kuzyara 1900 07.12.23 10:24 Сейчас в теме
(47) добавил оглавление и 3 дополнительных параграфа
Оставьте свое сообщение