При интеграции 1С с внешними системами разработчики часто сталкиваются с ситуацией, когда один и тот же объект (например, сотрудник или физическое лицо) дублируется при вызове веб-сервиса. Рассмотрим подробнее причину возникновения этой проблемы и разберем практические способы её устранения.
Проанализируем классическую схему загрузки сотрудников через веб-сервис. Алгоритм, как правило, выглядит следующим образом:
НайтиПоКоду().НайтиПоРеквизиту().ФизическиеЛица.Сотрудники.Рассмотрим пример кода обработки поступающего пакета данных:
ТаблицаСотрудника = СоздатьТаблицуДанныеСотрудника;
// Заполнение таблицы данных из XDTO-пакета
ФИО = ТаблицаСотрудника[0].Фамилия + " " + ТаблицаСотрудника[0].Имя + " " + ТаблицаСотрудника[0].Отчество;
Сотрудник = Справочники.Сотрудники.НайтиПоКоду(ТаблицаСотрудника[0].ТабельныйНомер);
Если ЗначениеЗаполнено(Сотрудник) Тогда
ФизЛицо = Сотрудник.ФизическоеЛицо;
Иначе
ФизЛицо = Справочники.ФизическиеЛица.НайтиПоРеквизиту("СтраховойНомерПФР", ТаблицаСотрудника[0].СНИЛС);
КонецЕсли;
Если НЕ ЗначениеЗаполнено(ФизЛицо) Тогда
НовоеФизЛицо = Справочники.ФизическиеЛица.СоздатьЭлемент();
// Заполнение реквизитов физического лица
НовоеФизЛицо.Записать();
ФизЛицо = НовоеФизЛицо.Ссылка;
Иначе
// Обновление реквизитов физического лица при необходимости
КонецЕсли;
Если НЕ ЗначениеЗаполнено(Сотрудник) Тогда
НовыйСотрудник = Справочники.Сотрудники.СоздатьЭлемент();
НовыйСотрудник.Код = ТаблицаСотрудника[0].ТабельныйНомер;
// Заполнение прочих реквизитов сотрудника
НовыйСотрудник.Записать();
КонецЕсли;
Возврат "0";
Выясним причину, почему при работе данного кода в базе могут появляться дубли. Пул сеансов веб-сервисов в платформе 1С работает многопоточно. Если внешняя система отправляет повторные запросы (например, из-за сетевых таймаутов или настройки повторной отправки) либо передает пакеты с высокой частотой, сервер 1С начинает обрабатывать их параллельно в разных сеансах.
Каждая запись элемента (вызов метода Записать()) выполняется в отдельной транзакции. В рамках одной секунды сервер успевает выполнить множество операций. Когда два параллельных потока одновременно выполняют поиск по коду или СНИЛС, они оба получают результат «Не найдено», так как первая транзакция еще не успела завершиться и зафиксировать измененные данные в базе. В результате оба сеанса переходят к созданию нового объекта, создавая дубли с одинаковым кодом в разных транзакциях.
Для предотвращения состояния гонки (Race Condition) разберем установку управляемой блокировки. Оборачиваем процедуру поиска и записи в явную транзакцию и устанавливаем управляемую блокировку перед поиском объектов.
В этом случае второй поток при попытке обращения к ключу блокировки будет ожидать завершения транзакции первого потока. После снятия блокировки второй поток выполнит поиск и найдет уже записанный элемент.
Посмотрим на решение через контроль входящих сообщений. Реализуем регистр сведений, в котором фиксируются уникальные идентификаторы пакетов (GUID) или ID сообщений внешней системы.
Рассмотрим ещё один архитектурный подход. Вместо прямого создания физических лиц и сотрудников непосредственно внутри веб-сервиса организуем очередь сообщений:
Этот подход полностью исключает параллельное создание одинаковых элементов и существенно снижает время отклика веб-сервиса.
Для детального анализа поведения системы выполним отладку:
Сотрудники и перейдем в обработчик ПередЗаписью.