Безопасная работа с транзакциями во встроенном языке

Публикация № 1026771

Программирование - Практика программирования

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

180
Разбираемся с опасностями использования транзакций во встроенном языке 1С. Познаем ошибку "В данной транзакции уже происходили ошибки". Учимся защищаться от них.

Статья актуальна для версии платформы 8.3.14

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

 

Транзакции применяются для целостного изменения связанных данных, т.е. все действия с базой данных, выполняемые в рамках транзакции или выполняются целиком, или целиком откатываются.

 

  1. Менеджер транзакции
    1. Это условное название единого для сеанса базы 1С внутреннего объекта платформы, управляющего транзакцией. Единый для сеанса значит, что он синхронизируется между толстым клиентским и серверным контекстном сеанса.
    2. Свойства менеджера транзакции
      1. Глубина/Вложенность/Счетчик - целое число - сколько раз была открыта транзакция минус сколько раз была закрыта
      2. Отменена – булево - признак отмены транзакции
  2. Вложенные транзакции
    1. Менеджер транзакции содержит свойство “Глубина”. Если оно больше 1, то транзакция считается вложенной.
    2. Логические (1С) и фактические (СУБД) транзакции
      1. Логическая (1С) транзакция - все операции между началом транзакции и следующим завершением транзакции с тем же значением глубины/вложенности/счетчика транзакций
      2. Фактическая (СУБД) транзакция - совпадает с логической транзакцией с Глубина = 1
    3. Вложенными в 1С могут быть только логические транзакции
    4. При начале логической транзакции увеличивается на 1 свойство “Глубина” менеджера транзакции
    5. При завершении логической транзакции уменьшается на 1 свойство “Глубина” менеджера транзакции
    6. Определить во встроенном языке значение свойства Глубина или хотя бы наличие одной вложенности, не изменяя состояние менеджера транзакции, невозможно.
    7. Вложенность транзакций (ИТС)
    8. Правила использования транзакций (ИТС)
  3. Явные и неявные логические транзакции
    1. Явные - начинаются/завершаются методами встроенного языка
      1. НачатьТранзакцию
      2. ЗафиксироватьТранзакцию/ОтменитьТранзакцию
    2. Неявные - начинаются/завершаются платформой в начале/конце выполнения записи объектов данных
    3. Определить во встроенном языке, является ли транзакция явной/неявной, невозможно.
  4. Сломанные транзакции
    1. Менеджер транзакции содержит признак “Отменена”. Сбрасывается он только при начале фактической транзакции. Если он установлен, то транзакция считается сломанной и фактическая транзакция подлежит отмене при ее любом завершении. Устанавливается он при возникновении ошибки базы данных и при вызове ОтменитьТранзакцию().
    2. Определить во встроенном языке, является ли транзакция сломанной, напрямую невозможно, но можно косвенно с достаточной долей уверенности. Пример будет рассмотрен далее.
    3. Примеры ошибок базы данных
      1. Ошибка выполнения запроса
      2. Необработанное исключение при записи объекта
      3. Отказ при записи объекта (“Не удалось записать <объект>”)
      4. Ошибка установки транзакционной блокировки
      5. Ошибка установки объектной блокировки
    4. Ошибки базы данных и транзакции (ИТС)
    5. Невосстановимые и восстановимые исключения (ИТС)
    6. При обращении к БД в сломанной транзакции платформа выбрасывает ошибку “В данной транзакции уже происходили ошибки”. Здесь возникает разрыв между первичной ошибкой, ломающей транзакцию, и этой вторичной ошибкой. Из-за этого разрыва разработчику обычно бывает очень тяжело добраться до причины первичной ошибки. Поэтому к обработке ошибок в сломанной транзакции нужно подходить очень аккуратно. Пример будет рассмотрен далее.
  5. Работа со ссылками в фактической (СУБД) транзакции
    1. Кэш представлений ссылок
      1. Транзакция имеет собственный кэш представлений ссылок.
      2. Обращение к представлению ссылки может вызывать неявное обращение к БД для обновления кэша представления по этой ссылке.
      3. Примеры обращений к представлению ссылки
        1. “” + Ссылка
        2. Таблица.Сортировать(“Ссылка”) - платформа считывает представления ссылок для сортировки по ним
        3. ЗаписьЖурналаРегистрации(,,, Ссылка) - всегда получает представление от ссылки
      4. При обработке исключений в транзакции часто возникает потребность вывода диагностической информации в лог/пользователю. Тут кроется самая коварная особенность сломанной транзакции. При получении представления ссылки с обновлением кэша в сломанной транзакции платформа выбрасывает необрабатываемое исключение без указания исходной строки, в которой выполнено это обращение. Причем если код выполняется внутри неявной транзакции, то исключение является восстановимым, а иначе не восстановимым. Об этом очень неудобном поведении я сообщал в 1С в 2013г и в 2019г, но невосстановимость этой ошибки до сих пор осталась. Далее будет приведен безопасный подход к решению задачи.
    2. Объектный кэш
      1. Транзакция имеет собственный объектный кэш.
      2. Обращение к полю ссылки может вызывать неявное обращение к БД для обновления кэша объекта по этой ссылке.
      3. Примеры обращений к объектному кэшу
        1. Ссылка.ПометкаУдаления;
        2. Ссылка.ПолучитьОбъект();
      4. Аналогично кэшу представлений ссылок при обращении к объектному кэшу в сломанной транзакции платформа может выбрасывать необрабатываемые и невосстановимые исключения, но в меньшем числе ситуаций.
  6. Взаимоблокировка (Deadlock)
    1. Чтобы снизить вероятность появления взаимоблокировок, нужно стараться устанавливать управляемые блокировки, нужные для всех вложенных транзакций, в самом начале фактической (СУБД) транзакции. Тогда выполнение кода установки управляемых блокировок во вложенных транзакциях не будет изменять состав заблокированных ресурсов и тем самым порядок захвата ресурсов в транзакции будет более стабильным и предсказуемым.

 

Таблица операций, воз