Правило
Заголовок раздела «Правило»Обработчик приёма данных обязан быть идемпотентным: повторная обработка того же сообщения не должна создавать дубли объектов или повторно применять эффекты.
- Ключ идемпотентности хранится в ИБ и ищется до создания — внешний идентификатор сообщения/объекта в индексируемом реквизите или в регистре сведений «Соответствие внешних ключей» (измерение — внешний ключ, ресурс — ссылка).
- Ключ выбирается устойчивый. Если источник даёт идентификатор
(
ИдСообщения, GUID чека) — используется он. Если нет (периодическая выгрузка без идентификаторов) — ключ составной и естественный: период + источник (касса, подразделение, организация). - При параллельной загрузке поиск и создание — под блокировкой по ключу (см. ниже), иначе два потока создадут два объекта.
Каналы доставки допускают повторную доставку: ретраи отправителя, обрыв связи после записи, но до подтверждения, повторный запуск обмена вручную. Без идемпотентности каждый такой повтор — дубль документа и расхождение учёта, которое разбирают руками.
Параллельная загрузка: блокировка по ключу
Заголовок раздела «Параллельная загрузка: блокировка по ключу»При параллельной загрузке (несколько потоков/фоновых заданий/инстансов отправителя) пара «найти-или-создать» сама по себе не защищает от гонки: два потока одновременно не находят объект и создают два. Поиск и запись — под управляемой блокировкой по ключу идемпотентности:
Блокировка = Новый БлокировкаДанных;Элемент = Блокировка.Добавить("РегистрСведений.СоответствиеВнешнихКлючей");Элемент.УстановитьЗначение("ВнешнийКлюч", ВнешнийКлюч);НачатьТранзакцию();Попытка Блокировка.Заблокировать(); // второй поток ждёт здесь, потом находит объект Ссылка = НайтиПоВнешнемуКлючу(ВнешнийКлюч); Если Ссылка = Неопределено Тогда Ссылка = СоздатьДокумент(ДанныеСообщения, ВнешнийКлюч); КонецЕсли; ЗафиксироватьТранзакцию();Исключение ОтменитьТранзакцию(); ВызватьИсключение;КонецПопытки;Возврат Ссылка;Блокировка ставится на ключ, а не на всю таблицу — параллельная загрузка разных сообщений не сериализуется. Транзакция при этом остаётся короткой: внутри только поиск и запись, без внешних вызовов.
Схема обработки
Заголовок раздела «Схема обработки»flowchart TD
A[Сообщение получено] --> B{Найден объект по ИдСообщения?}
B -- Да --> C[Получить существующий объект]
B -- Нет --> D[Создать новый объект]
C --> E[Заполнить и записать]
D --> E
E --> F[Дублей нет]
Пример из практики
Заголовок раздела «Пример из практики»Два реальных случая, где видны обе половины правила:
- Загрузка чеков маркетплейса. Требование пришло от самого отправителя: «важна идемпотентность создания объектов, так как одновременно может быть несколько инстансов». Один и тот же чек физически приходит несколько раз — ретраи, параллельные инстансы, переотправка после таймаута. Ключ — внешний идентификатор чека, поиск и создание — под блокировкой по ключу (шаблон выше), иначе два инстанса создают два документа.
- Загрузка продаж из ДодоИс. У ежедневной выгрузки нет идентификатора сообщения, и повторный запуск за тот же день задваивал «Отчёты о розничных продажах». Ключ здесь составной и естественный — период + касса/подразделение: повторный запуск находит и обновляет существующий ОРП. Разбор всего конвейера — в сквозном примере.
Когда не применять
Заголовок раздела «Когда не применять»Чисто читающие обработчики (отдают данные, ничего не записывают) идемпотентны по построению — дополнительный ключ не нужен. Для всего, что создаёт или изменяет данные, исключений нет: даже «одноразовая» загрузка когда-нибудь будет запущена повторно.
См. также
Заголовок раздела «См. также»- Короткие транзакции — обработчик приёма должен укладываться в короткую транзакцию.
- Не выполнять однотипные запросы в цикле — поиск по идентификатору сообщения для пачки данных делайте одним запросом.