Работа в распределенных информационных базах (РИБ) с большим количеством узлов (более 300) накладывает серьезные ограничения на процессы создания новых элементов справочников. Типичная ситуация: пользователи на разных узлах создают одну и ту же номенклатуру, получая данные из внешней системы. Поскольку в 1С идентификация объектов в РИБ по умолчанию идет по УникальныйИдентификатор (GUID), при синхронизации в центральной базе (ЦБ) неизбежно возникают дубли — визуально одинаковые товары с разными внутренними ссылками.
В этой статье мы подробно проанализируем ситуацию, разберем архитектурные ошибки и рассмотрим несколько эффективных способов решения проблемы: от онлайн-запросов к центральной базе до использования алгоритмов хеширования для генерации идентичных идентификаторов.
Когда мы создаем новый элемент справочника Номенклатура методом Справочники.Номенклатура.СоздатьЭлемент(), система генерирует новый случайный GUID. Если два пользователя на узлах "Филиал 1" и "Филиал 2" создадут товар с артикулом "АБ-100", в каждой базе появится объект с собственным уникальным кодом. При обмене с ЦБ оба этих объекта попадут в общую базу. Система 1С не знает, что это один и тот же товар, так как для нее "уникальность" определяется ссылкой, а не строковым артикулом.
Рассмотрим наиболее элегантный способ, который не требует постоянного интернет-соединения в момент записи объекта. Мы можем заставить 1С генерировать не случайный GUID, а GUID, основанный на уникальном ключе из внешней базы (например, на ID товара во внешней системе или на его артикуле).
Алгоритм действий:
ExternalID).Разберем пример кода для реализации этого механизма:
Функция ПолучитьGUIDИзСтроки(Знач ИсходнаяСтрока) Экспорт
// Используем объект ХешированиеДанных для получения MD5
Хеш = Новый ХешированиеДанных(АлгоритмХеширования.MD5);
Хеш.Добавить(ИсходнаяСтрока);
СтрокаХеша = СтрЗаменить(Строка(Хеш.ХешСумма), " ", "");
// Форматируем строку хеша в формат UUID: XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX
// Пример: 4e91244e-1284-4861-a070-58079a49931b
СтрокаGUID = Сред(СтрокаХеша, 1, 8) + "-" +
Сред(СтрокаХеша, 9, 4) + "-" +
Сред(СтрокаХеша, 13, 4) + "-" +
Сред(СтрокаХеша, 17, 4) + "-" +
Сред(СтрокаХеша, 21, 12);
Возврат Новый УникальныйИдентификатор(СтрокаGUID);
КонецФункции
// При создании номенклатуры на узле:
НовыйОбъект = Справочники.Номенклатура.СоздатьЭлемент();
УИД_из_внешней_базы = ПолучитьGUIDИзСтроки(ID_Внешней_Системы);
// Устанавливаем ссылку заранее
НоваяСсылка = Справочники.Номенклатура.ПолучитьСсылку(УИД_из_внешней_базы);
// Проверяем, нет ли уже такого объекта (вдруг уже загрузили из ЦБ)
НайденныйОбъект = Справочники.Номенклатура.НайтиПоСсылке(НоваяСсылка);
Если НайденныйОбъект.Пустая() Тогда
НовыйОбъект.УстановитьСсылкуНового(НоваяСсылка);
// Заполняем данные...
НовыйОбъект.Записать();
КонецЕсли;
Преимущество: Даже если 300 узлов одновременно создадут один и тот же товар, у него будет один и тот же GUID. При синхронизации в ЦБ они просто "наложатся" друг на друга, и дублей не возникнет.
Проанализируем ситуацию, когда у пользователей есть оперативный доступ к корпоративной сети через VPN или CiscoConnect. В этом случае мы можем организовать "Единую точку ввода" через HTTP-сервис в центральной базе.
Логика работы:
GUID.GUID, который прислала ЦБ.Этот метод гарантирует, что ЦБ всегда является "эталоном", а на узлах данные появляются уже синхронизированными. Рассмотрим пример функции запроса к сервису:
Функция ЗапроситьНоменклатуруВЦентре(АртикулВнешний)
HTTPСоединение = Новый HTTPСоединение("server-cb.company.com", , "user", "pass");
HTTPЗапрос = Новый HTTPЗапрос("/v1/nomenclature/get_or_create?id=" + АртикулВнешний);
Попытка
Ответ = HTTPСоединение.Получить(HTTPЗапрос);
Если Ответ.КодСостояния = 200 Тогда
ДанныеJSON = Ответ.ПолучитьТелоКакСтроку();
// Читаем JSON, получаем ГУИД и создаем объект в ПБ (аналогично Решению 1)
// ...
КонецЕсли;
Исключение
// Обработка ошибки связи: можно либо запретить создание, либо использовать Решение 1
КонецПопытки;
КонецФункции
Поскольку внешняя база доступна через CiscoConnect, мы можем подключить её непосредственно в конфигурацию 1С как ВнешнийИсточникДанных. Это позволит нам работать с таблицами сторонней базы (SQL, Oracle и др.) как с обычными справочниками 1С.
Выясним преимущества этого подхода: мы можем не копировать весь справочник номенклатуры на узлы. Пользователь выбирает товар во внешнем источнике, и только в момент записи документа программа создает (или подбирает) соответствующий элемент в справочнике Номенклатура 1С. При этом код создания должен обязательно использовать механизмы из Решения 1 или Решения 2 для синхронизации GUID.
Если изменить процесс создания на узлах невозможно, придется бороться с последствиями при загрузке данных в ЦБ. В Библиотеке стандартных подсистем (БСП) существуют механизмы сопоставления объектов.
Мы можем переопределить обработчик ПриПолученииДанныхОтПодчиненного в плане обмена. Проанализируем алгоритм:
Номенклатура.Артикул).Важное предупреждение: Этот метод довольно сложен в реализации для РИБ, так как требует тонкой настройки регистрации изменений, чтобы "правильная" ссылка улетела обратно на узел и заменила там "неправильную".
Если дубли уже созданы, их необходимо объединить. Однако в РИБ на 300+ узлов стандартная обработка "Поиск и удаление дублей" может вызвать коллапс системы обмена. Разберем почему:
Когда вы объединяете два элемента, 1С помечает один из них на удаление и переписывает ссылки во всех документах, где он участвовал. В РИБ это приводит к тому, что во все 300 узлов полетят сотни измененных документов. Обмен "распухнет", и база может встать.
Рекомендации по замене дублей:
DataExchangeSelection), чтобы изменения по старым (удаленным) объектам не транслировались на те узлы, где они гарантированно не использовались.Проанализировав все варианты, можно сделать вывод: лучшим решением для РИБ является Решение №1 (Детерминированные GUID). Оно работает автономно, не зависит от качества интернет-соединения в момент ввода данных и гарантирует отсутствие дублей на уровне платформы.
Если же бизнес-процесс требует жесткого контроля параметров (например, цен или остатков из центра), следует комбинировать его с Решением №2 (HTTP-сервисы), используя хеширование как резервный вариант при отсутствии связи.