При выполнении групповых операций, таких как проведение большого массива документов, разработчики часто сталкиваются с ситуацией, когда стандартная конструкция Попытка — Исключение не спасает от остановки процесса. Вместо того чтобы зайти в блок Исключение, платформа 1С выбрасывает системное модальное окно с текстом «Конфликт блокировок». Пока пользователь не нажмет кнопку «ОК», выполнение кода приостанавливается, что делает невозможным автономную работу обработки, например, в ночное время.
В этой статье мы подробно разберем, почему так происходит, и рассмотрим несколько эффективных стратегий обхода этой проблемы, начиная от изменения архитектуры вызовов и заканчивая использованием внешних инструментов.
Разберем природу возникновения проблемы. В старых версиях платформы (8.1, 8.2) или при использовании конфигураций в режиме автоматических блокировок, управление захватом ресурсов в базе данных полностью делегируется MS SQL Server. Когда возникает ситуация дедлока (Deadlock, ошибка 1205) или превышается таймаут ожидания, SQL Server откатывает транзакцию.
Платформа 1С:Предприятие в «толстом клиенте» или в обычном приложении интерпретирует этот ответ от СУБД как критическое событие. Механизм ядра 1С перехватывает его раньше, чем управление передается интерпретатору встроенного языка. В результате вызывается системный диалог, блокирующий поток выполнения. Чтобы решить эту задачу, нам нужно исключить возможность появления интерактивных окон.
Проанализируем самый надежный и современный метод — перенос логики проведения на сторону сервера через механизм фоновых заданий. Главная особенность фонового задания заключается в том, что у него полностью отсутствует пользовательский интерфейс (UI). Физически некому и нигде показать модальное окно «ОК».
Рассмотрим алгоритм реализации:
ФоновыеЗадания.Выполнить().Посмотрим на пример кода для запуска:
ПараметрыЗадания = Новый Массив;
ПараметрыЗадания.Добавить(МассивСсылок);
ФоновыеЗадания.Выполнить("МойОбщийМодуль.ПровестиДокументыГруппой", ПараметрыЗадания);
В этом случае, если внутри фонового задания возникнет конфликт блокировок SQL, система честно зайдет в блок Исключение. Мы сможем зафиксировать ошибку в журнале регистрации или в специальном логе и продолжить выполнение цикла для следующего документа.
Если конфигурация слишком старая и механизм фоновых заданий недоступен или неудобен, можно использовать внешнее COM-соединение с той же самой базой. Вызывая метод проведения через COMОбъект, мы создаем отдельный сеанс. Если в этом сеансе произойдет ошибка блокировки, она вернется в основной код как программная ошибка COM-объекта, которую уже можно поймать стандартной попыткой.
Выясним, как это выглядит на практике:
// Устанавливаем соединение с текущей базой
Connector = Новый COMОбъект("V83.COMConnector");
Connection = Connector.Connect("Srvr='ServerName';Ref='BaseName';Usr='Admin';Pwd='123'");
Для Каждого ДокСсылка Из СписокДокументов Цикл
Попытка
// Вызываем проведение через COM
Connection.МойОбщийМодуль.ПровестиДокумент(ДокСсылка);
Исключение
ЗаписьЖурналаРегистрации("Ошибка проведения документа: " + ДокСсылка, УровеньЖурналаРегистрации.Ошибка);
КонецПопытки;
КонецЦикла;
Этот метод позволяет изолировать выполнение от интерфейса текущего сеанса и гарантированно избежать модальных окон.
Рассмотрим более радикальный, но правильный с точки зрения архитектуры способ. Конфликты блокировок на уровне SQL чаще всего возникают из-за избыточности: SQL блокирует целые таблицы или диапазоны индексов там, где это не нужно. В режиме управляемых блокировок платформа 1С сама сообщает СУБД, какие записи нужно заблокировать.
Проанализируем преимущества этого подхода:
Попытка — Исключение.Для реализации необходимо в свойствах конфигурации установить «Режим управления блокировками данных» в значение «Управляемый» (или «Смешанный» для постепенного перехода) и прописать программную установку блокировок в модулях проведения документов.
Часто проблема кроется в том, что разработчик пытается провести все 1000 документов в одной транзакции. Выясним, почему это плохо: чем дольше длится транзакция, тем больше ресурсов она удерживает. Вероятность того, что другой пользователь «столкнется» с вашей обработкой, растет в геометрической прогрессии.
Разберем правильную структуру цикла проведения:
Для Каждого ДокументОбъект Из Выборка Цикл
// Каждый документ проводится в своей транзакции
Попытка
ДокументОбъект.Записать(РежимЗаписиДокумента.Проведение);
Исключение
// Здесь мы ловим ошибки, если они не заблокированы ядром
Сообщить("Документ " + ДокументОбъект + " не проведен.");
КонецПопытки;
КонецЦикла;
Важно: не используйте НачатьТранзакцию() перед циклом. Дайте платформе завершать транзакцию после каждого документа. Это значительно снизит нагрузку на MS SQL и уменьшит время удержания блокировок.
Если у вас нет возможности менять код или архитектуру, а обработку нужно запускать «здесь и сейчас», можно прибегнуть к помощи внешних утилит автоматизации, таких как AutoIt. Это скриптовый язык, который позволяет отслеживать появление окон в Windows по их заголовку и имитировать нажатие клавиш.
Пример логики скрипта AutoIt:
Enter.Это «костыльное» решение, но оно эффективно в случаях, когда вмешательство в конфигурацию полностью запрещено регламентами организации.
Напоследок разберем настройки СУБД, которые помогут снизить частоту появления таких ошибок:
Подведем итог: наиболее стабильным решением является использование фоновых заданий. Это полностью избавляет систему от интерактивности и позволяет коду 1С корректно отрабатывать любые исключения, возникающие на уровне SQL-сервера.