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