Как присвоить номер сообщения изменениям в плане обмена без физической записи файла

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

При разработке механизмов интеграции часто возникает ситуация, когда стандартный обмен данными (через XML-файлы определенного формата) не подходит, но механизм регистрации изменений в Планах обмена крайне удобен. Мы можем использовать план обмена только как «очередь» изменившихся объектов, которую нужно выгрузить в произвольном формате (например, через JSON или HTTP-запрос) и затем очистить.

Основная сложность заключается в том, чтобы корректно присвоить номер сообщения зарегистрированным записям. Это необходимо для обеспечения целостности данных: если во время процесса выгрузки объект будет снова изменен другим пользователем, мы не должны удалять его регистрацию. В этой статье мы подробно разберем, как заставить платформу 1С пронумеровать изменения без создания реальных файлов на диске.

Зачем вообще нужен номер сообщения при произвольной выгрузке

Рассмотрим ситуацию: мы решили просто выбрать все изменения запросом и после выгрузки очистить их методом ПланыОбмена.УдалитьРегистрациюИзменений(). Если база многопользовательская, велик риск «потерять» данные. Пока наша обработка формировала пакет данных для отправки, пользователь мог изменить и записать один из объектов, который уже попал в наш пакет. Если мы просто удалим всю регистрацию по узлу, то изменение, внесенное пользователем после начала нашей выгрузки, будет стерто, и в следующую выгрузку объект не попадет.

Использование номера сообщения решает эту проблему:

  1. Мы фиксируем срез изменений, присваивая им уникальный номер.
  2. Если объект перезаписывается в процессе нашей работы, платформа автоматически сбрасывает номер сообщения для этого объекта в 0 (или NULL).
  3. При вызове УдалитьРегистрациюИзменений(Узел, Номер) будут удалены только те записи, чей номер совпадает с нашим. Новые изменения останутся в базе и попадут в следующий сеанс обмена.

Решение 1: Использование записи сообщения в память (строку)

Чтобы избежать обращения к файловой системе, мы можем использовать метод УстановитьСтроку() объекта ЗаписьXML. Рассмотрим этот алгоритм по шагам.

Проанализируем программный код, который позволяет «обмануть» систему и пронумеровать записи в памяти:


// Подготовим необходимые объекты
УзелОбмена = ПланыОбмена.МойПланОбмена.НайтиПоКоду("SiteExchange");
ЗаписьXML = Новый ЗаписьXML;
ЗаписьXML.УстановитьСтроку(); // Всё будет происходить в оперативной памяти

ЗаписьСообщения = ПланыОбмена.СоздатьЗаписьСообщения();

// 1. Начинаем запись сообщения. 
// В этот момент платформа инкрементирует номер отправленного сообщения на узле.
ЗаписьСообщения.НачатьЗапись(ЗаписьXML, УзелОбмена);

НомерСформированногоСообщения = ЗаписьСообщения.НомерСообщения;

// 2. Выбираем изменения. 
// Именно этот вызов проставляет полученный НомерСообщения в таблицу регистрации изменений.
ВыборкаИзменений = ПланыОбмена.ВыбратьИзменения(УзелОбмена, НомерСформированногоСообщения);

Пока ВыборкаИзменений.Следующий() Цикл
    // Здесь мы можем выполнять свою произвольную логику:
    // Формировать JSON, отправлять данные по HTTP или записывать в свою таблицу.
    ОбъектДанных = ВыборкаИзменений.Данные;
    // ... логика обработки ...
КонецЦикла;

// 3. Завершаем запись сообщения.
ЗаписьСообщения.ЗавершитьЗапись();
СформированнаяПустаяСтрокаXML = ЗаписьXML.Закрыть(); 

// 4. Удаляем только те изменения, которые получили наш номер.
ПланыОбмена.УдалитьРегистрациюИзменений(УзелОбмена, НомерСформированногоСообщения);

В данном примере мы используем ЗаписьXML.УстановитьСтроку(), что полностью исключает создание временных файлов на жестком диске сервера. Это работает быстро и эффективно.

Решение 2: Тщательный контроль через транзакции и массивы

Некоторые разработчики предпочитают не полагаться на стандартный механизм нумерации, если логика обмена очень сложная. Рассмотрим альтернативный подход, который обсуждался участниками сообщества. Суть его заключается в ручном контроле состава выгруженных данных внутри транзакции.

Разберем логику этого метода:

  1. Мы сначала собираем все объекты, которые нужно выгрузить, в массив или таблицу значений.
  2. Выполняем отправку данных во внешнюю систему.
  3. Важный момент: После успешной отправки мы открываем транзакцию.
  4. Внутри транзакции мы снова проверяем регистрацию изменений.
  5. Если объект из нашего «отправленного» списка все еще находится в регистрации и не имеет новых изменений (которые могли появиться за время отправки), мы удаляем его регистрацию пообъектно.

Посмотрим, как это может выглядеть в коде:


// Предварительный сбор данных
СписокВыгруженныхОбъектов = Новый Массив;
Выборка = ПланыОбмена.ВыбратьИзменения(УзелОбмена, 0); // Берем всё без присвоения номера
Пока Выборка.Следующий() Цикл
    // Выгружаем...
    СписокВыгруженныхОбъектов.Добавить(Выборка.Данные);
КонецЦикла;

// ... блок кода отправки данных (может быть длительным) ...

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

Важное примечание: Этот метод гораздо более трудозатратен и опасен с точки зрения производительности (блокировки). Использование стандартного механизма с НомерСообщения, как в первом решении, является рекомендуемым подходом фирмы «1С».

Разбор нюансов работы метода ВыбратьИзменения()

Проанализируем, почему метод ВыбратьИзменения(Узел, Номер) так важен. Многие начинающие программисты полагают, что этот метод просто фильтрует записи. На самом деле, когда вы передаете в него второй параметр (номер), платформа выполняет Update в таблице регистрации изменений в базе данных (SQL). Она прописывает этот номер всем записям, у которых номер на данный момент равен 0.

Это происходит атомарно. Именно поэтому, даже если вы не записываете данные в XML-файл, вызов этого метода обязателен для «маркировки» пакета. Если вы вызовете ВыбратьИзменения без второго параметра, номер присвоен не будет, и метод УдалитьРегистрациюИзменений(Узел, Номер) ничего не удалит.

Очистка «зависших» номеров

Иногда процесс выгрузки может завершиться аварийно (например, выключился сервер или произошла ошибка в коде формирования данных). В этом случае в таблице регистрации останутся записи, которым уже присвоен номер сообщения, но они так и не были удалены.

Рассмотрим, как с этим бороться. При следующем запуске обмена рекомендуется анализировать наличие таких записей. Стандартный метод ВыбратьИзменения без указания номера выберет все записи (и с номерами, и без). Однако, если вы хотите перепослать именно «зависший» пакет, вы можете вызвать ВыбратьИзменения(Узел, НомерЗависшегоПакета).

Практический совет: Чтобы узнать номер следующего сообщения, которое ожидает платформа, можно обратиться к свойству узла УзелОбмена.НомерОтправленногоСообщения. Номер сообщения, который будет присвоен при следующем вызове НачатьЗапись, обычно равен НомерОтправленногоСообщения + 1.

Выводы

Мы выяснили, что для реализации качественного обмена без использования файлов достаточно использовать стандартные объекты ЗаписьСообщенияОбмена и ЗаписьXML, работающую со строкой. Это позволяет:

Рассмотренные подходы позволяют гибко настраивать интеграции любой сложности, не ограничиваясь рамками стандартного формата обмена данными.

← На главную