Форум 1С
Программистам, бухгалтерам, администраторам, пользователям
Задай вопрос - получи решение проблемы
28 мар 2024, 18:47

Блокировка при записи в регистр сведений.

Автор Z.spb, 03 окт 2016, 13:00

0 Пользователей и 1 гость просматривают эту тему.

Z.spb

Просьба сильно не пинать. Не так давно занимаюсь 1С, во всех нюансах ещё не разобрался :dfbbdrfb:.
УПП 8.2 Обычное приложение. Режим блокировок в свойствах конфигурации - управляемый.

Исходные данные: В общем модуле сделана подписка перед записью некоторых справочников. Создан регистр сведений (непериодический, независимый),
измерения:
Объект (тип УникальныйИдентификатор); Номер (тип Число).
ресурс: ДопСведения (которые нужно сохранить)
реквизит: ДатаЗаписи.
Перед записью объекта делаю запрос:
| ВЫБРАТЬ ЕСТЬNULL(МАКСИМУМ(Номер), 0) КАК Номер
| ИЗ РегистрСведений.<ИмяРегистра>
| ГДЕ Объект = &УИД"; //(уникальный идентификатор)
Выборка = Запрос.Выполнить().Выбрать(); Выборка.Следующий();
x = РегистрыСведений.<ИмяРегистра>.СоздатьМенеджерЗаписи();
x.Объект = УИД;
x.Номер = Выборка.Номер + 1; // - это обеспечивает уникальность записи
x.ДатаЗаписи = ТекущаяДата();
x.ДопСведения = <тут идут нужные мне данные>;
x.Записать();


Далее. Раз в полчаса мне нужно оперативно сливать эти доп. данные в отдельную базу. Сделал фоновое задание.
Чтобы брать только записи, транзакция по которым точно завершилась, написал запрос к SQL серверу, который возвращает мне дату самой давней активной транзакции. Если активных транзакций в данный момент нет, то возвращается текущаядата(). Записи из регистра читаю только те, которые младше по дате
ВЫБРАТЬ * ИЗ РегистрСведений.<ИмяРегистра> ГДЕ ДатаЗаписи < &ДатаНачалаПоследнейАктивнойТранзакции
Выборка = Запрос.Выполнить().Выбрать();
Пока Выборка.Следующий()....
<идёт обработка выборки, передача по внешнему соединению в другую базу там создаётся по каждой записи отдельный документ>
//если всё прошло без ошибок то
Набор = РегистрыСведений.<ИмяРегистра>.СоздатьНаборЗаписей();
Набор.Отбор.Объект.Установить(Выборка.Объект);
Набор.Отбор.Номер.Установить(Выборка.Номер);
//не прочитав записываю, т.е. стираю обработанную строчку из регистра
Набор.Записать();


Как то так. Неделю эта хрень работала в нагруженной базе без ошибок, фоновое задание отрабатывало как надо. Недавно была массовая операция записи объектов (периодически у нас бывает получение данных из других программ и т.д), всё выполнялось в одной длительной транзакции, которая длилась более часа. Фоновое задание стартовало 2 раза в этот период и оба раза свалилось с ошибкой:
Ошибка при вызове метода контекста (Записать): Конфликт блокировок при выполнении транзакции:
Превышено максимальное время ожидания предоставления блокировки


Если кто-то может объяснить, откуда взялась блокировка, буду признателен. Я же считывают только те записи запросом, которые были записаны ДО момента начала транзакции. А значит они должны быть не заблокированы? :dfbbdrfb:
(ps. как только длительная транзакция завершилась следующее фоновое задание отработало без ошибок)




   


ilyay

Во-первых, если транзакция завершилась, то данные есть, а если не завершилась, их нет. Промежуточного состояния не может быть.

Не проверяйте время окончания транзакции (это технологически неверно). Вы видите, что транзакция закончилась, лезете в базу, а в этот момент там параллельно началась другая транзакция.

Если режим блокировок управляемый, тогда устанавливайте их явно: БлокировкаДанных = Новый БлокировкаДанных и т.д.
Скорее всего, взаимная блокировка возникает, потому что вы не блокируете данные (в режиме исключительно), которые собираетесь изменять, до чтения их запросом. Надо Заблокировать-Прочитать-Записать в транзакции.
Добавлено: 03 окт 2016, 15:15


Блокировка может накладываться на более широкие диапазоны данных, вплоть до таблицы целиком. Настройте технологический журнал для отображения события TLOCK, например, чтобы увидеть как накладываются блокировки в вашем случае.

Z.spb

Спасибо за ответ. Но вот про это я не понял
Цитата: ilyay от 03 окт 2016, 15:10
Во-первых, если транзакция завершилась, то данные есть, а если не завершилась, их нет. Промежуточного состояния не может быть.
Как не может? Вот я беру тестовую базу, открываю в двух экземплярах. В одном окне запускаю любую длительную транзакцию, ну просто для примера
НачатьТранзакцию();
Ном = Справочники.Номенклатура.Выбрать();
сч=0;
Пока Ном.Следующий() И сч<10000 Цикл
сч = сч + 1;
Об=Ном.ПолучитьОбъект();
        Об.Наименование = Об.Наименование + "1234";
