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