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

15.05.24

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

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

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

Содержание:


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

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

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

 

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

Диагностика

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

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

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

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

Примеры

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

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

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

На 3 из 4 поддерживаемых СУБД, при вызове НачатьТранзакцию платформа идет в СУБД и открывает там транзакцию. В этот момент может произойти все что угодно: нехватка памяти для очередного соединения с СУБД, разрыв связи, ошибка дисковой подсистемы.
Давайте делать, как написано в документации: НачатьТранзакцию до начала Попытки, Зафиксировать — внутри, Откатить — в обработке исключения. Стандартная для всех языков практика, в общем-то.

Хотя науке и неизвестны случаи выброса перехватываемых исключений из НачатьТранзакцию().

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

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 Почему нужно использовать блокировки при многопоточной загрузке данных?

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

10.7 Какие действия будут отменены в результате выполнения ОтменитьТранзакцию()

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

Ответы:

  1. Будут отменены и вложенная и внешняя транзакция
  2. Только внешняя транзакция. ОтменитьТранзакцию() во вложенных транзакциях не работает, т.к. они не поддерживаются
  3. Т.к. вложенные транзакции технологическая платформа не поддерживает, то в начале вложенной транзакции возникнет ошибка
  4. Только вложенная транзакция. ОтменитьТранзакцию() во внешней транзакции нужно выполнять отдельно.
 
 Ответ

10.8 Использование конструкции Попытка...Исключение...КонецПопытки внутри транзакции:

  1. Не имеет смысла, транзакция при возникновении любой исключительной ситуации все равно откатывается
  2. Не всегда оправданно, транзакция откатывается, если исключительная ситуация определена как восстановимая
  3. Иногда оправданно, транзакция не откатывается, если исключительная ситуация не определена как восстановимая
  4. Мешает понять, что на самом деле происходит, если исключение не логируется
  5. Верны ответы 1 и 4
  6. Верны ответы 2, 3 и 4
 
 Ответ

10.9 При использовании конструкции Попытка...Исключение… КонецПопытки внутри вложенной транзакции, если внутри этой конструкции возникла восстановимая исключительная ситуация:

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

10.10 При использовании конструкции Попытка...Исключение… КонецПопытки снаружи вложенной транзакции, если внутри этой транзакции возникла восстановимая исключительная ситуация:

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

Ответ на вопрос разобран в примере в источнике: Филиппов Е.В., Настольная книга 1С:Эксперта по технологическим вопросам. 2-е издание, стр. 49-50:
"Пример 2
Сначала в процедуру ПриЗаписи модуля справочника Организации добавим строку:

ЭтотОбъект.ВыполнитьНесуществующийМетод(); // (такого метода у объекта не создавали)

Далее изменим код из примера 1:

НачатьТранзакцию();
Попытка
    СпрСсылка = Справочники.Организации.НайтиПоКоду("000001");
    СпрОбъект = СпрСсылка.ПолучитьОбъект();
    СпрОбъект.НаименованиеПолное = ТекущаяДата();
    //это чтобы отследить, зафиксирована транзакция или нет
    СпрОбъект.Записать(); // неявная транзакция, там, как помним, ошибка
Исключение
КонецПопытки;
Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ
    | ПлатежноеПоручение.Ссылка
    |ИЗ
    | Документ.ПлатежноеПоручение КАК ПлатежноеПоручение";
Результат = Запрос.Выполнить();
ЗафиксироватьТранзакцию();

Получим ошибку:

Ошибка при вызове метода контекста (Выполнить)
Результат = Запрос.Выполнить();
По причине: Ошибка выполнения запроса.
По причине: В данной транзакции уже происходили ошибки!

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

...

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

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

 

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

 

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

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

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

См. также

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

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

23.06.2024    7425    bayselonarrend    20    

154

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

Поделюсь своим опытом аудита кода авторских продуктов с Infostart.ru как одним из элементов применения DevOps-практик внутри Инфостарт. Будет настоящий код, боевые скриншоты, внутренние мемы от команды ИТ-лаборатории Инфостарт и прочее мясо – все, что любят разработчики.

10.04.2024    13356    artbear    85    

108

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

Пример использования «Сервисов интеграции» без подключения к Шине и без обменов.

13.03.2024    5938    dsdred    16    

80

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

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

11.12.2023    11216    dsdred    44    

130

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

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

06.10.2023    23753    SeiOkami    48    

135

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

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

14.09.2023    18822    human_new    27    

80

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

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

28.08.2023    14728    YA_418728146    7    

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

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

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

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

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

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

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

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

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

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

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

Всегда.

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

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


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

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

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

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

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


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

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

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

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

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

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

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

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

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

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


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

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