Ответственное чтение данных

Область применения: управляемое приложение, мобильное приложение, обычное приложение.

1. Общие рекомендации по использованию транзакций.
2. Выбор: исключительная или разделяемая блокировка.
3. Избегать длинных транзакций.

1. Общие рекомендации по использованию транзакций.

Область применения (уточнение): управляемое приложение, обычное приложение.

1.1. Если чтение данных из информационной базы должно быть ответственным, следует производить такое чтение в транзакции с предварительной установкой управляемых блокировок. В общем случае, ответственным следует считать любое чтение, на основе результатов которого производятся какие-либо изменения в информационной базе или принимаются решения.
Например, ответственное чтение данных требуется в следующих случаях:

  • Чтение данных при проведении, для последующего формирования движений;
  • Чтение данных для последующей целостной передачи в другую систему, например в программы типа «Клиент банк»;
  • Выполнение групповой обработки объектов, при реструктуризации данных в обработчиках отложенного и оперативного обновления ИБ (*)

    * Примечание: перед модификацией ссылочных объектов, обычно, следует устанавливать на них пессимистичные объектные блокировки.

Неправильно:

// 1. Прочитать регистр сведений
Запрос = Новый Запрос(
  "ВЫБРАТЬ РАЗРЕШЕННЫЕ
  | ЗаметкиПоПредмету.КоличествоЗаметок КАК КоличествоЗаметок
  |ИЗ
  | РегистрСведений.ЗаметкиПоПредмету КАК ЗаметкиПоПредмету
  |ГДЕ
  | ЗаметкиПоПредмету.Предмет = &Предмет");
Запрос.УстановитьПараметр("Предмет", ПредметЗаметок);
Выборка = Запрос.Выполнить().Выбрать();
 
КоличествоЗаметок = 0;
Если Выборка.Следующий() Тогда
  КоличествоЗаметок = Выборка.КоличествоЗаметок;
КонецЕсли;
 
// 2. Записать в регистр сведений
НаборЗаписей = РегистрыСведений.ЗаметкиПоПредмету.СоздатьНаборЗаписей();
НаборЗаписей.Отбор.Предмет.Установить(ПредметЗаметок);
НоваяЗапись = НаборЗаписей.Добавить();
НоваяЗапись.Предмет = ПредметЗаметок;
НоваяЗапись.КоличествоЗаметок = КоличествоЗаметок + 1;
НаборЗаписей.Записать();

Правильно:

// 1. Начать транзакцию для пакета из двух операций чтения и записи регистра
НачатьТранзакцию();

Попытка
  // 2. Установить исключительную блокировку на интересующий диапазон записей регистра,
  // для того чтобы гарантировать, что в момент записи количество заметок не изменилось с момента чтения в каком-либо другом сеансе.
  БлокировкаДанных = Новый БлокировкаДанных;
  ЭлементБлокировкиДанных = БлокировкаДанных.Добавить("РегистрСведений.ЗаметкиПоПредмету");
  ЭлементБлокировкиДанных.УстановитьЗначение("Предмет", ПредметЗаметок);
  ЭлементБлокировкиДанных.Режим = РежимБлокировкиДанных.Исключительный;
  БлокировкаДанных.Заблокировать();
 
  // 3. Прочитать регистр сведений
  Запрос = Новый Запрос(
    "ВЫБРАТЬ РАЗРЕШЕННЫЕ
    | ЗаметкиПоПредмету.КоличествоЗаметок КАК КоличествоЗаметок
    |ИЗ
    | РегистрСведений.ЗаметкиПоПредмету КАК ЗаметкиПоПредмету
    |ГДЕ
    | ЗаметкиПоПредмету.Предмет = &Предмет");
  Запрос.УстановитьПараметр("Предмет", ПредметЗаметок);
 
  Выборка = Запрос.Выполнить().Выбрать();
 
  КоличествоЗаметок = 0;
  Если Выборка.Следующий() Тогда
    КоличествоЗаметок = Выборка.КоличествоЗаметок;
  КонецЕсли;
 
  // 4. Записать в регистр сведений
  НаборЗаписей = РегистрыСведений.ЗаметкиПоПредмету.СоздатьНаборЗаписей();
  НаборЗаписей.Отбор.Предмет.Установить(ПредметЗаметок);
  НоваяЗапись = НаборЗаписей.Добавить();
  НоваяЗапись.Предмет = ПредметЗаметок;
  НоваяЗапись.КоличествоЗаметок = КоличествоЗаметок + 1;
  НаборЗаписей.Записать();
 
  ЗафиксироватьТранзакцию();
Исключение
  // 5. Если при установке блокировки возникла исключительная ситуация из-за того, что регистр уже заблокирован в другом сеансе (или по другим причинам),
  // отменить транзакцию и записать сведения об ошибке в журнал регистрации.
  ОтменитьТранзакцию();
  ЗаписьЖурналаРегистрации(НСтр("ru = 'Заметки'", ОбщегоНазначенияКлиентСервер.КодОсновногоЯзыка()), УровеньЖурналаРегистрации.Ошибка,,, ПодробноеПредставлениеОшибки(ИнформацияОбОшибке()));
  ВызватьИсключение;
КонецПопытки;

В некоторых случаях, ответственное чтение не требуется в силу решаемой прикладной задачи, например:

В некоторых случаях, ответственное чтение не требуется, так как конкурентная работа с данными маловероятна или полностью исключена, например:

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

Например, в системной транзакции выполняются обработчики модулей объектов и соответствующие им подписки на события:

Подробнее – см. документацию к платформе 1С:Предприятие.

Область применения (уточнение): управляемое приложение, обычное приложение.

2. Выбор: исключительная или разделяемая блокировка.

2.1. Если в транзакции производится ответственное чтение данных с их последующим изменением, необходимо установить исключительную управляемую блокировку (до выполнения чтения). В противном случае возможно возникновение взаимоблокировки.

Пример установки исключительной блокировки (без открытия транзакции – в предположении, что ранее уже была открыта системная транзакция):

// 1. Установить исключительную блокировку для ответственного чтения объекта с целью его дальнейшего изменения
Блокировка = Новый БлокировкаДанных;
ЭлементБлокировки = Блокировка.Добавить("Справочник.Приказы");
ЭлементБлокировки.УстановитьЗначение("Ссылка", ПриказСсылка);
ЭлементБлокировки.Режим = РежимБлокировкиДанных.Исключительный; // Можно не указывать, т.к. по умолчанию Исключительный
Блокировка.Заблокировать();

// 2. Получить объект для его дальнейшей модификации
Объект = ПриказСсылка.ПолучитьОбъект();

// Выполнить блокировку объекта от изменения другими режимами или пользователями
Объект.Заблокировать();
Объект.Реквизит = ...
// 3. Записать измененный объект
Объект.Записать();

2.2. Если в транзакции производится ответственное чтение данных без их последующего изменения (например, для формирования движений), необходимо установить разделяемую блокировку на читаемые данные и исключительную блокировку на изменяемые данные.

Пример установки разделяемой блокировки (без открытия транзакции – в предположении, что ранее уже была открыта системная транзакция):

// 1. Установить разделяемую блокировку для ответственного чтения нескольких связанных объектов
Блокировка = Новый БлокировкаДанных;
ЭлементБлокировки = Блокировка.Добавить("Справочник.Приказы");
ЭлементБлокировки.УстановитьЗначение("Ссылка", ПриказСсылка);
ЭлементБлокировки.Режим = РежимБлокировкиДанных.Разделяемый;
Блокировка.Заблокировать();

// 2. Прочитать первый объект - приказ
ПриказОбъект = ПриказСсылка.ПолучитьОбъект();
// 3. Прочитать второй объект – пользователя (автора приказа)
АвторПриказа = ПриказОбъект.Автор.ПолучитьОбъект();

 

См. также