При построении ИТ-ландшафта крупных предприятий часто возникает задача организации топологии обмена «один ко многим». Типичный пример: централизованное ведение нормативно-справочной информации (НСИ), в частности справочника Номенклатура, в единой базе 1С:ERP Управление предприятием с последующей трансляцией изменений в несколько (иногда десятки) локальных баз 1С:Бухгалтерия предприятия (БП). При этом в часть баз БП должен уходить полный пакет данных (документы, справочники), а в другие — строго только измененная номенклатура.
В этой статье мы подробно разберем, как решить эту задачу в рамках типового механизма БСП (Библиотеки стандартных подсистем) на платформе 1С:Предприятие 8.3, почему могут «не работать» измененные правила регистрации, и какие альтернативные архитектурные подходы следует использовать при масштабировании системы, чтобы избежать критического падения производительности СУБД.
Главное заблуждение при работе с планами обмена БСП заключается в том, что правила регистрации объектов (ПРО) едины для всего плана обмена, а значит, настроить индивидуальное поведение для разных баз-приемников в рамках одного и того же плана невозможно. Действительно, физический XML-файл правил регистрации загружается один на весь план обмена (например, СинхронизацияДанныхЧерезУниверсальныйФормат). Однако логика этих правил может динамически адаптироваться под каждый конкретный узел обмена.
Проанализируем, как реализовать этот механизм по шагам:
СинхронизацияДанныхЧерезУниверсальныйФормат в ERP. Добавим новый реквизит ВыгружатьТолькоНоменклатуру с типом Булево. Если конфигурация находится на поддержке, это расширение легко реализовать через механизм расширений конфигурации.При обработке для каждого такого правила пропишем условие проверки нашего нового реквизита.Рассмотрим пример кода, который необходимо вставить в обработчик правила регистрации объектов (ПРО) для документов или второстепенных справочников:
// Обработчик события "При обработке" в правилах регистрации (ПРО)
// Доступные параметры:
// Объект - проверяемый объект базы данных
// Получатель - ссылка на узел плана обмена (наш адресат)
// Отказ - Булево, если Истина - регистрация отменяется
Попытка
// Проверяем, включен ли на узле-получателе флаг ограничения выгрузки
Если Получатель.ВыгружатьТолькоНоменклатуру Тогда
// Если это не справочник Номенклатура, отменяем регистрацию изменения
Если ТипЗнч(Объект) <> Тип("СправочникСсылка.Номенклатура") Тогда
Отказ = Истина;
КонецЕсли;
КонецЕсли;
Исключение
// Исключаем падение системы, если реквизит на узле еще не создан в какой-то из баз
ЗаписьЖурналаРегистрации(
"ОбменДанными.ПравилаРегистрации",
УровеньЖурналаРегистрации.Предупреждение,
,
,
"Ошибка проверки реквизита ВыгружатьТолькоНоменклатуру: " + ПодробноеПредставлениеОшибки(ИнформацияОбОшибке())
);
КонецПопытки;
Благодаря этому несложному коду, система при записи любого объекта (например, документа «Реализация товаров и услуг») будет опрашивать все узлы обмена. Для двух стандартных баз, где флаг ВыгружатьТолькоНоменклатуру равен Ложь, документ успешно зарегистрируется к обмену. Для остальных 10 узлов, где флаг равен Истина, сработает Отказ = Истина, и документ не попадет в таблицу регистрации изменений.
Часто разработчики сталкиваются с ситуацией: правила изменены, успешно загружены в ERP через интерфейс «Параметры подключения» -> «Правила регистрации», но документы все равно продолжают регистрироваться на «урезанных» узлах. Выясним причину этого поведения:
/ЗапуститьОбновлениеИнформационнойБазы или воспользуйтесь программным методом очистки кэша БСП через вызов функции общего модуля ОбменДаннымиПолнотекстовыйПоиск.ОчиститьКэшПравилРегистрации().Важно четко разделять зоны ответственности инструментов при работе с современными обменами через Универсальный формат (EnterpriseData):
МенеджерОбменаЧерезУниверсальныйФормат), который преобразует объекты 1С в XML/XDTO-формат пакета данных и обратно. КД 3.0 никак не управляет тем, какие именно объекты и для каких узлов должны регистрироваться.Рассмотрим ситуацию с точки зрения производительности СУБД. Наш справочник «Номенклатура» содержит более 300 000 элементов. При изменении или добавлении хотя бы одной позиции номенклатуры (например, при обновлении цен, реквизитов или записи из внешней системы), платформа 1С обязана сделать записи в таблицы регистрации изменений для каждого из узлов плана обмена.
Если у нас настроено 12 узлов (2 полных обмена и 10 только по номенклатуре), то при записи одного элемента будет совершено 12 транзакционных записей в физические таблицы регистрации базы данных. При массовом изменении номенклатуры (например, групповой обработкой или при загрузке прайс-листа) это приведет к лавинообразному росту количества транзакционных блокировок таблиц _ChngRng. Пользователи начнут получать ошибки превышения времени ожидания блокировки данных.
Как решить эту проблему? Разберем два альтернативных архитектурных решения.
Для крупных распределенных систем оптимальным решением является интеграция через корпоративную шину данных (ESB):
1С:Шина или RabbitMQ).Если внедрение полноценной шины данных избыточно, можно реализовать легковесный асинхронный обмен на базе HTTP-сервисов с собственной очередью регистрации, исключающей стандартный механизм планов обмена 1С:
ОчередьВыгрузкиНоменклатуры с измерениями Номенклатура (типа СправочникСсылка) и БазаПолучатель (типа СправочникСсылка или перечисление).ПриЗаписи номенклатуры в ERP мы просто делаем одну быструю запись в наш регистр-очередь для каждой базы-приемника. Запись в плоский регистр сведений выполняется СУБД значительно быстрее, чем запись в служебные таблицы регистрации изменений планов обмена.Посмотрим на пример реализации отправки данных из локальной очереди ERP в базы БП с помощью встроенного языка 1С:
// Процедура регламентного задания в ERP для асинхронной отправки очереди номенклатуры
Процедура ВыполнитьАсинхроннуюОтправкуНоменклатуры() Экспорт
// Выбираем первые 100 записей из очереди, чтобы не перегружать память пакетами
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ ПЕРВЫЕ 100
| Очередь.Номенклатура КАК Номенклатура,
| Очередь.БазаПолучатель КАК БазаПолучатель
|ИЗ
| РегистрСведений.ОчередьВыгрузкиНоменклатуры КАК Очередь";
РезультатЗапроса = Запрос.Выполнить();
Если РезультатЗапроса.Пустой() Тогда
Возврат;
КонецЕсли;
Выборка = РезультатЗапроса.Выбрать();
Пока Выборка.Следующий() Цикл
// Подготавливаем JSON с данными измененного элемента
ЗаписьJSON = Новый ЗаписьJSON;
ЗаписьJSON.УстановитьСтроку();
ДанныеНоменклатуры = Новый Структура;
ДанныеНоменклатуры.Вставить("UID", Строка(Выборка.Номенклатура.УникальныйИдентификатор()));
ДанныеНоменклатуры.Вставить("Наименование", Выборка.Номенклатура.Наименование);
ДанныеНоменклатуры.Вставить("Артикул", Выборка.Номенклатура.Артикул);
ДанныеНоменклатуры.Вставить("Код", Выборка.Номенклатура.Код);
ЗаписатьJSON(ЗаписьJSON, ДанныеНоменклатуры);
ТелоЗапроса = ЗаписьJSON.Закрыть();
// Получаем параметры подключения к конкретной базе БП
ПараметрыПодключения = ПолучитьНастройкиПодключения(Выборка.БазаПолучатель);
HTTPСоединение = Новый HTTPСоединение(
ПараметрыПодключения.АдресСервера,
ПараметрыПодключения.Порт,
ПараметрыПодключения.Пользователь,
ПараметрыПодключения.Пароль,
,
10 // таймаут соединения в секундах
);
HTTPЗапрос = Новый HTTPЗапрос("/bp_api/hs/nomenclature/v1/update");
HTTPЗапрос.УстановитьТелоИзСтроки(ТелоЗапроса, КодировкаТекста.UTF8, ИспользованиеByteOrderMark.НеИспользовать);
HTTPЗапрос.Заголовки.Вставить("Content-Type", "application/json");
Попытка
HTTPОтвет = HTTPСоединение.ОтправитьДляОбработки(HTTPЗапрос);
Если HTTPОтвет.КодСостояния = 200 Тогда
// После успешного приема данных удаляем запись из нашей очереди
НаборЗаписей = РегистрыСведений.ОчередьВыгрузкиНоменклатуры.СоздатьНаборЗаписей();
НаборЗаписей.Отбор.Номенклатура.Установить(Выборка.Номенклатура);
НаборЗаписей.Отбор.БазаПолучатель.Установить(Выборка.БазаПолучатель);
НаборЗаписей.Записать();
Иначе
ЗаписьЖурналаРегистрации(
"ОбменДанными.HTTP",
УровеньЖурналаРегистрации.Ошибка,
,
,
СтроковыеФункцииКлиентСервер.Шаблон(
"Ошибка обновления номенклатуры в базе %1. Код ответа сервера: %2",
Выборка.БазаПолучатель,
HTTPОтвет.КодСостояния
)
);
КонецЕсли;
Исключение
ЗаписьЖурналаРегистрации(
"ОбменДанными.HTTP",
УровеньЖурналаРегистрации.Ошибка,
,
,
ПодробноеПредставлениеОшибки(ИнформацияОбОшибке())
);
КонецПопытки;
КонецЦикла;
КонецПроцедуры
Использование этого подхода полностью развязывает транзакционную нагрузку ERP от доступности и скорости ответа удаленных баз бухгалтерии. Запись данных происходит мгновенно, а регламентное задание гарантирует асинхронную доставку изменений во все приемники по очереди.