Как правильно использовать Попытку и Транзакцию в 1С:Предприятие

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

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

В чем разница между транзакцией и блоком Попытка

Для понимания архитектуры работы платформы важно четко разделять назначение этих двух механизмов:

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

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

Особенности вложенности транзакций и ошибки при их фиксации

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

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

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

Правильный шаблон совместного использования транзакций и Попытки

Рассмотрим эталонный шаблон кода, который исключает возникновение вторичных ошибок и корректно освобождает блокировки данных:


НачатьТранзакцию();
Попытка
    // Выполняем запись и проведение взаимосвязанных документов
    ДокументОснованиеОбъект.Записать(РежимЗаписиДокумента.Проведение, РежимПроведенияДокумента.Неоперативный);
    СвязанныйДокументОбъект.Записать(РежимЗаписиДокумента.Проведение, РежимПроведенияДокумента.Неоперативный);
    
    // Фиксация выполняется последней строкой внутри блока Попытка
    ЗафиксироватьТранзакцию();
Исключение
    // 1. Получаем описание исходной ошибки до любых других действий
    ТекстОшибки = ПодробноеПредставлениеОшибки(ИнформацияОбОшибке());
    
    // 2. Проверяем активность транзакции и немедленно выполняем откат
    Если ТранзакцияАктивна() Тогда
        ОтменитьТранзакцию();
    КонецЕсли;
    
    // 3. Записываем ошибку в системный журнал без участия транзакции БД
    ЗаписьЖурналаРегистрации(
        "ОбработкаДокументов.ОшибкаПеревешивания",
        УровеньЖурналаРегистрации.Ошибка,,,
        ТекстОшибки,
        РежимЗаписиЖурналаРегистрации.Транзакционная = Ложь
    );
    
    // 4. Пробрасываем исключение дальше при необходимости
    ВызватьИсключение "Не удалось провести связанные документы: " + ОписаниеОшибки();
КонецПопытки;

Пошаговый разбор ключевых правил

Разберем по шагам, почему код должен быть структурирован именно так:

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

Практические рекомендации по надежности

Для предотвращения взаимных блокировок (deadlocks) при одновременной модификации нескольких объектов рекомендуется соблюдать следующие правила:

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