Имя: Пароль:
1C
1С v8
1С взаимоблокировки при записи в регистр.
0 ejikbeznojek
 
14.10.14
14:14
Добрый день.
Имеется беда с которой борюсь уже 2ю неделю 8(((

Есть самописная конфигурация 8.1 . База скулёвая.
Есть Документ формирующий записи в регистр сведений.
И есть 20 компьютеров с которых эти документы очень быстро создают.
Могут раз в 20 секунд документ создавать с каждого компа.
В документ сканируется товар в количестве 1-3000 коробок. И на каждую коробку создаётся запись в регистре сведений.(т.е. при проведении документа может создаваться 1-3000 записей в регистре)

Соответственно у меня появилась беда со взаимоблокировками.

Я пытался её решить несколькими путями:
1. Создал константу с типом булева. И перед записью в регистр сначала проверял, есть ли галка.
Если есть то ждал 60 секунд или пока не снимется галка.
Потом галку ставил, делал записи в регистр, галку снимал.

2. Где-то вычитал что блокируются все константы, и вместо констант сделал новый регистр из одной записи. И аналогично работал с ним.

В итоге блокируется теперь новый регистр, я уже и различные попытки ставил и паузы. Теперь пишет в данной транзакции уже происходили ошибки.
Код выглядит так:
///////////////////////////////чтение из регистра
Запрос = Новый Запрос;
Запрос.Текст = "ВЫБРАТЬ ПЕРВЫЕ 1 ИнформацияШКЛН.НомерКоробки КАК НомерКоробки
        |ИЗ
        |    РегистрСведений.ИнформацияШКЛН КАК ИнформацияШКЛН
        |ГДЕ
        |    ИнформацияШКЛН.ВидНакладной = &ВидНакладной
        |    И ИнформацияШКЛН.НомерНакладной = &НомерНакладной
        |
        |УПОРЯДОЧИТЬ ПО
        |    НомерКоробки УБЫВ
        |АВТОУПОРЯДОЧИВАНИЕ";        Запрос.УстановитьПараметр("ВидНакладной",ДокОснование.Метаданные().Синоним);        Запрос.УстановитьПараметр("НомерНакладной",ДокОснование.номер);


Конст=ПолучитьКонстантуБлокировкиИнформацииШКЛН();
если Конст тогда
Пока истина Цикл
Пауза();                                Конст=ПолучитьКонстантуБлокировкиИнформацииШКЛН();
если Конст=ложь тогда
    Прервать;
КонецЕсли;
                
Если ТекущаяДата()-тВремя > 60 Тогда
Пауза();                    УстановитьКонстантуБлокировкиИнформацииШКЛН(ложь);
Пауза();
КонецЕсли;
КонецЦикла;
КонецЕсли;
        УстановитьКонстантуБлокировкиИнформацииШКЛН(Истина);
    Выборка = Запрос.Выполнить().Выбрать();
УстановитьКонстантуБлокировкиИнформацииШКЛН(ложь);
///////////////////////////////чтение из регистра

///////////////////////////////запись в регистр    
Конст=ПолучитьКонстантуБлокировкиИнформацииШКЛН();
если Конст тогда
Пока истина Цикл
Пауза();                                Конст=ПолучитьКонстантуБлокировкиИнформацииШКЛН();
если Конст=ложь тогда
    Прервать;
КонецЕсли;
                
Если ТекущаяДата()-тВремя > 60 Тогда
Пауза();                    УстановитьКонстантуБлокировкиИнформацииШКЛН(ложь);
Пауза();
КонецЕсли;
КонецЦикла;
КонецЕсли;
            УстановитьКонстантуБлокировкиИнформацииШКЛН(Истина);                
Для Сч = НаборЗаписей.Количество() + 1 По КоличествоКоробок Цикл
Запись = НаборЗаписей.Добавить();                    
Запись.ШК = ТекШККоробкиЛистаНабора;                    
Запись.ШКЛистаНабора = ШКЛистаНабора;
Запись.НомерЛН = Номер;
Запись.ВидНакладной = ДокОснование.Метаданные().Синоним;
Запись.НомерНакладной = ДокОснование.Номер;
Запись.Количество = товары.Итог("Количество");
Запись.ГруппыТоваров = ГруппыТоваров;
Запись.Подразделение = ПараметрыСеанса.ЭтотУзел.Подразделение;
Запись.КоличествоКоробок = КоличествоКоробок;
запись.НомерКоробкиЛН = Сч;
ТекНомерКоробки = ТекНомерКоробки + 1;
Запись.НомерКоробки = ТекНомерКоробки;
КонецЦикла;                
НаборЗаписей.записать();                УстановитьКонстантуБлокировкиИнформацииШКЛН(ложь);
///////////////////////////////запись в регистр    






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


Процедура УстановитьКонстантуБлокировкиИнформацииШКЛН(значение) экспорт
    если значение=Истина или значение=ложь тогда
        сч=0;
        пока Истина Цикл
            Попытка                  ЗаписьВРег=РегистрыСведений.Константы.СоздатьМенеджерЗаписи();                ЗаписьВРег.ИмяКонстанты="БлокироватьРегистрШКЛН";
ЗаписьВРег.Период=ТекущаяДата();                ЗаписьВРег.ЗначениеРеквизита=значение;            
ЗаписьВРег.Записать();
Прервать;
            Исключение
                сч=сч+1;
                если сч>10 тогда Возврат;КонецЕсли;
            КонецПопытки;
            Пауза(2000);
        КонецЦикла;
    КонецЕсли;
КонецПроцедуры
1 Alex_MA
 
14.10.14
14:30
Совет:

Запусти консоль кластера 1С.
Пытайся добиться взаимоблокировки. Отслеживай блокировки по консоли - увидишь кто кого блокирует => узнаешь какой сеанс что запустил.

Дальше анализируешь выполнение операций этими двумя сеансами. Если не получается проанализировать запускай профайлер mssql. Там событие Deadlock graph - сохрани в xml и разбирай. В этом xml будет описание: запрос_1 и запрос_2, а так же блокирующиеся ресурсы. Попытайся эту информацию сопоставить с кодом 1С и исправить ошибку.
Удачи
2 Alex_MA
 
14.10.14
14:31
и сразу же настораживает:

ПолучитьКонстантуБлокировкиИнформацииШКЛН
УстановитьКонстантуБлокировкиИнформацииШКЛН
3 ejikbeznojek
 
14.10.14
14:32
Ну это функция и процедура, я их код чуть ниже написал
4 ejikbeznojek
 
14.10.14
14:33
Собственно на 99% я уверен что они и вызывают блокировку, но не пойму как этого избежать
5 ejikbeznojek
 
14.10.14
14:36
УстановитьКонстантуБлокировкиИнформацииШКЛН(Истина);
    Выборка = Запрос.Выполнить().Выбрать();
УстановитьКонстантуБлокировкиИнформацииШКЛН(ложь);


при выполнении "Выборка = Запрос.Выполнить().Выбрать();"
ругается иногда, в данной транзакции уже происходили ошибки.
6 rsv
 
14.10.14
14:36
(0) Переходите на 8.2  т.к. можно перейти в управляемый режим (понизить  до  read comm ) и перейти в режим разделения итогов.
7 ejikbeznojek
 
14.10.14
14:38
Я бы и рад))
Но мне сказали, что переход в ближайшие 2 года неосуществим))
8 Alex_MA
 
