Как исправить ошибку «Объект уже заблокирован» при работе с бизнес-процессами и задачами через веб-сервер в 1С?

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

При интеграции сторонних систем с конфигурацией «1С:Документооборот» (или другими решениями на платформе 1С) через веб- или HTTP-сервисы разработчики часто сталкиваются с неприятной проблемой. При попытке изменить бизнес-процесс или связанные с ним задачи возникает критическая ошибка: «Ошибка блокировки объекта. Объект уже заблокирован». При этом стандартный метод проверки блокировки ведет себя непредсказуемо, показывая, что объект свободен.

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

Анализ поведения методов Заблокирован() и Заблокировать()

Выясним, как работают базовые механизмы платформы 1С для контроля пессимистических блокировок объектов базы данных. В платформе предусмотрено два основных метода:

  1. Метод Заблокирован(). Предназначен исключительно для проверки того, заблокирован ли конкретный объект базы данных текущим сеансом. Если объект заблокирован другим сеансом (даже если это сеанс того же пользователя) или вообще не заблокирован, метод всегда возвращает Ложь. Он не предоставляет возможность проверить, заблокирован ли объект в базе данных в принципе другими пользователями.
  2. Метод Заблокировать(). Пытается наложить пессимистическую блокировку на объект от имени текущего сеанса. Если объект уже заблокирован другим сеансом, платформа мгновенно вызывает исключение, которое прерывает выполнение кода, если оно не обработано.

Проанализируем пример стандартного кода, который часто приводит к ошибкам при интеграции:


Если Не СсылкаНаОбъект.Заблокирован() Тогда
    // Внимание: Заблокирован() вернул Ложь, так как текущий сеанс блокировку не накладывал.
    // Но объект может быть заблокирован другим веб-сеансом!
    НовыйОбъект = СсылкаНаОбъект.ПолучитьОбъект();
    НовыйОбъект.Заблокировать(); // Здесь падает критическая ошибка
КонецЕсли;

Если этот код выполняется во втором последовательном запросе к веб-сервису, метод Заблокирован() вернет Ложь, так как текущий веб-сеанс еще не блокировал объект. Однако при вызове метода Заблокировать() система выдаст ошибку, поскольку объект удерживается сеансом из предыдущего запроса.

Почему возникает иллюзия «одного пользователя» при пустой консоли сеансов?

Рассмотрим классическую ситуацию, описанную разработчиками: веб-сервис сообщает, что объект заблокирован «другим соединением», но при проверке консоли администрирования кластера 1С никаких сторонних активных сеансов не видно. Причина кроется в особенностях архитектуры работы веб-сервера (IIS или Apache) с платформой 1С:

Специфика бизнес-процессов и задач в «1С:Документообороте»

В типовых решениях на базе Библиотеки Стандартных Подсистем (БСП), особенно в «1С:Документообороте», бизнес-процессы и задачи тесно связаны на уровне метаданных. При блокировке бизнес-процесса система автоматически может накладывать блокировки на все связанные с ним задачи через подписки на события перед записью.

Если внешний веб-сервис пытается сначала заблокировать бизнес-процесс, а затем поочередно блокировать каждую его задачу, возникает ситуация самоблокировки (deadlock) из-за пересечения контекстов выполнения в разных сеансах пула веб-сервера.

Решение проблемы через «Попытку — Исключение» (Быстрый обход ошибки)

Самый простой способ избежать падения кода при блокировке — обернуть вызов метода Заблокировать() в конструкцию перехвата ошибок. Разберем пример реализации такого обхода:


ОбъектЗадачи = СсылкаНаЗадачу.ПолучитьОбъект();
Попытка
    ОбъектЗадачи.Заблокировать();
    // Производим изменения объекта
    ОбъектЗадачи.Реквизит = "Значение";
    ОбъектЗадачи.Записать();
Исключение
    // Сюда код попадет, если объект уже заблокирован в другом сеансе веб-сервиса
    ЗаписьЖурналаРегистрации("Интеграция.Блокировка", 
        УровеньЖурналаРегистрации.Предупреждение, , , 
        "Не удалось заблокировать задачу, возможно она уже заблокирована бизнес-процессом: " + ОписаниеОшибки());
КонецПопытки;

Важно: Хотя этот способ решает проблему аварийного завершения веб-сервиса, использовать его как постоянное архитектурное решение крайне опасно. Это «костыль», который маскирует проблему. Если объект действительно редактируется реальным пользователем в этот момент в тонком клиенте, ваш веб-сервис проигнорирует блокировку, запишет данные поверх и затрет изменения пользователя, либо вызовет ошибку оптимистической блокировки версий при вызове метода Записать().

Архитектурно правильные способы решения проблемы

Разберем системные подходы, которые позволяют полностью исключить возникновение конфликтов блокировок при интеграции через веб-сервер.

Способ 1. Настройка пула сеансов и сохранение Cookie (Session Pooling)

Чтобы все запросы от одного внешнего клиента гарантированно обрабатывались в рамках одного и того же сеанса 1С, необходимо настроить веб-сервер и внешнюю систему:

  1. В файле публикации веб-сервиса default.vrd настройте параметры пула веб-сервисов, установив оптимальное время жизни сеансов (параметры poolSize и poolTimeout).
  2. Обеспечьте поддержку Cookie со стороны внешнего приложения. При первом успешном запросе веб-сервер 1С возвращает HTTP-заголовок Set-Cookie: ibsession=.... Внешняя система должна сохранять это значение и передавать его обратно во всех последующих запросах. В этом случае все вызовы будут выполняться строго внутри единого сеанса 1С, и повторные вызовы методов блокировки внутри одной сессии не будут вызывать конфликтов.

Способ 2. Отказ от пессимистических блокировок в API

Метод Заблокировать() разработан для интерактивной работы пользователей во избежание одновременного редактирования одной формы на экране. В веб-сервисах и API интерактивность отсутствует. В таких сценариях правильнее использовать механизм коротких транзакций и управляемые транзакции:

Посмотрим на пример использования управляемой блокировки:


НачатьТранзакцию();
Попытка
    Блокировка = Новый БлокировкаДанных;
    ЭлементБлокировки = Блокировка.Добавить("Справочник.ЗадачиИсполнителя");
    ЭлементБлокировки.УстановитьЗначение("Ссылка", СсылкаНаЗадачу);
    Блокировка.Заблокировать();
    
    // Безопасное чтение и изменение данных внутри транзакции
    ОбъектЗадачи = СсылкаНаЗадачу.ПолучитьОбъект();
    ОбъектЗадачи.Выполнена = Истина;
    ОбъектЗадачи.Записать();
    
    ЗафиксироватьТранзакцию();
Исключение
    ОтменитьТранзакцию();
    ВызватьИсключение; // Передаем ошибку вызывающей системе
КонецПопытки;

Способ 3. Использование метода ЗаблокироватьДанныеДляРедактирования()

Если вам все же необходимо заблокировать данные от изменений другими пользователями на длительное время выполнения процесса интеграции, используйте специализированный метод глобального контекста:

ЗаблокироватьДанныеДляРедактирования(Ссылка, , ИдентификаторФормы)

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

Применяя эти подходы, мы сможем построить надежную и отказоустойчивую интеграцию с «1С:Документооборотом» без риска потери данных и непредвиденных взаимных блокировок сеансов. Для отладки и проверки интеграционных запросов подойдёт инструмент для тестирования REST API на платформе 1С.

← На главную