При работе в распределенных информационных базах (РИБ) разработчики часто сталкиваются с ситуацией, когда обмен данными прерывается критической ошибкой: "Запись с такими ключевыми полями уже существует". Особенно странно это выглядит, когда в базе-источнике операции выполнялись последовательно и логично — например, сначала был распроведен один документ, а затем проведен другой, использующий те же измерения в регистре сведений. В рамках данной статьи мы детально разберем внутренние механизмы платформы «1С:Предприятие», выясним причину нарушения хронологии при обмене и найдем способы надежного решения этой проблемы.
Для начала проанализируем ситуацию, описанную в обсуждении. У нас имеется непериодический регистр сведений, подчиненный регистратору. Логика разработчика проста: если запись подчинена документу, значит, документы не должны конфликтовать друг с другом. Однако на практике платформа выдает ошибку уникальности. Почему это происходит?
Разберем, как формируется ключ записи регистра сведений. Ключ записи — это набор полей, который однозначно идентифицирует строку в базе данных. Именно по этому ключу платформа проверяет уникальность при попытке записи данных. Правила формирования ключа зависят от настроек регистра:
Период.Регистратор является сервисным полем для связи, но не входит в ключ записи.Это критически важный момент: для непериодического регистра уникальность определяется только составом его измерений. Если два разных документа пытаются записать в такой регистр одинаковые значения измерений, возникнет ошибка, так как с точки зрения платформы это дублирование одной и той же записи, несмотря на разные ссылки в поле Регистратор.
Рассмотрим, как это влияет на обмен в РИБ. Выясним причину, по которой обмен «встает» при загрузке данных в периферийную базу. Когда мы регистрируем изменения для обмена, платформа фиксирует текущее состояние объектов. При формировании файла выгрузки (XML) платформа группирует объекты по типам метаданных и записывает их в пакет.
Проанализируем сценарий:
Поскольку в приемнике (база №2) записи документа А еще существуют, попытка создать такие же записи документом Б приводит к конфликту ключей. Платформа 1С не гарантирует строгую транзакционную последовательность «удаление-запись» внутри одного сообщения обмена для наборов записей разных регистраторов.
Для наглядности проанализируем пример кода, который моделирует поведение платформы при записи движений двумя документами. Рассмотрим, как неправильное управление набором записей приводит к ошибке уникальности:
Процедура КомандаЗаписатьНаСервере()
// Пытаемся записать данные для Документа №1
НаборЗаписей1 = РегистрыСведений.ТестовыйРегистр.СоздатьНаборЗаписей();
Док1 = Документы.ТестовыйДокумент.НайтиПоНомеру("001");
НаборЗаписей1.Отбор.Регистратор.Установить(Док1);
НоваяЗапись1 = НаборЗаписей1.Добавить();
НоваяЗапись1.ИзмерениеСтатус = Истина; // Устанавливаем значение измерения
НаборЗаписей1.Записать(); // Здесь все пройдет успешно
// Пытаемся записать данные для Документа №2 с тем же значением измерения
НаборЗаписей2 = РегистрыСведений.ТестовыйРегистр.СоздатьНаборЗаписей();
Док2 = Документы.ТестовыйДокумент.НайтиПоНомеру("002");
НаборЗаписей2.Отбор.Регистратор.Установить(Док2);
НоваяЗапись2 = НаборЗаписей2.Добавить();
НоваяЗапись2.ИзмерениеСтатус = Истина; // Ключевое поле совпадает с записью Док1!
НаборЗаписей2.Записать(); // ОШИБКА: Запись с такими ключевыми полями существует!
КонецПроцедуры
В данном примере мы видим, что даже если Регистратор у наборов разный, платформа блокирует запись из-за совпадения поля ИзмерениеСтатус. При обмене РИБ происходит ровно то же самое, но на уровне системных механизмов загрузки XML.
Самый простой и рекомендуемый способ — изменить архитектуру регистра. Посмотрим, как это поможет. Если мы сделаем регистр периодическим и установим режим записи "По позиции регистратора", платформа автоматически включит регистратор в ключ записи. Это обеспечит уникальность движений каждого документа, даже если все остальные измерения совпадают.
Важные нюансы:
Если вы не хотите делать регистр периодическим, мы можем применить другой подход. Разберем его по шагам:
ДокументОснование с типом ДокументСсылка (или составным типом).ЭтотОбъект.Ссылка).В этом случае поле Регистратор все еще остается системным, но наше новое измерение ДокументОснование входит в ключ уникальности. Теперь записи от разных документов всегда будут иметь разные ключи, и ошибка в РИБ исчезнет, так как при загрузке наборы перестанут "наступать" друг на друга.
Если бизнес-логика жестко диктует, что в регистре должна быть только одна актуальная запись по конкретному набору измерений (без учета регистратора), то необходимо пересмотреть способ записи. Рассмотрим алгоритм:
Вместо стандартного режима Движения.ИмяРегистра.Записывать = Истина в модуле документа, используйте явную очистку конфликтующих записей перед записью новых. Однако помните, что в РИБ это не спасет от ошибки при загрузке, так как там работает стандартный механизм. Поэтому для РИБ лучшим решением остается Изменение состава ключевых полей (измерений).
Как показывает практика и обсуждение на форумах, данное поведение платформы является "особенностью реализации". Платформа оптимизирована для передачи состояния объектов, а не истории транзакций. В версиях платформы 8.3.17 и выше механизм регистрации изменений постоянно дорабатывается, но фундаментальное правило уникальности ключа записи регистра сведений остается неизменным с версии 8.0.
Резюмируем наши выводы:
Регистратор в непериодическом регистре не обеспечивает уникальность записи.Следуя этим рекомендациям, вы обеспечите стабильную работу распределенной информационной базы и избавитесь от необходимости ручного вмешательства в процесс обмена данными.