Встречались ли вы с ситуацией, когда регламентное задание, которое должно выполняться в единственном экземпляре, внезапно запускается почти одновременно в разных сеансах? Это приводит к созданию дублирующихся документов, бизнес-процессов или записей в регистрах (поможет универсальный поиск и удаление дублей в 1С), нарушая целостность данных. Особенно часто такая проблема проявляется в клиент-серверных базах с кластером серверов 1С, даже если задание жестко привязано к конкретному серверу.
Проанализируем типичный сценарий: регламентное задание по обработке бизнес-событий в «1С:Документооборот» (подобное тому, как реализован дополнительный обработчик для бизнес-события "Поступление входящей почты") изредка создает два идентичных бизнес-процесса по одному и тому же документу с разницей в долю секунды. Журнал регистрации показывает, что запуски произошли из разных сеансов, хотя и на одном сервере. Разберем, почему это происходит и как этого избежать, используя системные механизмы платформы 1С. Для контроля работы удобно использовать автоматический мониторинг выполнения заданий с уведомлением в Telegram.
Корень проблемы часто кроется в так называемой "гонке состояний" (race condition). Это ситуация, когда результат работы кода зависит от того, какой из параллельно выполняющихся потоков (в нашем случае — сеансов) успеет выполнить операцию первым. Часто такие ситуации становятся критическими факторами, влияющими на то, насколько быстро работает наша система, и где находится "узкое место".
Рассмотрим код, который проверяет наличие активного бизнес-процесса перед его созданием. Логика кажется правильной: сначала проверяем, потом создаем.
// ... фрагмент кода для проверки существующего БП
Запрос = Новый Запрос;
Запрос.Текст = "ВЫБРАТЬ
| КомплексныйПроцессПредметы.Ссылка КАК Ссылка
|ИЗ
| БизнесПроцесс.КомплексныйПроцесс.Предметы КАК КомплексныйПроцессПредметы
|ГДЕ
| КомплексныйПроцессПредметы.Предмет = &Предмет
| И КомплексныйПроцессПредметы.Ссылка.Завершен = Ложь
| И КомплексныйПроцессПредметы.Ссылка.ПометкаУдаления = Ложь
| И КомплексныйПроцессПредметы.Ссылка.Стартован
| И КомплексныйПроцессПредметы.Ссылка.Состояние = ЗНАЧЕНИЕ(Перечисление.СостоянияБизнесПроцессов.Активен)
| И КомплексныйПроцессПредметы.Ссылка.Шаблон = &Шаблон";
Запрос.УстановитьПараметр("Предмет", Событие.Источник);
Запрос.УстановитьПараметр("Шаблон", СтрокаТаблицы.ШаблонБизнесПроцесса);
РезультатПроверки = Запрос.Выполнить();
Если РезультатПроверки.Пустой() Тогда
// Бизнес-процесс не найден, создаем новый...
КонецЕсли;
А теперь представим, что планировщик заданий по какой-то причине запустил два экземпляра нашего регламентного задания одновременно в Сеансе 1 и Сеансе 2.
В результате мы получаем два абсолютно одинаковых бизнес-процесса. Подобные логические ошибки встречаются и в других механизмах, например, когда требуется исправление дублирования роли файла при создании подзадачи в 1С:Документооборот. Проблема в том, что операции проверки и создания не являются атомарными, то есть не выполняются как единое целое.
Самый простой и эффективный способ предотвратить параллельный запуск одного и того же регламентного задания — это указать для него уникальный Ключ. Ключ — это уникальная строка, которая служит идентификатором для группы заданий. Планировщик заданий 1С устроен так, что он не будет запускать новое фоновое задание, если уже выполняется другое задание с таким же ключом.
Как это сделать:
Общие -> Регламентные задания — для этого подойдёт инструментарий для управления и отладки регламентных заданий."ОбработкаБизнесСобытийДокументооборот".После этого планировщик будет игнорировать повторные попытки запуска. Однако стоит изучить некоторые неочевидные особенности фоновых заданий и свойства Ключ, чтобы понимать все нюансы этого механизма на уровне платформы.
Установка ключа решает проблему с параллельным запуском самого задания. Однако для создания надежного кода необходимо использовать механизм управляемых транзакционных блокировок. Это позволяет анализировать SQL сервер глазами 1С-ника и видеть, как система управляет ожиданиями и блокировками на уровне СУБД, предотвращая коллизии.
Идея состоит в том, чтобы заблокировать данные, связанные с предметом, на время проверки и создания бизнес-процесса. Пока один сеанс держит блокировку, другой сеанс будет ждать ее освобождения.
Пример кода с управляемой блокировкой:
НачатьТранзакцию();
Попытка
// 1. Создаем объект блокировки
Блокировка = Новый БлокировкаДанных;
// 2. Указываем, какие данные будем блокировать.
ЭлементБлокировки = Блокировка.Добавить("Справочник.ВнутренниеДокументы");
// 3. Устанавливаем исключительный режим.
ЭлементБлокировки.Режим = РежимБлокировкиДанных.Исключительный;
// 4. Указываем конкретный элемент, который блокируем
ЭлементБлокировки.УстановитьЗначение("Ссылка", Событие.Источник);
// 5. Выполняем блокировку.
Блокировка.Заблокировать();
// 6. Теперь, когда данные надежно заблокированы, выполняем нашу проверку
Запрос = Новый Запрос;
// ... текст запроса, как в примере выше ...
РезультатПроверки = Запрос.Выполнить();
Если РезультатПроверки.Пустой() Тогда
// 7. Если БП не найден, создаем его внутри той же транзакции
НовыйБП = БизнесПроцессы.КомплексныйПроцесс.СоздатьБизнесПроцесс();
// ... заполняем реквизиты ...
НовыйБП.Предметы.Добавить().Предмет = Событие.Источник;
НовыйБП.Записать();
НовыйБП.Стартовать();
КонецЕсли;
// 8. Если все прошло успешно, подтверждаем все изменения
ЗафиксироватьТранзакцию();
Исключение
// 9. Если произошла ошибка, отменяем все изменения
ОтменитьТранзакцию();
ВызватьИсключение;
КонецПопытки;
Теперь, если два сеанса одновременно дойдут до этого участка кода, второй сеанс на строке Блокировка.Заблокировать() остановится и будет ждать, пока первый сеанс завершит транзакцию, что полностью исключает дублирование.
В исходной проблеме упоминалось, что задание было вынесено на отдельный сервер кластера. Это правильный подход, если вы ускоряете 1С:Документооборот КОРП и распределяете нагрузку. Однако стоит убедиться, что настройки правил назначения функциональности не содержат конфликтов.
Для глубокой диагностики можно использовать Технологический журнал (ТЖ), отслеживая события SDBL и JOB. Это поможет выявить аномалии в работе планировщика и понять причины избыточных запусков.
Для надежного решения проблемы дублирования данных рекомендуется применять комплексный подход:
Комбинация этих методов обеспечивает многоуровневую защиту и стабильную работу системы даже при высоких нагрузках.