Об.Записать();
КонецЦикла;
ОтменитьТранзакцию();

Процесс пошёл. Система начинает массово перезаписывать номенклатуры. Каждый раз срабатывает моя подписка "Перед записью", она получает нужные мне доп.данные, пишет их в регистр. Если в это время во втором окне открыть консоль запросов и написать мой запрос из фонового задания (ВЫБРАТЬ * ИЗ РегистрСведений.<ИмяРегистра>) то данные вполне себе есть. Я могу их спокойно считывать, обрабатывать, и пересылать "кривые" наименования с "1234" в другую базу. И это не смотря на то, что транзакция ещё не завершилась. Когда же транзакция отменится все записи из регистра исчезнут, как будто их там и не было. Но всё то время, что шла транзакция, они там были и были доступны для чтения... И если фоновое задание в этот период сработает то оно их прочитает и перешлёт неверные наименования. Разве нет? 

Цитата: ilyay от 03 окт 2016, 15:10Не проверяйте время окончания транзакции (это технологически неверно). Вы видите, что транзакция закончилась, лезете в базу, а в этот момент там параллельно началась другая транзакция.
Я получаю время не окончания, а начала самой ранней из активных транзакций. Именно из-за того что я описал выше я и решил отделить те записи, по которым транзакция, возможно, ещё не завершена (и они могут впоследствии "исчезнуть" из регистра, если транзакция к которой они относятся будет отменена) и те, которые уже ТОЧНО зафиксированы.

Цитата: ilyay от 03 окт 2016, 15:10Если режим блокировок управляемый, тогда устанавливайте их явно: БлокировкаДанных = Новый БлокировкаДанных и т.д.
Я читал про эту возможность, но, честно говоря, никогда не ставил блокировки данных вручную. У нас есть несколько "самописных" регистров, куда пишутся данные, нигде ничего подобного не написано насчёт блокировок. Всё работает. Попробую, если ничего другого не получится.

Цитата: ilyay от 03 окт 2016, 15:10Скорее всего, взаимная блокировка возникает, потому что вы не блокируете данные (в режиме исключительно), которые собираетесь изменять, до чтения их запросом. Надо Заблокировать-Прочитать-Записать в транзакции.
Спасибо, попробую. А если я заблокирую все записи в регистре, а в этот момент сработает очередное "ПередЗаписью" и запрос   | ВЫБРАТЬ ЕСТЬNULL(МАКСИМУМ(Номер), 0) КАК Номер
    | ИЗ РегистрСведений.<ИмяРегистра>" попытается прочитать этот регистр, то что будет? Не будет ли в этом случае ошибка, из-за того что регистр не доступен для чтения?

Добавлено: 04 окт 2016, 16:15


Ну вот. Я разобрался где ошибка, всё оказалось проще, чем я думал. Ошибся я в запросе к SQL серверу. Он возвращает дату начала транзакции иногда как дату, а иногда как строку!, в формате месяц-день-год-время-PM\AM. Почему так, я пока не понял. Когда тестировал на отладке всегда возвращалась дата-время. Отсюда урок - нужно в подобных запросах не забывать писать CONVERT(datetime,<Дата>,13). А в 1С проверять ТипЗнч, которое я получил. Потому что 1С запросу с условием "ГДЕ ДатаВерсии < &ДатаВерсии" всё равно что я ему подсунул дату или любую абракадабру в виде строки. Он всё равно выполняется без ошибок, только ничего не фильтурет и выдаёт все записи. Поэтому и блокировка, что я пытался стереть записи, по которым ещё шла транзакция.

ilyay

ЦитироватьЕсли в это время во втором окне открыть консоль запросов и написать мой запрос из фонового задания (ВЫБРАТЬ * ИЗ РегистрСведений.<ИмяРегистра>) то данные вполне себе есть.
Это "грязные данные", т.е. данные не завершенной транзакции. Это особенность файловой версии. На сервере может быть по-другому, в зависимости от настроек. Открывайте транзакцию, а потом читайте.

Сейчас вы читаете без транзакции. Блокировки учитываются только при работе транзакции. "Грязные данные" появляются, когда СУБД работает в режиме версионирования, а запрос на чтение вне транзакции. Когду СУБД работает как блокировочная (например, на 8.2 или 8.3 с автоматическими блокировками), там не будет такого эффекта.
Добавлено: 05 окт 2016, 11:48


ЦитироватьНе будет ли в этом случае ошибка, из-за того что регистр не доступен для чтения?
Будет ожидание на блокировках, т.е. система будет ждать, когда блокировки будут сняты и она сможет прочитать данные. Если ожидание будет слишком долгим, произойдет отмена транзакции из-за таймаута.
Добавлено: 05 окт 2016, 11:50


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

Теги:

Похожие темы (5)

Рейтинг@Mail.ru

Поиск