Как определить, что код выполняется в процессе обмена РИБ в 1С?

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

При разработке в распределенных информационных базах (РИБ) программисты часто сталкиваются с необходимостью разграничить выполнение бизнес-логики. Типичный сценарий: в системе настроена подписка на событие ПриЗаписи для набора записей регистра сведений, которая выполняет сложные расчеты, проверки или заполнение связанных данных. Однако при синхронизации (загрузке данных из другого узла) эту логику необходимо отключать. В противном случае мы рискуем получить критическое падение производительности, искажение данных (так как на узле-источнике они уже были проверены и рассчитаны) или зацикливание регистрации изменений.

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

Способ 1. Использование платформенного свойства ОбменДанными.Загрузка

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

Давайте проанализируем, как это работает. При получении сообщения обмена и вызове метода ПланыОбмена.ПрочитатьИзменения() платформа самостоятельно считывает сериализованные данные из XML и записывает их в базу данных текущего узла. В момент этой записи свойству ОбменДанными.Загрузка автоматически присваивается значение Истина.

Для реализации обхода подписки нам достаточно добавить простую проверку в самом начале обработчика подписки на событие. Рассмотрим практический пример кода:


Процедура ПриЗаписиНабораЗаписейРегистра(Источник, Отказ, Замещение) Экспорт
    
    // Обязательная проверка на загрузку данных в процессе обмена
    Если Источник.ОбменДанными.Загрузка Тогда
        Возврат;
    КонецЕсли;
    
    // Здесь располагается основная логика, которая должна выполняться 
    // только при интерактивной или программной работе пользователя в текущей базе
    ВыполнитьПолезнуюЛогику(Источник);
    
КонецПроцедуры

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

Глубокий технический анализ: Что на самом деле происходит при установке флага Загрузка?

Понимание внутренних механизмов платформы помогает избежать неочевидных ошибок. Свойство ОбменДанными.Загрузка = Истина не просто сигнализирует коду о том, что идет обмен. Оно меняет поведение самой СУБД и платформы при записи объекта:

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

Способ 2. Обход ограничений при записи через регистратор (Дополнительные свойства)

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

Но как быть, если подписка назначена на набор записей регистра, а флаг устанавливается в документе? Напрямую прочитать ДополнительныеСвойства набора записей не получится, так как при проведении документа платформа формирует набор и записывает его обособленно. Выясним, как элегантно обойти это ограничение.

Мы можем обратиться к регистратору набора записей напрямую из подписки и проанализировать его свойства. Разберем этот алгоритм по шагам:

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

// В модуле проведения документа или обработчике перед записью
ЭтотОбъект.ДополнительныеСвойства.Вставить("ОтключитьЛогикуВПодписке", Истина);
  1. В обработчике подписки на событие ПриЗаписи набора записей регистра сведений мы извлекаем этот флаг через отбор по регистратору:

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

Способ 3. Определение процесса выгрузки данных (экспорта)

Бывают обратные задачи, когда тяжелую логику в подписке нужно отключить не при приеме данных, а в процессе выгрузки (экспорта). Например, когда информационная база обходит зарегистрированные изменения для сериализации в XML-сообщение обмена. В этот момент свойство ОбменДанными.Загрузка равно Ложь, так как данные не записываются в базу из внешнего источника, но нагрузка на систему все равно возрастает. Посмотрим на альтернативные варианты решения этой задачи:

Вариант А. Анализ фонового задания обмена

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


ТекущееЗадание = ФоновыеЗадания.ТекущиеДанные();
Если ТекущееЗадание <> Неопределено Тогда
    НаименованиеЗадания = НРег(ТекущееЗадание.Наименование);
    // Проверяем вхождение ключевых слов синхронизации БСП
    Если Найти(НаименованиеЗадания, "обмен") > 0 
       Или Найти(НаименованиеЗадания, "синхронизация") > 0 Тогда
        Возврат; // Выполняется фоновый обмен, выходим
    КонецЕсли;
EndЕсли;

Вариант Б. Проверка специализированного пользователя

Если для выполнения процедур обмена (РИБ, интеграции) заведен отдельный служебный пользователь, мы можем проверять его имя в подписке. Это весьма надежный способ контроля сеанса:


ИмяТекущегоПользователя = ПользователиИнформационнойБазы.ТекущийПользователь().Имя;
Если ВРег(ИмяТекущегоПользователя) = "АДМИНИСТРАТОР_ОБМЕНА" 
   Или ВРег(ИмяТекущегоПользователя) = "СЛУЖЕБНЫЙ_РИБ" Тогда
    Возврат;
КонецЕсли;

Вариант В. Использование параметров сеанса

Если обмен запускается через ваши собственные регламентные задания или обработки, идеальным решением будет создание специального параметра сеанса, например, ПараметрыСеанса.ВыполняетсяОбменДанными (тип Булево). Перед началом выгрузки или загрузки мы устанавливаем этот параметр в значение Истина, а по завершении возвращаем в Ложь. В подписке останется сделать простую проверку:


// В модуле подписки:
Если ПараметрыСеанса.ВыполняетсяОбменДанными Тогда
    Возврат;
КонецЕсли;

Способ 4. Предотвращение зацикливания регистрации (Стандарты БСП)

Иногда подписка на событие используется для регистрации изменений объектов на узлах обмена. Если при загрузке данных сработает подписка и зарегистрирует этот же объект на отправку обратно, возникнет бесконечный цикл обмена («эхо-регистрация»). Для решения этой проблемы в БСП разработан стандартный механизм.

Чтобы отключить авторегистрацию при записи объекта во время загрузки, в БСП передают специальный параметр в дополнительные свойства объекта:


Источник.ОбменДанными.Загрузка = Истина;
Источник.ДополнительныеСвойства.Вставить("ОтключитьМеханизмРегистрацииОбъектов", Истина);

Этот флаг анализируется встроенными механизмами БСП, предотвращая повторную регистрацию изменений на других узлах и сохраняя целостность топологии РИБ.

Пошаговое руководство: Как отладить и протестировать подписку в режиме имитации РИБ

Для того чтобы убедиться, что ваша подписка корректно отрабатывает и умеет «засыпать» во время обмена, рекомендуется написать небольшую тестовую обработку. С ее помощью мы сымитируем поведение платформы при загрузке данных.

Разберем по шагам, как написать такой тест:

  1. Создайте внешнюю обработку в конфигураторе.
  2. Добавьте на форму кнопку и напишите следующий обработчик нажатия:

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

Типичные ошибки разработчиков при работе с РИБ и подписками

Давайте разберем основные грабли, на которые наступают даже опытные разработчики при интеграции кода в РИБ:

Мы подробно проанализировали основные инструменты платформы 1С:Предприятие 8 и прикладных решений БСП для управления поведением кода в процессе РИБ-синхронизации. Использование свойства ОбменДанными.Загрузка решает подавляющее большинство задач, а для оставшихся сложных архитектурных сценариев отлично подходят параметры сеанса, анализ фоновых заданий и передача признаков через дополнительные свойства объектов.

← На главную