Имя: Пароль:
1C
1С v8
8.2 Как распараллелить работу пользователей
0 The_SpecialOne
 
09.12.13
14:11
Добрый день! Столкнулся с вот такой задачей:

Есть непериодический, независимый регистр сведений "Номера" с 1 измерением "Порядковый номер". Режим управления блокировкой "Управляемый"
Есть 30 пользователей, которые одновременно раз в секунду жмут кнопку, по которой в этот регистр должна добавиться запись с порядковым номером на 1 больше максимального.

Если использовать след. вариант, то 29 пользователей, получив один и тот же номер, ждут 20 сек. установку блокировки, пока 1 записывает максимальный номер.

Функция НовыйНомер()

Запрос = Новый Запрос;
Запрос.Текст = "ВЫБРАТЬ ПЕРВЫЕ 1
               |    Номера.ПорядковыйНомер КАК ПорядковыйНомер
               |ИЗ
               |    РегистрСведений.Номера КАК Номера
               |
               |УПОРЯДОЧИТЬ ПО
               |    ПорядковыйНомер УБЫВ";
Выборка = Запрос.Выполнить().Выбрать();
ПорядковыйНомер = Выборка.ПорядковыйНомер + 1;

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

...

КонецФункции


Понятно, что условие очень утрировано, но такая ситуация периодически возникает. Хотелось бы узнать, есть ли возможности в этой ситуации обойтись без блокировок? уменьшить частоту их наложения? уменьшить длительность ожидания на блокировку (кроме установки в параметрах ИБ в конфигураторе)?
Вобщем хочется, чтобы когда 1 пользователь установил блокировку, то остальные, несумев установить такую же без ожидания 20 сек., сразу же пытались сделать запись с порядковым номером на 1 больше.
1 mikecool
 
09.12.13
14:14
"Вобщем хочется, чтобы когда 1 пользователь установил блокировку, то остальные, несумев установить такую же без ожидания 20 сек., сразу же пытались сделать запись с порядковым номером на 1 больше." не взлетит
2 Bober
 
09.12.13
14:17
(0) перейти на справочник с автонумерацией
3 Sammo
 
09.12.13
14:20
Изменить задачу.
Например 2
Например, присваивать номера потом отдельным заданием
4 The_SpecialOne
 
09.12.13
14:24
(2) На справочник перейти не вариант, уж больно много уже завязано на регистре. А вот прикрутить механизм нумерации в процедуру перед записью, впринципе, можно попробовать

(3) Задача бесспорно дурацкая, но увы ничего не поделать... Присваивать номера потом - тоже не вариант, пользователи используют те номера, которые генерят по нажатию своей кнопки
5 Eugene_life
 
09.12.13
14:38
(4) а если просто по-тупому завести константу "Последний номер" и с нее выдавать номер, сразу его меняя в +1 ?
6 The_SpecialOne
 
09.12.13
14:45
(5) Вариант с константой не прокатит. На нее тоже нужно будет накладывать блокировку.
Иначе для пользователей А и Б возможна ситуация:
А: прочитал константу
Б: прочитал константу
А: установил константу (+1)
Б: установил константу (+1)

Таким образом пользователи А и Б получили один и тот же порядковый номер, что критично в моей задаче.
7 mikecool
 
09.12.13
14:48
(6) польз А получил код, отказался - что будет?
8 Eugene_life
 
09.12.13
14:49
(6) Понятно. Ну, а если порядковый номер вычислять из данных самого регистра? Скажем, пусть по нажатию кнопки данные запишутся в регистр, а вернется пользователю номер. например, номер = порядковый номер строки в регистре.
9 The_SpecialOne
 
09.12.13
15:01
(7) Отказ не принципиален, если какой-то Порядковый номер будет пропущен - проблем не будет.
(8) Интересно, сейчас попробую
10 The_SpecialOne
 
09.12.13
15:30
(8) хм, а как получить номер строки в регистре?
"Выбрать Количество(Номера.ПорядковыйНомер) из Номера"? Тогда на время выполнения этого запроса нужно полностью блокировать регистр чтобы не было ситуации описанной в (6)
11 Мимохожий Однако
 
09.12.13
15:35
Какой ресурс у регистра?
12 mistеr
 
09.12.13
15:40
(0) Во-первых, непонятно, где завершение транзакции.