14.10.14
14:39
(4) суть примерно скорее такова:
1сеанс выполняет блокировку Рес_1
2сеанс выполняет блокировку Рес_2
далее
1сеанс выполняет блокировку Рес_2
2сеанс выполняет блокировку Рес_1

захват ресурсов в разном порядке из разных транзакций - подумай над этим
9 ejikbeznojek
 
14.10.14
14:53
не совсем пойму.
Регистр то у меня тут 1, и в нём всего одна запись.
Я задумывал, что если взаимоблокировка происходит, то отрабатывает исключение, и так 10 попыток. Если за 10 раз все попытки не успешные, то всё равно возвращается истина.
10 КартузПива
 
14.10.14
15:20
(7) Погоди. Одно дело УФ, второе - смена платформы. Работать сейчас на 8.1 в толстом клиенте - верх глупости. Все уже давно перешли на 8.2.
11 ejikbeznojek
 
14.10.14
15:26
Возможно))
Но у меня руководство убедить что нужен переход не получилось.
12 Зеленый пень
 
14.10.14
15:34
(10) А вот и не все.
(0) Что за цирк с константой?
Ответь сначала - откуда взаимоблокировки, регистр сведений подчинен регистратору или нет?
Если нет - накладывай управляемые блокировки.
13 Necessitudo
 
14.10.14
15:36
Как вариант - прямые запросы.
14 ejikbeznojek
 
14.10.14
15:40
Ну я создал булеву константу. Перед записью проверяю истина ли она. Если ложь, то ставлю в константу истину и пишу в свой регистр и обратно ставлю в консту ложь.
Если истина, то в цикле жду пока не станет ложь.
Ну т.е. просто индикатор можно писать в регистр или нет.

Я потом всё переделал аналогично, но вместо константы регистр сведений из 1й записи.
15 ejikbeznojek
 
14.10.14
15:42
Под прямыми запросами имеется ввиду
"Select нужное поле From..."?
А это точно избавит от взаимоблокировок?
16 Ник второй
 
14.10.14
15:44
(6) Это и в 8.1 можно
17 Necessitudo
 
14.10.14
15:45
(15) Конечно - особенно если сам наложишь тот уровень блокировки который захочешь.
18 ejikbeznojek
 
14.10.14
15:45
У меня режим управления блокировками в конфигураторе стоит
"Автоматический". Насколько  понял нужно будет поставить "Автоматический и управляемый" это может негативно сказаться на старом коде?
19 Ник второй
 
