Транзакции и управляемые блокировки
Общая информация
Приложения «1С:Исполнителя» хранят данные в СУБД. Любое чтение или запись данных в СУБД выполняются в транзакции.
Транзакция — это неделимая, с точки зрения воздействия на базу данных, последовательность операций манипулирования данными, выполняющаяся по принципу «все или ничего». Она переводит базу данных из одного целостного состояния в другое целостное состояние. Если по каким-либо причинам одно из действий транзакции невыполнимо или произошло какое-либо нарушение работы системы, база данных возвращается в то состояние, которое было до начала транзакции (происходит откат транзакции).
Методы работы с транзакциями
«Исполнитель» предоставляет следующие методы для работы с транзакциями:
Транзакции.Начать()
— откр ывает транзакцию и возвращает объектТранзакция
.Транзакция.Закрыть()
— закрывает текущую транзакцию. Если закрытие происходит из-за выброса исключения, транзакция отменяется; иначе — фиксируется.Транзакция.Откатить()
— закрывает транзакцию и откатывает ее.Транзакция.Фиксировать()
— закрывает транзакцию и фиксирует ее. Если транзакция находится в состоянии, при котором возможно только отменить изменения, то при попытке зафиксировать такую транзакцию будет выброшено исключение.
Для работы с транзакциями во встроенном языке используйте модификатор исп
. При вызове метода Транзакции.Начать()
будет выполнено открытие новой транзакции, а при выходе из области видимости транзакция будет автоматически зафиксирована или откачена, если выполнение кода завершится исключением. По возможности рекомендуется минимизировать исполняемый внутри транзакции код, а также избегать вызова внешних систем (например, HTTP-запросов).
Пример изменения объекта в рамках транзакции:
метод ЗаменитьДоговор(Документы: ЧитаемыйМассив<Накладная.Ссылка>,
НовыйДоговор: Договоры.Ссылка)
исп Транзакции.Начать()
для Ссылка из Документы
знч Накладная = Ссылка.ЗагрузитьОбъект(Истина)
если Накладная == Неопределено
// Если ссылка недействительна
продолжить
;
Накладная.Договор = НовыйДоговор
Накладная.Записать()
;
;
При работе с транзакциями может возникнуть необходимость проверить, выполняется ли код в транзакции (например, при работе с операциями ввода-вывода). Для этого предусмотрены следующие методы типа Транзакции
:
ЕстьАктивная()
— возвращаетИстина
при наличии активной транзакции.ПроверитьЕстьАктивная()
— проверяет, есть ли активная транзакция. Если нет, то выбрасываетИсключениеНетАктивнойТранзакции
.ПроверитьНетАктивной()
— проверяет, что активная транзакция отсутствует. При наличии транзакции выбрасываетИсключениеДействиеНеРазрешеноВТранзакции
.
Управляемые блокировки
Работа приложения в многопользовательской среде приводит к тому, что при одновременном чтении и изменении одних и тех же данных конкурирующими транзакциями (разными пользователями) могут возникнуть проблемы одноврем енного доступа.
Получается, что ради увеличения производительности системы нужно допустить параллельное выполнение транзакций. При этом так же нужно обеспечить необходимую степень целостности данных, то есть ограничить параллельность транзакций при работе с одними ресурсами.
«Исполнитель» для этих целей использует одновременно два механизма:
- поддержка изоляции транзакций, реализуемая СУБД;
- управляемые блокировки, реализуемые «Исполнителем».
В подавляющем большинстве случаев уровень изоляции транзакций, который «Исполнитель» использует в СУБД, обеспечивает высокую параллельность работы пользователей и не требует от разработчика каких-либо дополнительных действий:
- Microsoft SQL Server — Read Committed Snapshot;
- PostgreSQL — Read Committed.
Однако есть ряд ситуаций, когда эти уровни изоляции транзакций не могут обеспечить целостность и непротиворечивость данных. В таких случаях вы должны самостоятельно установить управляемые блокировки в программном коде. Они будут обработаны менеджером управляемых блокировок «Исполнителя».
Управляемые б локировки учитывают логическую структуру приложения, поэтому позволяют максимально точно блокировать необходимые области данных. Таким образом, менеджер управляемых блокировок позволяет максимально избежать возникновения избыточных блокировок, блокируя только действительно необходимые области данных.
В результате любая запись данных прежде всего обрабатывается собственным менеджером управляемых блокировок «Исполнителя». Если на уровне «Исполнителя» конфликт управляемых блокировок не обнаруживается, то запрос передается далее, на исполнение СУБД. СУБД использует собственный механизм блокировок для определения конфликтующих транзакций.
Когда устанавливать управляемые блокировки
Управляемые блокировки нужно устанавливать в транзакциях, где читаются данные, котор ые затем будут изменены и записаны в этой же транзакции.
Также управляемые блокировки нужно устанавливать в тех случаях, когда считывается согласованная совокупность данных, содержащаяся в нескольких экземплярах, и согласованность считанных данных нужно поддержать до окончания транзакции.
Существует два режима управляемых блокировок: разделяемый и исключительный.
Разделяемая блокировка устанавливается для того, чтобы данные не были изменены другими транзакциями.
Исключительная блокировка, помимо этого, обеспечивает запрет не только изменения этих данных, но даже их чтения другими транзакциями, устанавливающими управляемые блокировки. Можно сказать, что исключительная управляемая блокировка является средством борьбы с конфликтами блокировок (deadlock).
Типы встроенного языка
Для управления блокировками предназначены несколько типов. Основной из них это тип-одиночка Блокировки
. Он устанавливает блокировку указанного режима на указанное пространство блокировок.
Для каждого возможного пространства блокировок в «Исполнителе» существуют собственные типы, связанные с элементами проекта:
Справочник
имя-справочника.Блокировки.Ссылка
имя-справочника.Блокировки.Код
Этот тип существует, если у справочника есть реквизит Код
.
имя-справочника.Блокировки.имя-дополнительного-пространства-блокировок
Документ
имя-документа.Блокировки.Ссылка
имя-документа.Блокировки.Номер
Этот тип существует, если у документа есть реквизит Номер
.
имя-документа.Блокировки.имя-дополнительного-пространства-блокировок
План обмена
имя-плана-обмена.Блокировки.Ссылка
имя-плана-обмена.Блокировки.имя-дополнительного-пространства-блокировок
Пользователи
Пользователи.Блокировки.Ссылка
Регистр сведений
имя-регистра-сведений.Блокировки.КлючЗаписи
имя-регистра-сведений.Блокировки.Измерения
Явная установка управляемых блокировок
Чтобы установить управляемую блокировку, создайте ее конструктором, а затем вызовите метод Блокировки. Заблокировать()
. В конструктор блокировки нужно передать режим и значение, которое будет определять конкретный элемент пространства, на который устанавливается блокировка.
Например, в следующем примере разделяемая блокировка устанавливается на элемент справочника Товары, ссылка на который сохранена в переменной СсылкаНаТовар.
пер Блокировка = новый Товары.Блокировки.Ссылка(
РежимБлокировки = РежимБлокировки.Разделяемый,
Ссылка = СсылкаНаТовар
)
Блокировки.Заблокировать(Блокировка)
Если нужно установить исключительную блокировку, то режим можно не указывать, т.к. РежимБлокировки.Исключительный
это его стандартное значение:
Блокировки.Заблокировать(новый Товары.Блокировки.Код(Код = "00001"))
Неявная установка управляемых блокировок
Существует несколько методов встроенного языка, в которых вы можете установить управляемые блокировки неявным способом, просто указав необходимость их установок. Это следующие методы:
имя-справочника.Ссылка.ЗагрузитьОбъект()
;Пользователи.ЗагрузитьОбъект()
;
Стандартно эти методы не устанавливают блокировки, но если в них передать параметр Истина, тогда блокировка будет установлена. Например, запись:
пер ЭлементСправочника = СсылкаНаТовар.ЗагрузитьОбъект(Истина)
установит следующую исключительную блокировку:
Блокировки.Заблокировать(новый Товары.Блокировки.Ссылка(Ссылка = СсылкаНаТовар))
Блокировка исключительная потому, что подразумевается, что вы загружаете объект для того, чтобы изменить его данные и записать.
Кроме того, у регистра сведений существует два перегруженных метода, которые позволяют установить исключительную управляемую блокировку по значениям фильтра или ключа записи — имя-регистра-сведений.Заблокировать()
. В один из них вы можете передать фильтр регистра, по которому будут заблокированы изменения, а в другой — ключ записи, чтобы заблокировать его.
Например, запись:
КурсыВалют.Заблокировать(Запись.КлючЗаписи)
установит следующую исключительную блокировку:
Блокировки.Заблокировать(новый КурсыВалют.Блокировки.КлючЗаписи(КлючЗаписи = Запись.КлючЗаписи))
Автоматическая установка управляемых блокировок
При записи данных методами встроенного языка (например имя-справочника.Записать()
) или из интерфейса прикладного решения (например, нажатие Сохранить) «Исполнитель» автоматически устанавливает исключительные управляемые блокировки. Они полностью аналогичны тем блокировкам, которые вы м огли бы установить самостоятельно.
Блокировки устанавливаются на все имеющиеся пространства блокировок. Если данные изменяются, например, меняются код, реквизиты справочника или значения измерений регистра, то блокируются пространства как со старыми значениями, так и с новыми.
Например, при выполнении:
пер ТоварыЭлемент = СсылкаНаТовар.ЗагрузитьОбъект()
ТоварыЭлемент.Записать()
Будет автоматически установлена, в числе прочих, такая блокировка:
Блокировки.Заблокировать(новый Товары.Блокировки.Ссылка(СсылкаНаТовар))