Транзакции: правила использования
Область применения: управляемое приложение, мобильное приложение, обычное приложение.
Транзакции применяются для целостного изменения связанных данных, т.е. все действия с базой данных, выполняемые в рамках транзакции или выполняются целиком, или целиком откатываются.
1. Использование транзакций в 1С:Предприятии обладает рядом особенностей:
не поддерживаются вложенные транзакции (см. подробнее Вложенность транзакций);
при возникновении исключения в общем случае транзакция не может быть зафиксирована – при этом не важно, было ли это исключение обработано или нет (см. подробнее Ошибки базы данных и транзакции, Особенности работы объектов при отмене транзакции);
транзакция может быть инициирована явно в прикладном коде при использовании метода НачатьТранзакцию . Так же платформа 1С:Предприятие неявным образом начинает транзакцию при любой записи в базу данных (см. подробнее Документация платформы. Механизм транзакций);
Эти особенности накладывают ряд требований к написанию кода с использованием транзакций. Несоблюдение этих требований может приводить к возникновению ошибок вида «В этой транзакции уже происходили ошибки», которые может быть крайне сложно воспроизвести и отладить.
1.1. Поскольку исключение не отменяет транзакцию сразу, но запрещает успешное завершение транзакции, то все вызовы НачатьТранзакцию с одной стороны и ЗафиксироватьТранзакцию или ОтменитьТранзакцию с другой стороны должны быть парными.
1.2. Начало транзакции и ее фиксация (отмена) должны происходить в контексте одного метода
Попытка
. // чтение или запись данных
ДокументОбъект.Записать()
ЗафиксироватьТранзакцию();
Исключение
ОтменитьТранзакцию();
. // дополнительные действия по обработке исключения
КонецПопытки;
Попытка
. // чтение или запись данных
ДокументОбъект.Записать()
ЗафиксироватьТранзакцию();
Исключение
ОтменитьТранзакцию();
. // дополнительные действия по обработке исключения
КонецПопытки;
1.3. При использовании транзакций необходимо предусмотреть обработку исключений, придерживаясь следующих правил:
метод НачатьТранзакцию должен быть за пределами блока Попытка-Исключение непосредственно перед оператором Попытка ;
все действия, выполняемые после вызова метода НачатьТранзакцию , должны находиться в одном блоке Попытка, в том числе чтение, блокировка и обработка данных;
метод ЗафиксироватьТранзакцию должен идти последним в блоке Попытка перед оператором Исключение , чтобы гарантировать, что после ЗафиксироватьТранзакцию не возникнет исключение;
необходимо предусмотреть обработку исключений – в блоке Исключение нужно сначала вызвать метод ОтменитьТранзакцию , а затем выполнять другие действия, если они требуются;
рекомендуется в блоке Исключение делать запись в журнал регистрации;
при использовании вложенных транзакций (см. п. 1.4) в конце блока Исключение рекомендуется добавить оператор ВызватьИсключение . В противном случае исключение не будет передано выше по стеку вызовов, там не сработает обработка исключения, внешняя транзакция не будет явным образом отменена и платформа вызовет исключение «В данной транзакции происходила ошибка»
НачатьТранзакцию();
Попытка
БлокировкаДанных = Новый БлокировкаДанных;
ЭлементБлокировкиДанных = БлокировкаДанных.Добавить(«Документ.ПриходнаяНакладная»);
ЭлементБлокировкиДанных.УстановитьЗначение(«Ссылка», СсылкаДляОбработки);
ЭлементБлокировкиДанных.Режим = РежимБлокировкиДанных.Исключительный;
БлокировкаДанных.Заблокировать();
. // чтение или запись данных
ЗаписьЖурналаРегистрации(НСтр(«ru = ‘Выполнение операции'»),
УровеньЖурналаРегистрации.Ошибка,
,
,
ОбработкаОшибок.ПодробноеПредставлениеОшибки(ИнформацияОбОшибке()));
ВызватьИсключение; // есть внешняя транзакция
1.4. Использование вложенных транзакций приводит к усложнению кода. Принимая решение об использовании этой возможности, нужно очень взвешенно оценить решаемую задачу: возможно, это усложнение просто не оправдано.
1.4.1. Не стоит усложнять код, явно используя метод НачатьТранзакцию , когда кроме записи объекта другие действия c базой данных не делаются – платформа при записи сама откроет транзакцию.
Не нужно явно открывать транзакцию тогда, когда не требуется выполнять ответственное чтение данных. Например, обычно ответственное чтение не требуется при записи нового объекта (нового набора записей регистра).
При использовании методов ПолучитьОбъект (или Прочитать для наборов записей) необходимо анализировать должно ли чтение быть отвественным и в зависимости от этого принимать решение о явном использовании метода НачатьТранзакцию .
Попытка
ДокументОбъект = Документы.ПриходнаяНакладная.СоздатьДокумент();
. // действия по заполнению объекта
ДокументОбъект.Записать();
Исключение
. // действия по обработке исключения
КонецПопытки;
НачатьТранзакцию();
Попытка
ДокументОбъект = Документы.ПриходнаяНакладная.СоздатьДокумент();
. // действия по заполнению объекта
ДокументОбъект.Записать();
ЗафиксироватьТранзакцию();
Исключение
ОтменитьТранзакцию();
КонецПопытки;
1.4.2. Если метод рассчитан на вызов только в рамках уже открытой транзакции (например, метод предназначен для вызова только из событий ПередЗаписью , ОбработкаПроведения и т.п.) в общем случае явным образом открывать в нем транзакцию не имеет никакого практического смысла.
1.4.3. При необходимости повысить качество сообщений об ошибках – на каждом уровне разработчик может предусмотреть свою обработку исключений, для чего, возможно, потребуется открыть вложенную транзакцию.
Пример. Вызывается метод ДобавитьЭлектроннуюПодпись . Внутри, если что-то пошло не так, нужно обработать исключение и добавить текст вида: «Не удалось добавить электронную подпись к объекту %ПредставлениеОбъекта% по причине:%ОписаниеОшибки%». В противном случае исключение будет обработано выше по стеку вызовов, например, при записи файла и будет выдано сообщение вида: «Не удалось записать файл %ИмяФайла% по причине: %ОписаниеОшибки%», где в «%ОписаниеОшибки%», будет просто указание на строчку кода и пользователю будет непонятно, зачем вообще программа записывала файл, если он просто его подписывал.
1.4.4. При обработке исключения, если транзакция все еще активна, например, исключение возникло во вложенной транзакции, нельзя обращаться к базе данных, так как это приведет к исключению «В этой транзакции уже происходили ошибки». При этом нужно учитывать, что обращение к базе данных может быть неявным, например, для получения представления ссылки.
2. Ограничение на длину (продолжительность) транзакции.
2.1. В общем случае в рамках одной транзакции нужно выполнять только те действия, которые неделимы, исходя из бизнес-логики.
Пример. При проведении документа записывается документ и его движения в регистрах. Если не прошла запись хотя бы в один регистр вся операция проведения должна быть отменена.
2.1.1. Если с точки зрения бизнес-логики действия могут быть выполнены по отдельности, то их в общем случае не следует объединять в одну транзакцию.
2.1.2. Исключением из п.2.1.1 могут быть случаи, когда с целью оптимизации несколько несвязанных объектов обрабатываются в рамках одной транзакции. В этом случае необходимо взвешенно подходить к выбору порции обработки данных: нужно стремиться к достижению золотой середины между длительностью одной транзакции и объемом фиксируемых данных с одной стороны и количеством транзакций с другой.
2.2. Следует избегать транзакций, которые выполняются длительное время.
Например, неправильно : для загрузки адресного классификатора записывать все данные, относящиеся к одной версии классификатора в одной транзакции, для того, чтобы в случае ошибки откатить целиком загружаемую версию классификатора. Т.к. данных по одной версии классификатора много (объем около 1 Гб), то для выполнения такой транзакции, во-первых, может не хватить оперативной памяти (особенно при использовании файловой информационной базы на 32-разрядной ОС), а, во-вторых, такая операция будет выполняться достаточно долго и ее нельзя будет оптимизировать за счет выполнения в несколько потоков.
Правильно: разбить загрузку новой версии классификатора на небольшие порции так, чтобы запись порции в одной транзакции не превышала 20 секунд в условиях высоконагруженной информационной системы и реализовать функциональность по откату к предыдущей версии в случае ошибки. Максимальная продолжительность указана исходя из того, что время ожидания установки транзакционной блокировки данных в информационной базе по умолчанию равно 20 сек.
2.2.1 Чем дольше выполняется транзакция, тем большее время будут заняты ресурсы сервера 1С:Предприятия и СУБД. Как правило длинные транзакции занимают следующие ресурсы:
- в ходе выполнения транзакции все изменения в базе данных записываются в журнал транзакций, что необходимо для возможности откатить транзакцию;
- блокировки, установленные в транзакции, остаются до конца транзакции;
- на сервере 1С:Предприятия блокировки занимают оперативную память;
- другие ресурсы, необходимые самой бизнес-логике, которая выполняется в транзакции.
Все это в целом может снижать эффективность использования ресурсов.
2.2.2. Если две транзакции пересекаются по блокируемым ресурсам, то транзакция, которая начала выполняться позже, будет ожидать возможность установления блокировки ограниченное время (по умолчанию – 20 секунд), после чего будет завершена с исключением «Превышено время ожидания установки блокировки». Поэтому длинные транзакции могут сильно снижать удобство параллельной работы пользователей.
Возникновение таких исключений – это повод провести анализ действий, которые выполняются в конфликтующих транзакциях
возможно, какие-то действия можно вынести за транзакцию (см. п. 2.4);
если действие вынести нельзя, то нужно постараться оптимизировать алгоритм его выполнения;
также нужно проанализировать оптимальность устанавливаемых блокировок (см. группу стандартов Избыточные блокировки и методы оптимизации )
2.3. В рамках транзакции нужно стремиться выполнять минимум действий – только те, которые нельзя в соответствии с бизнес-логикой выполнять вне транзакции. В частности:
сложные, ресурсоемкие расчеты нужно стремиться делать до начала транзакции, если это позволяет бизнес-логика;
если расчет должен выполняться в транзакции, то нужно стремиться сделать его как можно более простым. Например, контроль остатков можно делать уже после записи простым запросом к записываемому регистру;
проверка заполнения объекта должна делаться вне транзакции (см. Проверки, выполняемые в и вне транзакции записи объекта);
запросы, перед выполнением которых не нужно устанавливать блокировку данных, нужно стремиться выполнять до начала транзакции (см. Ответственное чтение данных);
запросы, выполняемые в рамках транзакций нужно стремиться оптимизировать (см. группу стандартов Оптимизация запросов )
не следует в транзакции вызывать внешние ресурсы (сетевые папки, интранет- и интернет-сайты, почтовые, FTP-, HTTP-, веб-сервисы и т.п.):
обращение к внешнему ресурсу может быть длительным;
ресурс может быть недоступен;
может произойти ошибка тайм-аута.
3. Обязательное использование транзакции.
3.1. В случае, если для ускорения операции записи в регистр используется отключение итогов, такую операцию вместе с отключением и включением итогов необходимо выполнять в транзакции, иначе в других сеансах может возникнуть ошибка при получении среза последних.
НаборЗаписей = РегистрыСведений.КурсыВалют.СоздатьНаборЗаписей();
НаборЗаписей.Отбор.Валюта.Установить(ВалютаСсылка);
НаборЗаписей.Загрузить(ТаблицаКурсов);
НаборЗаписей.ОбменДанными.Загрузка = Истина;
НачатьТранзакцию();
Попытка
РегистрыСведений.КурсыВалют.УстановитьИспользованиеИтогов(Ложь);
НаборЗаписей = РегистрыСведений.КурсыВалют.СоздатьНаборЗаписей();
НаборЗаписей.Отбор.Валюта.Установить(ВалютаСсылка);
НаборЗаписей.Загрузить(ТаблицаКурсов);
НаборЗаписей.ОбменДанными.Загрузка = Истина;
ЗафиксироватьТранзакцию();
Исключение
ОтменитьТранзакцию();
ВызватьИсключение;
КонецПопытки;
Транзакции в 1С
В этом материале мы расскажем о «зоне ответственности» транзакций «1С». О том, что такое блокировки и взаимоблокировки мы уже писали, так вот, данные действия являются прямым следствием транзакций «1С».
Что же из себя представляет транзакция в 1С?
По определению, транзакция в 1С это последовательность действий, которая переводит базу данных из одного целостного состояния в другое целостное состояние. К примеру — процесс перевода денег с одной банковской карты на другую.
Итог транзакций может быть диаметрально противоположен: они либо выполняются до конца, либо в принципе невыполнимы. Такого понятия как «частично выполненная транзакция» нет. В СУБД транзакции фиксируются с помощью COMMIT.
Свойства транзакций
Обозначим наиболее часто встречающие требования к транзакциям «1С» — ACID (Atomicity, Consistency, Isolation, Durability).
Есть перечень свойств, которые должны быть у каждой транзакции:
1. Атомарность (неделимость).
Суть ее состоит в том, чтобы по окончании транзакции все сведения были согласованы. Ведь даже если добавили незначительную запись, то вполне может возникнуть рассогласование.
Рассмотрим пример, когда происходит попытка внести запись вне транзакции. Т.е. вначале пользователь добавляет запись в основную таблицу, а потом в индексы. При этом возможно будет внести новые данные лишь в первый индекс. Во второй сделать это будет проблематично, могут возникнуть непредвиденные помехи (к примеру, отключится электропитание).
Новые сведения получат статус «несогласованных» – т.е. в таблице все отражено, а в индексе нет информации. На этом этапе и «подключается» атомарность для того, чтобы те или иные действия были завершены.
2. Изоляция.
Данное свойство «отвечает» за параллельный режим работы пользователей и помогает предотвратить потерю общих данных. К примеру, может возникнуть ситуация, при которой два пользователя вносят изменения в один документ, и, как результат, стирают изменения друг друга. Здесь и приходит на помощь изоляция.
Главная ее задача – защищать информацию от других транзакций. Блокировки при этом обеспечивают изоляцию транзакции.
Cпособы создания транзакции
1. Автоматический. К примеру, обработать проведение документа или запись элемента справочника в информационную базу. Увидеть, активна ли транзакция или нет, поможет опция «Транзакция активна()».
2. Самостоятельный. (Т.е., когда ее создает разработчик). Пошагово это выглядит так: «Начать Транзакцию()», в заключении «Зафиксировать Транзакцию()», либо (при необходимости) «Отменит Транзакцию()».
Отметим, что вложенные транзакции 1С не поддерживает. Если пользователь периодически открывает транзакцию, она «объединяется» и «превращается» в одну. Если проводится фиксация или отмена, то это распространяется на все транзакции, которые были активированы до этого.
Возможно, что вас не устраивает производительность «1С». Повысить скорость возможно с помощью верно созданной транзакции. Если пользователь все проводит правильно, то не будет блокировок и взаимоблокировок. Не будут отображаться ошибки, и система не будет отменять проведение документа.
Использование транзакций
Использование транзакций в 1С
Транзакция предназначена для записи в информационную базу согласованных изменений. Все изменения, внесенные в информационную базу после начала транзакции, будут затем либо целиком записаны, либо целиком отменены. Ключевыми функциями для работы в транзакции всегда являлись: начать, завершить, отменить. Вспомогательные: активность другой транзакции, возможность начала транзакции.
Простой пример использования транзакций приведен в коде ниже, который устанавливает зависимость создания элемента справочника номенклатуры и элемента справочника единицы измерения, владельцем которого является номенклатура. Если не удалось создать базовую единицу измерения, то и нельзя сохранять саму новую номенклатуру. Все действия происходят в транзакции:
Код 1C v 8.х
Попытка
// Начать транзакцию
НачатьТранзакцию( РежимУправленияБлокировкойДанных.Автоматический );
// Создать элемент номенклатура, записать
// Создать элемент единицы измерения, установить владельца, записать
// Обновить базовую единицу измерения в номенклатуре, записать
// Зафиксировать транзакцию
ЗафиксироватьТранзакцию();
Исключение
// Отменить транзакцию
ОтменитьТранзакцию();
КонецПопытки;
Метод «НачатьТранзакцию» должен располагаться непосредственно перед оператором «Попытка»
Метод НачатьТранзакцию должен быть за пределами блока Попытка-Исключение непосредственно перед оператором Попытка .
ПодготовкаДанных(); Попытка НачатьТранзакцию(); // ошибка СложнаяФункция(); ЗафиксироватьТранзакцию(); Исключение ОтменитьТранзакцию(); ВызватьИсключение; КонецПопытки;
ПодготовкаДанных(); НачатьТранзакцию(); Попытка СложнаяФункция(); ЗафиксироватьТранзакцию(); Исключение ОтменитьТранзакцию(); ВызватьИсключение; КонецПопытки;
См. также
- ИТС: Транзакции: правила использования
- Вы не умеете работать с транзакциями
Экранирование кода
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the «Software»), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED «AS IS», WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. —>
