Столкнулись с ситуацией, когда в многопользовательской SQL-базе 1С:Предприятие 7.7 пользователи не могут программно записать справочник или провести документ? Система выдает сообщение "Объект заблокирован", хотя сам объект никто не открывал в интерактивном режиме. Часто проблема проявляется под одним конкретным пользователем, и временным решением становится перезапуск его сеанса 1С. Давайте вместе разберемся в причинах этой проблемы и найдем надежное решение.
Проанализируем ситуацию: ошибка возникает при программной работе с объектами, база данных работает под управлением MS SQL Server. Это ключевые моменты, которые указывают на то, что проблема, скорее всего, лежит глубже, чем просто открытая пользователем форма.
Для начала важно понять, что в SQL-версии 1С 7.7 мы имеем дело с двумя системами блокировок, работающими одновременно:
*.LCK). Обычно такие блокировки возникают при интерактивной работе и снимаются при закрытии формы.Сообщение "Объект заблокирован" в нашем случае — это чаще всего интерпретация платформой 1С долгого ожидания снятия именно транзакционной блокировки на уровне SQL Server. Один пользователь начал операцию, "захватил" данные, а все остальные ждут, пока он их "отпустит".
Самой вероятной причиной "зависших" блокировок является незавершенная транзакция в коде 1С. Проанализируем, как это происходит. В коде 1С для выполнения группы операций как единого целого используется пара команд: НачатьТранзакцию() и ЗафиксироватьТранзакцию().
Если программист запустил транзакцию командой НачатьТранзакцию(), а до команды ЗафиксироватьТранзакцию() дело не дошло (например, из-за ошибки в коде или сбоя), то эта транзакция "зависает" на сервере SQL. Сессия пользователя в 1С может выглядеть неактивной, но его соединение с SQL Server продолжает удерживать все блокировки, установленные в рамках этой транзакции. Эти блокировки будут удерживаться бесконечно долго, пока сессия не будет принудительно завершена.
Любой другой пользователь, который попытается обратиться к заблокированным данным (например, провести документ, который двигает те же регистры), будет поставлен в очередь ожидания. Для 1С это ожидание выглядит как блокировка объекта.
Почему транзакция может не завершиться?
ОтменитьТранзакцию().Чтобы найти источник проблемы, нам нужно заглянуть "под капот" и посмотреть, что происходит на сервере SQL. Для этого понадобится доступ к MS SQL Server Management Studio (SSMS).
Стандартная команда sp_lock показывает наличие блокировок, но не дает полной картины. Гораздо удобнее использовать системную процедуру sp_who2. Она показывает все активные процессы на сервере.
Выполните в SSMS следующий запрос:
sp_who2
В результате вы увидите таблицу со списком всех сессий. Нас интересует столбец BlkBy (Blocked By). Если у какой-то строки в этом столбце стоит номер (например, "58"), это означает, что данный процесс (сессия) заблокирован процессом с SPID 58. SPID — это уникальный идентификатор сессии, он находится в первом столбце.
Теперь найдите в списке строку, у которой SPID равен 58. Это и есть "виновник" — сессия, которая удерживает блокировку.
После того как мы нашли блокирующую сессию (SPID), нужно понять, кто ее запустил. Посмотрите на ту же строку в выводе sp_who2. Нам помогут столбцы:
HostName — имя компьютера, с которого установлено соединение.LoginName — логин, под которым установлено соединение с SQL.Если пользователи работают не на терминальном сервере, этих данных обычно достаточно, чтобы найти компьютер и пользователя, сеанс которого вызвал проблему.
В терминальной среде все сессии будут исходить от одного сервера. В этом случае для точной идентификации пользователя 1С можно использовать более продвинутые методики, например, создать в SQL специальную таблицу, куда при старте сеанса 1С будет записываться связка "Имя пользователя 1С — SPID соединения".
Когда "виновник" найден и нужно срочно восстановить работу остальных пользователей, можно принудительно завершить его сессию на SQL Server. Для этого используется команда KILL.
Внимание! Используйте эту команду с осторожностью. Она аварийно прервет сессию пользователя, все его незаписанные данные будут потеряны.
KILL 58 -- Где 58 - это SPID блокирующей сессии
После выполнения этой команды SQL Server автоматически отменит незавершенную транзакцию, снимет все блокировки, и другие пользователи смогут продолжить работу. Однако это лишь временное решение. Проблема вернется, если не устранить ее первопричину.
Теперь самое главное — найти в конфигурации 1С место, где транзакция не завершается. Основное правило, которое должно соблюдаться неукоснительно:
Любой вызов НачатьТранзакцию() должен находиться внутри конструкции Попытка ... Исключение ... КонецПопытки.
Правильная структура кода выглядит так:
Процедура ПровестиДокумент()
// ... какая-то подготовительная логика ...
Попытка
НачатьТранзакцию();
// Основной код проведения:
// Движения по регистрам, запись связанных объектов и т.д.
ЗафиксироватьТранзакцию();
Возврат 1; // Успешное завершение
Исключение
ОтменитьТранзакцию();
Сообщить("Не удалось провести документ! " + ОписаниеОшибки());
Возврат 0; // Неуспешное завершение
КонецПопытки;
КонецПроцедуры
В этом примере, если внутри блока между НачатьТранзакцию() и ЗафиксироватьТранзакцию() произойдет любая ошибка, выполнение перейдет в секцию Исключение, где команда ОтменитьТранзакцию() гарантированно отменит транзакцию и освободит все блокировки.
Ваша задача — провести ревизию кода конфигурации. Ищите все вхождения НачатьТранзакцию() и проверяйте, что они обернуты в такую безопасную конструкцию.
Иногда причина незавершенной транзакции не в коде, а во внешней среде. Разрыв соединения между клиентом и сервером может оставить "висячую" сессию на SQL Server.
Если вы уверены в коде, проверьте стабильность сетевого подключения на машине проблемного пользователя и настройки безопасности операционной системы.