Во-вторых, код по-моему неверный. Между Запрос.Выполнить().Выбрать() и НачатьТранзакцию() кто-то может успеть вставить новый номер, и блокировка будет наложена не на максимальный номер.

В третьих, архитектура конечно так еще, почему с самого начало не использовались механизмы платформы, непонятно.

Переходите на нумератор или на крайний случай на константу. Ситуация (6) решается так:
1. Начинаем транзакцию
2. Блокируем
2. Получаем максимальный номер
3. Записываем +1
4. Завершаем транзакцию
5. Возвращаем новое значение
13 The_SpecialOne
 
09.12.13
15:48
(11) У регистра несколько ресурсов, которые должны идентифицироваться порядковым номером в измерении ресурса. (12) Код приведен неполностью и несовсем так, как он написан в реальной базе. Естественно завершение транзации есть.
В реальной базе сейчас устанавливается разделяемая блокировка и пользователи отбрасываются при попытке записать уже существующий номер.

Вариант с константой никак не облегчает задачу. Разницы между тем блокировать 1 запись регистра или блокировать константу нет. Все равно часть пользователей будет висеть в ожидании завершения транзакции 1 пользователем
14 The_SpecialOne
 
09.12.13
15:52
(12) Архитектуру формировали предшественники, я уже разгребаю за ними.
15 Enders
 
09.12.13
16:06
Ну если с блокировками проблему уже решили, а осталась с попыткой записать такой же номер. То делайте это в попытке исключении.
Что-то типо:

Процедура Записи()
Номер = 0;
ЗаписатьНомер(Номер);
КонецПроцедуры

Процедура ЗаписатьНомер(Номер)
Попытка
//Записываем
Исключение
Если ОшибкаЗаписиСуществующего Тогда
ЗаписатьНомер(Номер+1);
Иначе
Сообщить(ОписаниеОшибки());
КонецЕсли;
КонецПопытки;
КонецПроцедуры

Криво конечно. Как анализировать что это именно ошибка записи существующего не думал ;)
16 The_SpecialOne
 
09.12.13
16:31
(15) Нет, с блокировками проблема не решается установкой разделяемого режима блокировки. Просто в этом случае блокировка без проблем устанавливаются, а пользователи отваливаются уже при попытке записи недопустимого номера.

Основная проблема в том, что до того как пользователь отваливается, он ждет 20 сек. - стандартное время ожидания на блокировку.


Пока вижу только одно решение - переход на справочник с автонумерацией.
17 The_SpecialOne
 
09.12.13
17:03
(15) блин, прикольная идея. Можно же ориентироваться на то что у меня 1 ключевое измерение и отлавливать ошибки записи набора. При этом нет никакого ожидания. Блокировки вообще не нужны. Сделал так:

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


Запустил 10 сеансов, каждую секунду каждый сеанс делает новую запись. Периодически получаю сообщения:
отвалился! Номер: 137669 время: 09.12.2013 14:51:32
отвалился! Номер: 137670 время: 09.12.2013 14:51:32
отвалился! Номер: 137671 время: 09.12.2013 14:51:32

Но никакой задержки на установку блокировки, рано или поздно каждый получает свой номер.
18 mistеr
 
09.12.13
22:57
(13) Ты не понял похоже. Смысл в том, чтобы не держать блокировки долго, тогда не будет ожидания. Сейчас у тебя (если я правильно понял) блокировка держится до момента записи в регистр. А нужно выдать номер и сразу отпустить.

Константа нужна, чтобы хранить максимальный номер из *выданных* (но еще не записанных)
19 The_SpecialOne
 
09.12.13
23:34
(18) ага, теперь понял. Теперь вопрос, что лучше: обработка Попытка/Исключение или блокировка на константу?
20 EvgeniuXP
 
09.12.13
23:44
(19) смотря какую версию платформы используешь, если до 8.2.14 - то константы хуже, если от - то лучше.
21 The_SpecialOne
 
10.12.13
00:12
(20) как раз таки 8.2.14, значит оставлю попытку исключение. Спасибо
22 mistеr
 
10.12.13
00:39
(19) Какая Попытка/Исключение? Если (17), то там жуть. Во-первых, ты не проверяешь ошибку, хотя (15) тебе показал. Это пахнет бесконечной рекурсией.
Во-вторых, кто будет удалять запись, если пользователь раздумает ее сохранять? И где гарантия, что не будет удалена чужая.