Разберем ситуацию, с которой сталкиваются многие разработчики: при попытке модификации данных в регистре сведений непосредственно в момент проведения документа возникают коллизии, данные перезаписываются или теряются, а установленные БлокировкаДанных кажутся неэффективными. Выясним, почему это происходит и как выстроить надежный механизм работы с последовательностями документов.
Основная причина проблем заключается в непонимании того, как платформа 1С управляет транзакциями. Важно помнить следующее:
НачатьТранзакцию() внутри подписки на событие ОбработкаПроведения, вы не создаете новую изолированную область.ЗафиксироватьТранзакцию() внутри подписки не имеют силы, так как фиксация всей цепочки произойдет только тогда, когда проведение документа завершится успешно на самом верхнем уровне.Попытки «управлять» транзакцией изнутри подписки лишь вносят хаос в логику работы сервера 1С и СУБД, что приводит к непредсказуемому поведению при многопользовательском режиме работы.
Проанализируем механику работы блокировок. БлокировкаДанных — это механизм, работающий на уровне сервера 1С. Он «сообщает» другим сеансам, что ресурс занят. Однако:
БлокировкаДанных, он может попытаться записать данные, что приведет к конфликту уже на уровне СУБД.ЗаблокироватьДанныеДляРедактирования не имеет отношения к транзакционным блокировкам, так как это объектный механизм, предназначенный для блокировки формы или элемента справочника, а не для синхронизации записей в регистре.Чтобы избежать перетирания данных, перестроим логику обращения к регистру сведений. Рассмотрим правильный подход:
НачатьТранзакцию() в подписке. Позвольте платформе самой управлять транзакцией проведения.РегистрСведенийНаборЗаписей для чтения и изменения данных. Это позволит вам считать текущее состояние, модифицировать его в памяти и записать одним пакетом.Пример корректного кода для установки блокировки и работы с данными:
// Устанавливаем блокировку ДО чтения данных
Блокировка = Новый БлокировкаДанных;
ЭлементБлокировки = Блокировка.Добавить("РегистрСведений.ИмяРегистра");
ЭлементБлокировки.УстановитьЗначение("ИзмерениеКлюч", ЗначениеКлюча);
ЭлементБлокировки.Режим = РежимБлокировкиДанных.Исключительный;
Блокировка.Заблокировать();
// Теперь считываем данные, зная, что никто другой их не изменит
НаборЗаписей = РегистрыСведений.ИмяРегистра.СоздатьНаборЗаписей();
НаборЗаписей.Отбор.ИзмерениеКлюч.Установить(ЗначениеКлюча);
НаборЗаписей.Прочитать();
// Модифицируем данные внутри набора
Для Каждого Запись Из НаборЗаписей Цикл
// Логика пересчета цепочки
Запись.ПолеСчетчик = ...;
КонецЦикла;
// Записываем набор, транзакция проведения сама зафиксирует результат
НаборЗаписей.Записать();
Записи «теряются» из-за того, что при уровне изоляции Read Committed другие сеансы не видят ваших незафиксированных изменений. Если два документа проводятся одновременно, каждый из них читает состояние регистра «как есть», не видя изменений коллеги.
Решение:
Если задача подразумевает выстраивание жесткой последовательности (например, партионный учет или сквозная нумерация), рассмотрите следующие архитектурные решения:
Подводя итог: залогом стабильной работы является использование управляемых блокировок до чтения данных и отказ от манипуляций с транзакциями внутри событий проведения. Работайте с НаборЗаписей и всегда придерживайтесь правила: сначала блокируем ресурс, затем считываем, анализируем и записываем изменения.