14.10.14
15:46
1. Определить ресурс который блокируется, это можно быть вполне и не регистр и не константа. Определить можно например через ТЖ
2. Устранить проблему.
20 Ник второй
 
14.10.14
15:46
(17) Вообще дурь.
21 Ник второй
 
14.10.14
15:46
(18) нет
22 Ник второй
 
14.10.14
15:47
(13) А какой смысл, если платформа позволяет штатно устанавливать различные уровни изоляции?
23 Necessitudo
 
14.10.14
15:48
(20) В смысле дурь? Ты отменил BEGIN { TRAN | TRANSACTION } ?
24 Ник второй
 
14.10.14
15:49
(23) Дурь в прямом смысле, так как "Платформа позволяет штатно устанавливать различные уровни изоляции"
25 Necessitudo
 
14.10.14
15:50
(24) Платформа использует далеко не все уровни транзакции, которые предоставляет SQL сервер.
26 Ник второй
 
14.10.14
15:51
(25) ну и какие в данном случае помогут, которые не использует платформа?
27 Necessitudo
 
14.10.14
15:53
(26) Snapshot
28 Ник второй
 
14.10.14
15:54
(27) И зачем тут он, если у нас только запись? 0_о
29 Necessitudo
 
14.10.14
15:55
(28) Всего лишь запись, какие мелочи)))
30 Ник второй
 
14.10.14
15:55
(28) + и между прочем Snapshot 1С-ка штатно использует, но реально тут он ни к чему.
31 Necessitudo
 
14.10.14
15:55
(30) Не ври, не использует.
32 Ник второй
 
14.10.14
15:56
(31) Использует.... естественно не 8.1
33 Necessitudo
 
14.10.14
15:57
(32) Я сказал Snapshot, а не Read Commited Snapshot.
34 Ник второй
 
14.10.14
16:00
(33) Вот как раз Snapshot без Read Commited Snapshot может иметь больше проблем, так как позволяет устанавливать общую блокировку ресурса.

Но если вернуться к задаче, то автору нужно грамотно изменить архитектуру регистров и возможно перевести на упр блокировки.
35 H A D G E H O G s
 
14.10.14
16:01
А можно спросить - нафига вообще этот огород?
36 Necessitudo
 
14.10.14
16:01
(35) Письками помериться)
37 Ник второй
 
14.10.14
16:02
(35) +100500
38 H A D G E H O G s
 
14.10.14
16:03
Чтобы пользователь сам каждый раз не жал повторно кнопку, а тупо смотрел в застывшую 1С или пошел пить кофе?
39 Ник второй
 
14.10.14
16:28
(38) 20 секунд это долго, и то можно поменять.

А лучше всего изменить архитектуру, что бы блокировок в принципе не было.
40 MrStomak
 
14.10.14
16:43
(29) Тебе объясняют, что в операциях записи в один ресурс две транзакции всегда несовместимы друг с другом, какой уровень изоляции ни поставь. В MSSQL проблема потерянного обновления решается даже в хинте WITH NOLOCK.
41 MM
 
14.10.14
17:08
(0) А почему в запросе на чтение нет ДЛЯ ИЗМЕНЕНИЯ ?
Для автоматического режима блокировок это обязательно.
42 Fragster
 
гуру
14.10.14
17:12
в (0) имеено ВЗАИМОблокировки или таймауты?
43 Fragster
 
гуру
14.10.14
17:12
*именно
44 ejikbeznojek
 
14.10.14
17:39
Взаимоблокировки
{Документ.ЛистНабора(101)}: Ошибка при вызове метода контекста (Выполнить): Ошибка выполнения запроса "Конфликт блокировок при выполнении транзакции:
Microsoft OLE DB Provider for SQL Server: Транзакция (идентификатор процесса 63) вызвала взаимоблокировку ресурсов блокировка с другим процессом и стала жертвой взаимоблокировки. Запустите транзакцию повторно.
HRESULT=80004005, SQLSrvr: Error state=12, Severity=D, native=1205, line=1
"
Выборка = Запрос.Выполнить().Выбрать();
45 MM
 
14.10.14
17:45
(44) Конструкция ДЛЯ ИЗМЕНЕНИЯ в языке запросов, как раз и служит для борьбы с взаимоблокировками в автоматическом режиме.
46 Fragster
 
гуру
14.10.14
19:26
зачем там вообще чтение из РС?
47 hhhh
 
14.10.14
19:37
(44) вот это полный бред:

ВЫБРАТЬ
    |    Константы.ЗначениеРеквизита как знач
    |ИЗ
    |    РегистрСведений.Константы КАК Константы
    |ГДЕ
    |    Константы.ИмяКонстанты = &ИмяКонстанты";


как можно было регистр назвать "Константы"? Вы - самоубийца.
48 hhhh
 
14.10.14
19:42
(47) и потом

Функция ПолучитьКонстантуБлокировкиИнформацииШКЛН()

возвращает

Возврат результат.Получить(0).знач;

например Результат имеет 99 строчек, вам надо взять последнюю, 99-ю, а вы берете самую первую строку: результат.Получить(0)   ???????