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