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