В практике разработки на платформе «1С:Предприятие 8.3» часто возникает задача организации одностороннего или двустороннего обмена данными между базами без создания полноценной Распределенной Информационной Базы (РИБ/РБД). Для этого идеально подходит стандартный объект конфигурации План обмена, у которого свойство Распределенная информационная база установлено в значение Ложь. В данной статье мы подробно разберем, как правильно использовать этот механизм, избегая классических ошибок новичков, связанных с потерей данных и неоптимальной производительностью.
План обмена в 1С выполняет две ключевые функции:
Если мы не используем РИБ, платформа не накладывает жестких ограничений на идентичность конфигураций в базах-участниках обмена и не управляет передачей конфигурации. Это развязывает нам руки: мы можем обмениваться данными между совершенно разными прикладными решениями (например, «1С:Бухгалтерия» и «1С:Розница» или ERP и мобильным приложением).
Рассмотрим популярную, но крайне опасную схему обмена, которую часто пытаются реализовать начинающие разработчики:
ПланыОбмена.УдалитьРегистрациюИзменений(Узел).В чем заключается смертельная опасность этого подхода?
Процесс выборки изменений, их подготовки, упаковки и отправки может занимать значительное время (от нескольких секунд до десятков минут на больших объемах данных). В этот самый момент пользователи продолжают активно работать в базе данных. Если во время формирования файла выгрузки пользователь запишет новый документ или изменит существующий, платформа моментально добавит новую запись в таблицу регистрации изменений плана обмена.
Когда выгрузка завершится и ваш код вызовет метод ПланыОбмена.УдалитьРегистрациюИзменений(Узел), платформа удалит вообще все зарегистрированные изменения для данного узла — включая то новое изменение, которое произошло во время выгрузки. Как результат, этот измененный объект никогда не попадет во внешнюю систему. Произойдет тихая и необратимая потеря данных.
Чтобы раз и навсегда решить проблему конкурентного доступа и потери данных (Race Condition), платформа 1С предоставляет механизм номеров сообщений (номеров пакетов). Номера сообщений служат для разграничения изменений, которые уже попали в текущую транзакцию выгрузки, и тех, которые возникли непосредственно в процессе выгрузки или после нее.
Разберем правильный алгоритм работы по шагам:
Перед началом выгрузки мы должны увеличить счетчик отправленных сообщений у узла обмена и выбрать изменения, зафиксировав за ними этот номер. Проанализируем пример кода:
// Получаем объект узла для изменения номера отправленного сообщения
УзелОбъект = СсылкаНаУзел.ПолучитьОбъект();
НовыйНомерОтправленного = УзелОбъект.НомерОтправленного + 1;
// Начинаем транзакцию для обеспечения согласованности данных
НачатьТранзакцию();
Попытка
// Записываем новый номер в узел
УзелОбъект.НомерОтправленного = НовыйНомерОтправленного;
УзелОбъект.Записать();
ЗафиксироватьТранзакцию();
Исключение
ОтменитьТранзакцию();
ВызватьИсключение "Не удалось зафиксировать номер отправленного сообщения: " + ОписаниеОшибки();
КонецПопытки;
// Выбираем изменения, привязывая их к новому номеру сообщения
ВыборкаИзменений = ПланыОбмена.ВыбратьИзменения(СсылкаНаУзел, НовыйНомерОтправленного);
Метод ПланыОбмена.ВыбратьИзменения() делает важнейшую вещь: он берет все зарегистрированные на данный момент изменения и сопоставляет их с числом НовыйНомерОтправленного. Если во время работы нашего цикла выгрузки пользователи изменят какие-то объекты, эти новые изменения получат признак номера сообщения NULL и не будут затронуты последующей очисткой.
Мы обходим выборку изменений и записываем данные в файл, XML, JSON или отправляем через HTTP/web-сервисы:
Пока ВыборкаИзменений.Следующий() Цикл
ДанныеОбъекта = ВыборкаИзменений.Получить();
// Выполняем экспорт данных (например, через КД или EnterpriseData)
// ЗаписатьДанныеВПоток(ДанныеОбъекта);
КонецЦикла;
Только после того, как файл успешно записан или удаленно получен принимающей стороной, мы можем безопасно удалить регистрацию изменений. Но делать это нужно строго с указанием номера сообщения, который мы зафиксировали на первом шаге:
// Удаляем только те изменения, которые вошли в пакет с НомеромОтправленного
ПланыОбмена.УдалитьРегистрациюИзменений(СсылкаНаУзел, НовыйНомерОтправленного);
Этот метод удалит из таблицы регистрации только те записи, у которых номер сообщения совпадает с переданным параметром. Новые изменения, возникшие во время выгрузки (с номером сообщения NULL), останутся нетронутыми и будут выгружены при следующем сеансе обмена. Мы полностью защитили систему от потери данных!
Даже если мы правильно используем номера сообщений, остается риск физического сбоя: файл обмена может повредиться при передаче по FTP, принимающая база может аварийно завершить работу во время загрузки, или пропадет интернет-соединение. Если мы очистим регистрацию сразу после записи файла на диск в источнике, а приемник его не получит — данные будут потеряны.
Для решения этой проблемы в обменах без РИБ реализуют механизм квитирования (двухэтапного подтверждения доставки):
"Сообщение № 15 успешно загружено".ПланыОбмена.УдалитьРегистрациюИзменений(СсылкаНаУзел, 15);
Если квитанция не пришла (или пришла информация об ошибке), источник не удаляет регистрацию. При очередном запуске обмена все эти данные снова попадут в новый файл выгрузки.
Иногда разработчики, не желая разбираться в механизмах планов обмена, решают создать собственную систему отслеживания изменений. Они создают самописный регистр сведений (например, ОчередьВыгрузки) и вешают подписки на события ПриЗаписи всех необходимых документов и справочников, чтобы вручную писать туда ссылки на измененные объекты.
Выясним, почему этот подход (метко названный на форумах «своим казино со своим блэкджеком») является архитектурно неверным:
УдалениеОбъекта. Нам остается лишь передать эту информацию в приемник, чтобы объект удалился и там. В случае с самописным регистром сведений вам придется писать сложнейшие обработчики в подписках на удаление, бороться с «битыми» ссылками (объект удален, ссылка превратилась в «Объект не найден», и мы не знаем, что это было за сущность) и постоянно чистить зависшие записи вручную.Рассмотрим, как данная задача решается в современных конфигурациях «1С». Писать обмены «с нуля» на чистых планах обмена в промышленной разработке сегодня считается дурным тоном. Стандартом фирмы «1С» является использование подсистемы «Синхронизация данных» из состава БСП:
Резюмируем: Использование планов обмена без признака РИБ — это мощный, быстрый и правильный инструмент для регистрации изменений. Главное — всегда использовать штатный механизм номеров сообщений платформы 1С для предотвращения потери данных и по возможности опираться на проверенные временем механизмы БСП.