Почему дублируются данные при загрузке через веб-сервис в 1С и как это исправить?

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

При интеграции 1С с внешними системами разработчики часто сталкиваются с ситуацией, когда один и тот же объект (например, сотрудник или физическое лицо) дублируется при вызове веб-сервиса. Рассмотрим подробнее причину возникновения этой проблемы и разберем практические способы её устранения.

Анализ проблемы и причин дублирования

Проанализируем классическую схему загрузки сотрудников через веб-сервис. Алгоритм, как правило, выглядит следующим образом:

  1. Получение сообщения из внешней системы через XDTO-пакет.
  2. Поиск сотрудника по табельному номеру с помощью НайтиПоКоду().
  3. Поиск физического лица по СНИЛС с помощью НайтиПоРеквизиту().
  4. Если физическое лицо не найдено — создание нового элемента справочника ФизическиеЛица.
  5. Если сотрудник не найден — создание нового элемента справочника Сотрудники.

Рассмотрим пример кода обработки поступающего пакета данных:


ТаблицаСотрудника = СоздатьТаблицуДанныеСотрудника;
// Заполнение таблицы данных из XDTO-пакета

ФИО = ТаблицаСотрудника[0].Фамилия + " " + ТаблицаСотрудника[0].Имя + " " + ТаблицаСотрудника[0].Отчество;
Сотрудник = Справочники.Сотрудники.НайтиПоКоду(ТаблицаСотрудника[0].ТабельныйНомер);

Если ЗначениеЗаполнено(Сотрудник) Тогда
    ФизЛицо = Сотрудник.ФизическоеЛицо;
Иначе
    ФизЛицо = Справочники.ФизическиеЛица.НайтиПоРеквизиту("СтраховойНомерПФР", ТаблицаСотрудника[0].СНИЛС);
КонецЕсли;

Если НЕ ЗначениеЗаполнено(ФизЛицо) Тогда
    НовоеФизЛицо = Справочники.ФизическиеЛица.СоздатьЭлемент();
    // Заполнение реквизитов физического лица
    НовоеФизЛицо.Записать();
    ФизЛицо = НовоеФизЛицо.Ссылка;
Иначе
    // Обновление реквизитов физического лица при необходимости
КонецЕсли;

Если НЕ ЗначениеЗаполнено(Сотрудник) Тогда
    НовыйСотрудник = Справочники.Сотрудники.СоздатьЭлемент();
    НовыйСотрудник.Код = ТаблицаСотрудника[0].ТабельныйНомер;
    // Заполнение прочих реквизитов сотрудника
    НовыйСотрудник.Записать();
КонецЕсли;
Возврат "0";

Выясним причину, почему при работе данного кода в базе могут появляться дубли. Пул сеансов веб-сервисов в платформе 1С работает многопоточно. Если внешняя система отправляет повторные запросы (например, из-за сетевых таймаутов или настройки повторной отправки) либо передает пакеты с высокой частотой, сервер 1С начинает обрабатывать их параллельно в разных сеансах.

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

Способ 1. Использование управляемых блокировок данных

Для предотвращения состояния гонки (Race Condition) разберем установку управляемой блокировки. Оборачиваем процедуру поиска и записи в явную транзакцию и устанавливаем управляемую блокировку перед поиском объектов.

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

Способ 2. Проверка идемпотентности входящих запросов

Посмотрим на решение через контроль входящих сообщений. Реализуем регистр сведений, в котором фиксируются уникальные идентификаторы пакетов (GUID) или ID сообщений внешней системы.

  1. При вызове веб-сервиса проверяем наличие идентификатора запроса в регистре.
  2. Если запрос с таким ID уже обрабатывается или был обработан ранее, веб-сервис сразу возвращает результат без повторной обработки.
  3. Если запрос новый — записываем его ID в регистр и переходим к стандартной обработке.

Способ 3. Асинхронная обработка через очередь сообщений

Рассмотрим ещё один архитектурный подход. Вместо прямого создания физических лиц и сотрудников непосредственно внутри веб-сервиса организуем очередь сообщений:

  1. Веб-сервис принимает XDTO-пакет, записывает его в регистр очереди и незамедлительно возвращает ответ внешней системе.
  2. Отдельное регламентное или фоновое задание последовательно в один поток обрабатывает записи из очереди.

Этот подход полностью исключает параллельное создание одинаковых элементов и существенно снижает время отклика веб-сервиса.

Диагностика и отладка

Для детального анализа поведения системы выполним отладку:

  1. Откроем модуль справочника Сотрудники и перейдем в обработчик ПередЗаписью.
  2. Установим точку останова и вызовем веб-сервис из внешней системы.
  3. При срабатывании точки останова проанализируем стек вызовов для каждого сеанса и зафиксируем время записи объектов в Журнале регистрации.
← На главную