Правильное использование явных транзакций и блока Попытка-Исключение в 1С

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

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

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

В чем ошибка конструкции с НачатьТранзакцию внутри Попытки

Проанализируем шаблон кода, который часто вызывают на обсуждение начинающие разработчики:


Ошибка = Ложь;
Попытка
    НачатьТранзакцию();
    // Что-то делаем с базой данных
    Если НЕ Ошибка Тогда
        ЗафиксироватьТранзакцию();
    Иначе
        ОтменитьТранзакцию();
    КонецЕсли;
Исключение
    Если ТранзакцияАктивна() Тогда
        ОтменитьТранзакцию();
    КонецЕсли;
    // Обработка ошибки
КонецПопытки;

В этой конструкции кроется сразу несколько существенных рисков:

  1. Возникновение ошибки «В данной транзакции уже происходили ошибки»: Если ошибка произойдет на уровне СУБД (например, конфликт блокировок или нарушение уникальности), платформа 1С автоматически помечает текущую транзакцию как испорченную (выставляется внутренний служебный флаг запрета фиксации). Если в коде внутри блока Попытка после такой ошибки произойдет попытка записи других данных или вызов дополнительных функций, 1С вызовет критическое исключение с сообщением о том, что в транзакции уже происходили ошибки.
  2. Ошибки во внешнем транзакционном контексте: Если данный код вызывается из другого метода, где транзакция уже была открыта ранее (например, в подписке на событие ПриЗаписи), вызов ОтменитьТранзакцию() отменит всю внешнюю транзакцию целиком, а счетчик транзакций сбросится. Попытка внешнего кода завершить свою транзакцию вызовет аварийное завершение работы программы.
  3. Избыточные проверки флага ошибки: Управление потоком выполнения через логические флаги вроде Ошибка = Истина внутри блока Попытка усложняет код. Блок обработки исключений предназначен именно для того, чтобы перехватывать любые сбои автоматически без ручных проверок.

Официальный стандарт 1С: классическая структура транзакции

Согласно стандартам разработки платформы 1С:Предприятие, вызов НачатьТранзакцию() должен располагаться строго ПЕРЕД блоком Попытка, а фиксация транзакции — быть ПОСЛЕДНИМ оператором основного блока Попытка. Отмена транзакции должна происходить на первой строчке в секции Исключение.

Разберем эталонный шаблон выполнения транзакционных операций по шагам:


// 1. Подготовительные действия вне транзакции
// (выборка данных, проверки, внешние HTTP-запросы)

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

Посмотрим на преимущества этого подхода:

  1. Если сбой произойдет до начала транзакции (в подготовительном коде), транзакция не откроется и ресурсы СУБД не будут заблокированы.
  2. Если ошибка случится при записи объектов БД внутри блока Попытка, управление сразу же передается в секцию Исключение, где первым делом вызывается ОтменитьТранзакцию(), снимая все блокировки СУБД.
  3. Фиксация транзакции методом ЗафиксироватьТранзакцию() гарантированно вызывается только тогда, когда все предыдущие строки кода выполнились успешно и без исключений.

Особенности работы и правила безопасности при использовании транзакций

Проанализируем ключевые моменты, которые необходимо учитывать при работе с транзакциями в 1С:

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

Альтернативный вариант: временная транзакция для получения расчетных данных

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

Разберем пример реализации такой схемы:


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

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

← На главную