При попытке ускорить обработку задач бизнес-процессов путем распараллеливания фоновых заданий разработчики часто сталкиваются с ошибкой «Конфликт блокировок». Рассмотрим, почему это происходит, даже если мы используем управляемые блокировки, и как с этим работать в высоконагруженных системах.
Основная проблема заключается в том, что метод ВыполнитьЗадачу() является атомарным для бизнес-логики, но крайне тяжелым для СУБД. При его вызове происходит не просто запись значений реквизитов, а цепочка сложных системных операций:
INSERT в БД).ByProcPointExec, которые критически важны для работы подсистемы бизнес-процессов.Когда несколько фоновых заданий одновременно выполняют ВыполнитьЗадачу(), СУБД начинает конфликтовать за ресурсы индексов. Даже при использовании БлокировкаДанных, SQL Server может инициировать эскалацию блокировок, превращая блокировку конкретной строки в блокировку всей страницы или даже всей таблицы индекса, что приводит к остановке остальных процессов.
Давайте проанализируем код, который вызывает ошибку, и поймем, где именно возникает «узкое горлышко».
Процедура ОбработатьМассивЗадачКонтроляОплаты(ПакетЗадач) Экспорт
// Инициализация данных
Для каждого ТекЗадача Из ПакетЗадач Цикл
НачатьТранзакцию(РежимУправленияБлокировкойДанных.Управляемый);
Блокировка = Новый БлокировкаДанных;
Элемент = Блокировка.Добавить("Задача.ксбЗадачиКонтроляОплаты");
Элемент.УстановитьЗначение("Ссылка", ТекЗадача.Ключ);
Блокировка.Заблокировать();
ЗадачаОбъект = ТекЗадача.Ключ.ПолучитьОбъект();
// Заполнение реквизитов
ЗадачаОбъект.Записать();
// Выполнение задачи (вызывает тяжелые операции)
ЗадачаОбъект.ВыполнитьЗадачу();
ЗафиксироватьТранзакцию();
КонецЦикла;
КонецПроцедуры
В данном примере, несмотря на явное использование БлокировкаДанных, метод ВыполнитьЗадачу() запускает скрытые механизмы, которые не учитывают наши ручные блокировки. Если выполнение задачи приводит к созданию новой задачи, платформа выполняет вставку в таблицу, что провоцирует конкуренцию за страницы индекса.
Чтобы избежать конфликтов, рекомендуем придерживаться следующих стратегий оптимизации:
Если логика позволяет, разнесите заполнение данных и само выполнение задачи в разные операции. Заполнение реквизитов — это безопасная операция, которая редко вызывает конкуренцию. Выполнение задачи, которое меняет статус и создает новые записи, лучше выполнять последовательно или малыми порциями.
Вместо того чтобы отдавать весь массив задач на одновременную обработку, реализуйте механизм очереди. Фоновые задания могут работать с общим регистром сведений, в котором статус задачи меняется с «К обработке» на «В обработке». Это позволит контролировать количество активных транзакций, обращающихся к системным таблицам бизнес-процессов.
Проверьте, что в свойствах конфигурации установлен режим «Управляемый». Если у вас режим «Автоматический и управляемый», убедитесь, что все задействованные метаданные (документы, задачи, бизнес-процессы) переведены в явный управляемый режим. Использование даже одного объекта в автоматическом режиме может привести к эскалации блокировок до уровня всей таблицы.
Если профайлер показывает конфликт в индексе ByProcPointExec, это прямой сигнал о том, что система «захлебывается» при массовой вставке записей в бизнес-процессы. Попробуйте:
НачатьТранзакцию выполнялся максимально быстро.Помните, что платформа 8.3 значительно лучше справляется с многопоточностью, однако механизмы бизнес-процессов по своей архитектуре требуют строгого порядка обработки. Если вы видите, что конфликт происходит на уровне платформенных механизмов, лучший способ — последовательное выполнение тех операций, которые меняют состояние бизнес-процесса.