В процессе сопровождения распределенных информационных баз (РИБ) или обычных обменов данными программисты 1С иногда сталкиваются с крайне неприятной ситуацией: в очередь регистрации изменений попадает «фантомный» объект. При попытке выгрузки такого объекта база-приемник выдает ошибку, так как не может обработать пустые ссылки или битые данные, а стандартные попытки отменить регистрацию программно заканчиваются неудачей из-за отсутствия физического объекта в базе. Разберем детально, как диагностировать и устранить эту проблему.
Ситуация, когда в сообщении обмена появляется элемент с идентификатором 00000000-0000-0000-0000-000000000000, обычно свидетельствует о нарушении логической целостности базы данных. Разберем основные причины возникновения «фантомов»:
Удалить() вместо пометки на удаление, при этом в таблице регистрации изменений (_ChngRec) осталась запись, ссылающаяся на уже несуществующий ID.Проблема осложняется тем, что стандартный метод ПланыОбмена.УдалитьРегистрациюИзменений часто требует в качестве второго параметра ссылку на объект. Но так как объекта нет (ссылка пустая или битая), система выдает ошибку при попытке обращения к свойствам этого элемента.
Рассмотрим самый безопасный способ, который не требует прямого вмешательства в SQL. Вместо того чтобы пытаться удалить регистрацию конкретного «битого» документа, мы можем временно очистить регистрацию для всего типа документов по данному узлу, а затем перерегистрировать нужные объекты.
Проанализируем пример кода, который позволяет очистить регистрацию для конкретного метаданного (например, Документ.РеализацияТоваровУслуг):
// Определяем узел, в котором возникла проблема
УзелОбмена = ПланыОбмена.СинхронизацияДанныхЧерезУниверсальныйФормат.НайтиПоКоду("001");
// Вместо передачи ссылки передаем объект метаданных
// Это очистит ВСЮ очередь изменений для данного типа документа по этому узлу
ПланыОбмена.УдалитьРегистрациюИзменений(УзелОбмена, Метаданные.Документы.РеализацияТоваровУслуг);
После выполнения этого кода «битая» запись исчезнет. Далее нам необходимо восстановить регистрацию актуальных документов. Это можно сделать простым запросом или через консоль регистрации изменений. Рассмотрим пример перерегистрации:
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Реализация.Ссылка КАК Ссылка
|ИЗ
| Документ.РеализацияТоваровУслуг КАК Реализация
|ГДЕ
| Реализация.Дата >= &НачалоПериода";
Запрос.УстановитьПараметр("НачалоПериода", '20230101');
Результат = Запрос.Выполнить().Выбрать();
Пока Результат.Следующий() Цикл
ПланыОбмена.ЗарегистрироватьИзменения(УзелОбмена, Результат.Ссылка);
КонецЦикла;
Если нам достоверно известно, что «фантом» — это результат удаления, мы можем попробовать передать в метод удаления регистрации специальный объект УдалениеОбъекта. Рассмотрим, как это работает программно:
// Создаем пустую ссылку нужного типа с нулевым GUID (или нужным битым GUID)
БитыйИдентификатор = Новый УникальныйИдентификатор("00000000-0000-0000-0000-000000000000");
БитыйОбъект = Документы.РеализацияТоваровУслуг.ПолучитьСсылку(БитыйИдентификатор);
// Формируем объект удаления
ДанныеДляУдаления = Новый УдалениеОбъекта(БитыйОбъект);
// Пытаемся удалить регистрацию именно для этого "удаления"
ПланыОбмена.УдалитьРегистрациюИзменений(УзелОбмена, ДанныеДляУдаления);
Важный момент: если GUID действительно состоит из одних нулей, платформа может не позволить создать УдалениеОбъекта стандартным способом. В этом случае перейдем к более радикальным методам.
Когда база данных имеет объем 800 Гб и более, полное тестирование и исправление (ТиИ) может занять несколько суток, что недопустимо. В такой ситуации эффективнее всего прибегнуть к прямому SQL-запросу. Выясним, как найти нужную таблицу.
Для начала воспользуемся функцией ПолучитьСтруктуруХраненияБазыДанных, чтобы узнать имя физической таблицы регистрации изменений для нашего документа:
Структура = ПолучитьСтруктуруХраненияБазыДанных(,"Документ.РеализацияТоваровУслуг");
// В полученной таблице ищем строку, где Назначение = "ТаблицаРегистрацииИзменений"
// Обычно имя выглядит как _Document123_ChngRec
После того как имя таблицы (например, _Document150_ChngRec) и имя поля ссылки (обычно _IDRRef) определены, мы можем выполнить запрос в консоли SQL (Management Studio):
SELECT * FROM _Document150_ChngRec
WHERE _IDRRef = 0x00000000000000000000000000000000
DELETE FROM _Document150_ChngRec
WHERE _IDRRef = 0x00000000000000000000000000000000
AND _NodeRRef = (SELECT _IDRRef FROM _ExchangePlan45 WHERE _Code = '001')
Внимание: любые операции напрямую в SQL нарушают условия лицензионного соглашения и должны проводиться только после создания резервной копии базы данных!
Разберем причину, по которой «фантомный» элемент останавливает весь обмен. В механизме РИБ при загрузке сообщения платформа пытается создать объект или вызвать обработку удаления для каждой записи. Когда она встречает элемент с Zero GUID, срабатывают следующие механизмы:
ПередЗаписью или ПриЗаписи, обращение к свойствам «битого» объекта вызывает исключение «Объект не найден».XDTO, используемый в современных обменах, может просто не пройти валидацию схемы при десериализации такого узла XML.Чтобы избежать подобных «бородавок» в плане обмена в будущем, рекомендуем придерживаться следующих правил:
Удалить() для объектов, участвующих в обменах. Используйте только УстановитьПометкуУдаления().Попытка...Исключение и контролируйте завершение транзакций.Проанализировав ситуацию, мы видим, что проблема «фантомных» элементов решаема. Наиболее рациональный путь для программиста — полная очистка регистрации по конкретному типу метаданных с последующей перерегистрацией, так как это штатный метод платформы, не требующий копания в SQL и гарантирующий целостность данных.