Есть клиент-серверная база 1с 8.3 с управляемым режимом блокировок как для конфигурации так и объектов.
Как добиться пропуск чтения заблокированных данных через запрос "ВЫБРАТЬ ..." не дожидаясь разблокировки?
Для базы mssql определен уровень изоляции Read Commited.
Конкретно, имеем непериодический независимый регистр сведений Заказы с измерением Заказ (тип ссылка, не индексирован) и ресурсом Пользователь (тип ссылка, индексирован):
В регистре куча заказов которые необходимо принимать пользователям. Это определяется по пустому значению Пользователь. Код:
Пользователь = ПараметрыСеанса.ТекущийПользователь;
НачатьТранзакцию(РежимУправленияБлокировкойДанных.Управляемый);
Попытка
// чтение
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ ПЕРВЫЕ 1
| Заказы.Заказ КАК Заказ
|ИЗ
| РегистрСведений.Заказы КАК Заказы
|ГДЕ
| Заказы.Пользователь = ЗНАЧЕНИЕ(Справочник.Пользователи.ПустаяСсылка)";
РезультатЗапроса = Запрос.Выполнить();
Если РезультатЗапроса.Пустой() Тогда
ОтменитьТранзакцию();
Возврат Ложь; // нет свободных заказов
КонецЕсли;
ВыборкаЗапроса = РезультатЗапроса.Выбрать();
ВыборкаЗапроса.Следующий(); // заказ получен
// блокировка
Блокировка = Новый БлокировкаДанных;
ЭлементБлокировки = Блокировка.Добавить("РегистрСведений.Заказы");
ЭлементБлокировки.Режим = РежимБлокировкиДанных.Разделяемый;
ЭлементБлокировки.УстановитьЗначение("Заказ", ВыборкаЗапроса.Заказ);
Блокировка.Заблокировать();
// запись
Заказы = РегистрыСведений.Заказы.СоздатьНаборЗаписей();
Заказы.Отбор.Заказ.Установить(ВыборкаЗапроса.Заказ);
НоваяЗапись = Заказы.Добавить();
НоваяЗапись.Заказ = ВыборкаЗапроса.Заказ;
НоваяЗапись.Пользователь = Пользователь;
Заказы.Записать();
ЗафиксироватьТранзакцию();
Исключение
ОтменитьТранзакцию();
ВызватьИсключение;
Возврат Ложь; // заказ не принят
КонецПопытки;
Возврат Истина; // заказ принят
Показать
Представляем что в регистре есть N свободных заказов.
В первом сеансе получили Заказ №1, заблокировали, и в отладчике поставили паузу на строке ЗафиксироватьТранзакцию(), то есть в регистре есть не зафиксиролванные изменения.
Во втором сеансе получаем ожидание на строке Блокировка.Заблокировать(), которое длится до момента разблокировки его первым сеансом (фиксация или отмена).
Возвращаясь к вопросу по запросу ВЫБРАТЬ во втором сеансе, как пропустить заблокированный Заказ №1 и взять на обработку Заказ №2 при выполнении строки Запрос.Выполнить()?
И верно ли мое понимание что это проблема грязного чтения? Так как во втором сеансе запрос использует отбор по пустому пользователю, который изменится первым сеансом или не изменится.
И какому виду использования блокировок будет относиться реализация данной задачи, транзакционным или объектным?
Вообще на самом деле нет необходимости получать заказы последовательно. Заказ №1 может забрать третий сеанс после момента отмена транзакции первым сеансом.
Цель максимально быстро получать и принимать заказы типовыми средствами платформы.
(1) по идее попытка заблокировать уже заблокированный объект приведет к исключению
может выбирать не первые 1, а несколько и циклом перебирать через попытку заблокировать
- успешно - никто еще не схватил и можно работать
- вывались в исключение - значит уже заблокировали в другой сессии, идем к следующему в запросе
(4) ну так-то да, с блокировками регистров все сложнее, чем с теми же справочниками
есть вариант "через Ж" - сделать свой регистр сведений и писать туда, что заблокировали вот этот заказ, после снятия блокировки запись удалять
и проверять, а заблокирован ли уже текущей заказ или еще нет
Блокировка была установлена для запрета получения этого заказа другими сеансами, без ожидания разблокировки.
Тут не получение данных как остаток или цена для дальнейших расчетов.
есть вариант "через Ж" - сделать свой регистр сведений и писать туда, что заблокировали вот этот заказ, после снятия блокировки запись удалять
Сеанс 1 получил заказ 1, добавил в свой регистр (ЗаказыДоп) новую запись и зафиксировал об этом.
Второй сеанс пытается этот же полученный заказ 1 добавить в регистр ЗаказыДоп, на которое ему будет отказ, из-за существующей записи.
Далее он (второй сеанс) берет заказ 2, который в этот момент принят третьем сеансом. Представьте высоконагруженную систему, где не только много пользователей, но и объем заказов. Длительное ожидание сеансов обеспечена. Не учитывая дополнительные затраты на двойную чтение/запись для второго регистра.
А если:
и проверять, а заблокирован ли уже текущей заказ или еще нет
(6) представляю, потому и вариант "через Ж..." ))
еще, как вариант
определить пользователя приоритеты, Вася 1, Петя 2, Клава 3 и т.д. и перебирать заказы согласно приоритетам
если пользователь Вася, то берет первый заказ, если Петя, то берет второй, если Клава, то берет третий и т.д. с сортировкой по чему там можно, чтобы всегда одинаково выходило в списке заказов
ну и еще смотреть, работает ли Вася сейчас, чтобы можно было при случае сменить приоритетность для Пети и всем остальным соответственно
или по аналоги с приоритетами. получать заказы и Васе - все с 1 по 5, Пете с 6 по 10 и т.д
все это будет подразумевать одно обработку для всех активных пользователей и алгоритм немного замороченный будет, но как варианты на уровне мозгового штурма имею право быть
(1)Не понимаю смысла обсуждения, и возникающей проблемы, если честно.
Регистр блокируется на время записи одной записи.
Неужели это настолько заметно в работе?
Когда запись в первом сеансе выполнится, во втором сеансе запрос вернет уже следующий свободный заказ. Так в чем проблема то выражается?
Этого пока не произошло, так как разработка в процессе определения ее архитектуры.
Но при моделировании данной ситуации проблема возникает.
Когда запись в первом сеансе выполнится, во втором сеансе запрос вернет уже следующий свободный заказ.
На самом деле, дождавшись фиксации транзакции сеанса 1, перезапишет данные своими, то есть - Заказ №1, Пользователь №2
Так в чем проблема то выражается?
Проблемы наверно и не было бы если уровень понимания как работают транзакционные блокировки и как ими управлять были бы более ясны. О чем и уточняю у знающих
С точки зрения 1С, то архитектура контроля остатков описывается в модуле набора записей регистра, где проверяется, достаточно ли остатка, и если недостаточно, то система генерит соответствующее исключение. Это ровно то, как предлагает решать вопрос 1С.
В 1C + MSSQL (без перевода в версионник) возможно реализовать аналог хинта Oracle SKIP LOCKED?
Пока придерживаюсь использование данных менеджера блокировок 1С для исключения заблокированных заказов в запросе условием не в списке элементов объекта Блокировка, наподобие ВЫБРАТЬ ... ГДЕ Заказ НЕ В (&Заблокированные) ? Так как есть такое, что в ситуации при открытой транзакции запрос не использует NOLOCK.