При работе в распределенных информационных базах (РИБ) на платформе 1С:Предприятие специалисты часто сталкиваются с крайне неприятной проблемой: в периферийных узлах внезапно обнаруживаются записи в регистрах накопления или сведений, ссылающиеся на несуществующие документы. В полях отчетов красуется надпись <Объект не найден...> (битая ссылка), хотя в центральной базе данный документ был давно и успешно удален. При этом в локальном режиме работы платформа гарантирует автоматическое удаление всех связанных движений при уничтожении регистратора.
В этой статье мы подробно разберем внутренние механизмы платформы 1С, которые приводят к появлению «осиротевших» движений в РИБ, проанализируем риски для стабильности базы данных и рассмотрим пошаговые методы исправления и полной профилактики этой проблемы.
Для того чтобы понять природу возникновения битых ссылок в регистрах, нам необходимо заглянуть под капот механизмов обмена данными 1С. В штатном (локальном) режиме работы системы удаление документа инициирует транзакционную очистку всех таблиц движений, где этот документ выступает в роли регистратора. За это отвечают свойства конфигурации и логика платформы. Однако при обмене данными в РИБ этот отлаженный механизм дает сбой по нескольким ключевым причинам.
1. Режим загрузки данных ОбменДанными.Загрузка = Истина
Когда пакет изменений из центральной базы (ЦБ) поступает в периферийную базу (ПБ), запись всех объектов (включая удаление) происходит принудительно с установленным признаком ОбменДанными.Загрузка = Истина. Этот режим предназначен для того, чтобы перенести данные «один в один» без выполнения прикладных проверок, проведения дополнительных расчетов и запуска стандартных обработчиков событий в конфигурации.
Если в ЦБ документ физически удаляется администратором напрямую (интерактивно или программно), в файл обмена записывается специальный маркер — УдалениеОбъекта, содержащий только уникальный идентификатор (GUID) и тип документа. При импорте этого маркера в ПБ платформа находит соответствующую запись в таблице документов и физически удаляет ее. Но поскольку импорт идет в режиме Загрузка = Истина, сопутствующие обработчики удаления движений просто не запускаются. Документ исчезает, а строки в таблицах регистров (например, в регистре накопления ТоварыНаСкладах) остаются нетронутыми.
2. Независимая регистрация изменений документов и регистров
В планах обмена таблицы документов и таблицы регистров являются абсолютно независимыми сущностями. При удалении документа в ЦБ платформа регистрирует изменение (удаление) самого документа. Очистка движений регистра накопления в ЦБ также генерирует изменения для таблицы этого регистра (записывается пустой набор записей по регистратору).
Однако в силу различных оптимизаций обмена, настроек фильтрации данных по узлам (например, миграция только по определенным складам или организациям) или банальных сбоев в процессе передачи пакетов, команда на запись пустого набора движений может не зарегистрироваться для выгрузки в конкретный периферийный узел. В результате периферийная база получает и обрабатывает команду «удалить документ», но не получает команду «очистить движения регистра». Получается рассинхронизация.
3. Асинхронность транзакций и коллизии
В крупных распределенных системах изменения могут происходить параллельно. Если в периферийном узле документ был изменен или перепроведен локально практически одновременно с его удалением в центральном узле, то при последующем обмене может возникнуть коллизия. Система примет удаление сущности из центра, но из-за наложения транзакций и разницы во времени регистрации изменений очистка движений будет проигнорирована платформой.
Многие ошибочно полагают, что битые ссылки — это лишь косметический дефект, который портит внешний вид отчетов. На самом деле наличие движений без регистратора несет в себе серьезные технические угрозы:
Регистратор.Склад или Регистратор.Организация). Обращение к реквизиту битой ссылки гарантированно вызывает системную ошибку, приводя к падению фоновых заданий или аварийному закрытию сеанса пользователя.Рассмотрим подробнее три основных способа очистки базы данных от движений по удаленным документам, начиная от точечных программных решений и заканчивая штатными инструментами платформы 1С.
Этот метод является наиболее безопасным и быстрым для больших рабочих баз, так как он не требует монопольного доступа и длительного времени на выполнение. Мы напишем небольшой код, который найдет все движения в регистре ТоварыНаСкладах, у которых регистратор не существует в базе данных, и принудительно запишет по ним пустые наборы записей.
Проанализируем пример кода для точечной очистки регистров:
// Создаем запрос для поиска битых ссылок в регистре
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ РАЗЛИЧНЫЕ
| ТоварыНаСкладах.Регистратор КАК БитыйРегистратор
|ИЗ
| РегистрНакопления.ТоварыНаСкладах КАК ТоварыНаСкладах
|ГДЕ
| ТоварыНаСкладах.Регистратор.Ссылка ЕСТЬ NULL
| И НЕ ТоварыНаСкладах.Регистратор = ЗНАЧЕНИЕ(Документ.РеализацияТоваровУслуг.ПустаяСсылка)"; // Исключаем пустые ссылки, если это необходимо
Выборка = Запрос.Выполнить().Выбрать();
Пока Выборка.Следующий() Цикл
// Создаем пустой набор записей с отбором по битому регистратору
НаборЗаписей = РегистрыНакопления.ТоварыНаСкладах.СоздатьНаборЗаписей();
НаборЗаписей.Отбор.Регистратор.Установить(Выборка.БитыйРегистратор);
// Включаем режим загрузки, чтобы обойти проверку прав и прикладную логику
НаборЗаписей.ОбменДанными.Загрузка = Истина;
Попытка
// Запись пустого набора очищает движения по этому регистратору в СУБД
НаборЗаписей.Записать();
Сообщить("Успешно очищены движения для битого регистратора: " + Выборка.БитыйРегистратор);
Исключение
Сообщить("Не удалось очистить движения для: " + Выборка.БитыйРегистратор + ". Ошибка: " + ОписаниеОшибки());
КонецПопытки;
КонецЦикла;
Использование этого скрипта мгновенно удаляет все осиротевшие записи из физических таблиц СУБД, возвращая остатки товаров в корректное состояние.
Если размер базы данных позволяет запустить монопольный процесс обслуживания, или если вы проводите плановое техническое обслуживание узла РИБ, можно воспользоваться встроенным инструментом конфигуратора.
Разберем этот процесс по шагам:
Важное примечание: На базах объемом в сотни гигабайт этот процесс может занять много часов, поэтому для оперативного исправления на «горячей» базе лучше применять первый способ.
В редких случаях, когда битых движений совсем немного и вам критически важно понять, что это был за документ, можно воссоздать его «призрак». Каждая битая ссылка содержит уникальный идентификатор. Например, в строке <Объект не найден (84:b70015e0c211e880e400155d011f03)> зашифрован GUID документа.
Посмотрим на пример кода, который позволяет воссоздать пустой документ с оригинальным GUID:
// Задаем тип документа и его исходный GUID из битой ссылки
ИмяДокумента = "ПоступлениеТоваровУслуг";
СтрокаGUID = "84b70015-e0c2-11e8-80e4-00155d011f03"; // GUID приводится к стандартному формату с дефисами
УникальныйИдентификатор = Новый УникальныйИдентификатор(СтрокаGUID);
СсылкаНаДокумент = Документы[ИмяДокумента].ПолучитьСсылку(УникальныйИдентификатор);
// Создаем новый документ и принудительно сопоставляем ему старый GUID
НовыйДокумент = Документы[ИмяДокумента].СоздатьДокумент();
НовыйДокумент.УстановитьСсылкуНового(СсылкаНаДокумент);
// Заполняем минимальные обязательные реквизиты
НовыйДокумент.Дата = ТекущаяДата();
НовыйДокумент.Организация = Справочники.Организации.ПустаяСсылка();
// Записываем документ без проведения
Попытка
НовыйДокумент.Записать(РежимЗаписиДокумента.Запись);
Сообщить("Документ успешно воссоздан. Теперь его можно пометить на удаление и удалить штатно.");
Исключение
Сообщить("Ошибка воссоздания: " + ОписаниеОшибки());
КонецПопытки;
После выполнения данного кода вместо битой ссылки в базе появится реальный непроведенный документ. Вы сможете запустить интерактивное удаление помеченных объектов, которое штатно, на уровне бизнес-логики конфигурации, сотрет все связанные с ним движения в периферийной базе.
Борьба с последствиями — это лишь временная мера. Чтобы навсегда исключить появление битых движений в вашей распределенной системе, необходимо пересмотреть методологию работы пользователей и администраторов с базой данных.
Правило 1. Полный запрет на непосредственное физическое удаление данных
У большинства пользователей, включая ключевых менеджеров и локальных администраторов, должно быть полностью отобрано право на непосредственное удаление объектов (интерактивное удаление без пометки на удаление, программное удаление через Shift+Del или обработки). Любое удаление должно происходить исключительно через двухэтапную процедуру.
Правило 2. Соблюдение регламента «Пометка -> Синхронизация -> Удаление»
Безопасный жизненный цикл удаления документа в РИБ должен выглядеть строго следующим образом:
Соблюдение этих простых методологических правил и периодический мониторинг базы данных с помощью предложенного выше программного скрипта позволят вам поддерживать абсолютную ссылочную целостность и идеальный порядок в регистрах вашей распределенной системы 1С:Предприятие.