При настройке ночных регламентных заданий — например, резервного копирования и последующего группового перепроведения документов в 1С:Предприятии 7.7 — администраторы часто сталкиваются с ошибкой ALTER DATABASE failed because a lock could not be placed on database. Эта ситуация возникает, когда платформа 1С пытается инициализировать сеанс в момент, когда база данных SQL Server всё ещё заблокирована другим процессом или находится в монопольном режиме SINGLE_USER.
Разберем причины возникновения этой ошибки, а также по шагам рассмотрим методы программной проверки готовности базы к многопользовательской работе с помощью SQL-DMO, ADO и системных функций SQL Server.
Проанализируем механику старта платформы. При каждом подключении к базе данных Microsoft SQL Server клиентская часть 1С 7.7 выполняет служебные проверки параметров базы данных и пытается применить системную инструкцию ALTER DATABASE (например, для проверки параметров автоматического обновления статистики или параметров блокировок).
Для выполнения команды ALTER DATABASE СУБД требует установки монопольной блокировки на уровне метаданных всей базы (блокировка типа LCK_M_X). Если в этот момент база находится в режиме SINGLE_USER и занята другим процессом (например, выгрузкой) либо в ней остаются незавершенные транзакции или активные клиентские подключения, попытка заблокировать базу завершается ошибкой, а запуск 1С зависает в сеансе.
Объектная модель SQL-DMO (SQL Distributed Management Objects) содержит специальное свойство для определения монопольного режима базы данных. У объекта Database доступно булево свойство SingleUser.
Посмотрим на пример кода на VBScript, проверяющего список баз:
Set oServer = CreateObject("SQLDmo.SqlServer")
oServer.LoginSecure = True
oServer.Connect "localhost"
For Each oDatabase In oServer.Databases
If Not oDatabase.SystemObject Then
If oDatabase.SingleUser Then
WScript.Echo "База " & oDatabase.Name & " находится в режиме SINGLE_USER!"
Else
WScript.Echo "База " & oDatabase.Name & " доступна в режиме MULTI_USER."
End If
End If
Next
oServer.DisConnect
Set oServer = Nothing
Важное ограничение: если база данных уже занята другим монопольным процессом, обращение к некоторым свойствам объекта oDatabase через SQL-DMO может вызвать исключение перехвата контекста СУБД. Поэтому рассмотрим более надежные подходы.
Выясним, как использовать системную процедуру sp_helpdb через метод ExecuteWithResults объекта SQL-DMO. Если база занята и недоступна для чтения метаданных, процедура возвращает пустой результат, что позволяет скрипту сделать паузу и отложить запуск 1С.
Function dbIsBusy(psBdName)
dbIsBusy = False
On Error Resume Next
Set oServer = CreateObject("SQLDmo.SqlServer")
oServer.LoginSecure = True
oServer.Connect "(local)"
Set oResult = oServer.ExecuteWithResults("sp_helpdb '" & psBdName & "'")
If Err.Number <> 0 Or oResult.Rows = 0 Then
dbIsBusy = True
End If
oServer.DisConnect
Set oServer = Nothing
On Error GoTo 0
End Function
Наиболее надежный способ, исключающий взаимные блокировки при опросе состояния, — подключение через ADODB.Connection к системной базе master с вызовом скалярной функции DATABASEPROPERTYEX. Данная функция считывает свойства из системного каталога уровня сервера, не обращаясь к самой базе и не пытаясь захватить в ней блокировку.
Разберем реализацию функции на VBScript:
Function GetDatabaseUserAccess(dbName)
Dim conn, rs, connStr, sql
connStr = "Provider=SQLOLEDB;Data Source=(local);Initial Catalog=master;Integrated Security=SSPI;"
Set conn = CreateObject("ADODB.Connection")
conn.Open connStr
sql = "SELECT CAST(DATABASEPROPERTYEX('" & dbName & "', 'UserAccess') AS VARCHAR(50)) AS AccessMode, " & _
" CAST(DATABASEPROPERTYEX('" & dbName & "', 'Status') AS VARCHAR(50)) AS DbStatus"
Set rs = conn.Execute(sql)
If Not rs.EOF Then
GetDatabaseUserAccess = rs("AccessMode")
Else
GetDatabaseUserAccess = "UNKNOWN"
End If
rs.Close
conn.Close
Set rs = Nothing
Set conn = Nothing
End Function
' Пример использования в цикле ожидания
Dim status
status = GetDatabaseUserAccess("ERP_TEST")
If status = "MULTI_USER" Then
WScript.Echo "База готова к работе. Запускаем пакетное перепроведение..."
Else
WScript.Echo "База занята режимом: " & status & ". Ожидаем освобождения."
End If
Функция DATABASEPROPERTYEX('ИмяБазы', 'UserAccess') возвращает следующие значения:
MULTI_USER — база доступна для всех пользователей (нормальный режим для запуска 1С);SINGLE_USER — монопольный режим, доступ только для одного подключения;RESTRICTED_USER — доступ разрешен только членам ролей db_owner, dbcreator или sysadmin.Если резервное копирование завершено, но предыдущий сеанс или скрипт оставил соединение активным, перед стартом 1С можно выполнить принудительный сброс режима с завершением всех мешающих процессов:
ALTER DATABASE ERP_TEST
SET MULTI_USER
WITH ROLLBACK IMMEDIATE;
Параметр WITH ROLLBACK IMMEDIATE немедленно откатывает все незавершенные сторонние транзакции и разрывает активные подключения, переводя базу в многопользовательский режим без ожидания их естественного закрытия.
Чтобы исключить возникновение конфликтов блокировок между архивацией и перепроведением, придерживайтесь следующих правил:
BACKUP DATABASE средствами самого SQL Server не требует перевода базы в SINGLE_USER и не блокирует чтение/запись данных.1cv7s.exe в пакетном режиме, обязательно проверяйте факт полного закрытия процесса в диспетчере задач перед стартом следующего шага.DBCC CHECKDB.