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