Представим ситуацию: ваша база 1С работает в штатном режиме, но внезапно пользователи начинают жаловаться, что не могут войти в систему. При этом первый успевший войти сотрудник работает без проблем, а все остальные получают критическую ошибку: «База данных заблокирована». При проверке консоли администрирования серверов выясняется, что «зависших» сеансов нет, блокировок на уровне СУБД тоже не наблюдается, а перезагрузка сервера и очистка кэша не приносят результата.
В этой статье мы подробно разберем, почему возникает такая ситуация, проанализируем конкретный пример ошибочного кода из отраслевых решений (в частности, от компании «Рарус») и выясним, как программно исправить эту проблему.
Когда мы сталкиваемся с тем, что в базу может зайти только один человек, первым делом необходимо исключить стандартные административные причины. Проанализируем контрольный список действий:
/m, который принудительно запускает сеанс в монопольном режиме.Если все вышеперечисленное в порядке, значит, причина кроется внутри кода самой конфигурации. Рассмотрим, как программная логика может «захватить» базу.
Разберем ситуацию, когда блокировка происходит на уровне встроенного языка. Для этого нам необходимо воспользоваться глобальным поиском по конфигурации (клавиши Ctrl + Shift + F). В строке поиска введем ключевую фразу УстановитьМонопольныйРежим(Истина).
Проанализируем, почему этот метод может стать причиной проблемы. Метод УстановитьМонопольныйРежим предназначен для того, чтобы запретить другим пользователям подключаться к информационной базе во время выполнения критических операций (например, пересчет итогов или загрузка данных). Если система вызывает этот метод при старте первого сеанса и по какой-то причине не вызывает УстановитьМонопольныйРежим(Ложь), база остается заблокированной для всех остальных до тех пор, пока первый пользователь не выйдет из программы.
В некоторых отраслевых конфигурациях (например, в решениях для управления автотранспортом) встречается механизм автоматического управления итогами регистров. Логика разработчиков проста: чтобы отчеты работали быстро, итоги должны быть рассчитаны на актуальную дату. Посмотрим на типичный фрагмент кода, который вызывает блокировку:
ДеньМесяцаНачалаНапоминания = 5;
ДеньМесяцаБезусловногоПересчета = 10;
ТекущийДеньМесяца = День(ТекущаяДата());
НадоПересчитывать = Истина;
Если ТекущийДеньМесяца < ДеньМесяцаНачалаНапоминания Тогда
НадоПересчитывать = Ложь;
Иначе
Если ТекущийДеньМесяца < ДеньМесяцаБезусловногоПересчета Тогда
ЕстьПользователиВБазе = Ложь;
Попытка
УстановитьМонопольныйРежим(Истина);
Исключение
ЕстьПользователиВБазе = Истина;
КонецПопытки;
Если Не ЕстьПользователиВБазе Тогда
УстановитьМонопольныйРежим(Ложь);
КонецЕсли;
НадоПересчитывать = Не ЕстьПользователиВБазе;
Иначе
НадоПересчитывать = Истина;
КонецЕсли;
КонецЕсли;
Проанализируем этот алгоритм. Мы видим, что программа начинает проверять необходимость пересчета итогов с 5-го числа каждого месяца. Если наступило 10-е число, пересчет становится «безусловным». Именно здесь кроется ловушка. Если условие ТекущийДеньМесяца < ДеньМесяцаБезусловногоПересчета не выполняется (то есть на календаре 10-е число или позже), программа переходит в ветку Иначе, где НадоПересчитывать = Истина, но при этом монопольный режим может быть установлен ранее или в последующих процедурах без должной проверки.
Выясним причину блокировки в основной части процедуры. Рассмотрим продолжение кода, где происходит непосредственная работа с регистрами:
Если Не НадоПересчитывать Тогда
Возврат;
КонецЕсли;
ПредпочтительнаяДатаРасчетаИтогов = НачалоМесяца(ТекущаяДата())-1;
СписокРегистровКРассчету = Новый Массив;
// Проход по регистрам накопления
Для Каждого Регистр ИЗ РегистрыНакопления Цикл
МетаданныеРегистра = Метаданные.НайтиПоТипу(Тип(Регистр));
Если МетаданныеРегистра.ВидРегистра = Метаданные.СвойстваОбъектов.ВидРегистраНакопления.Остатки Тогда
Если ПравоДоступа("УправлениеИтогами", МетаданныеРегистра) Тогда
Если Регистр.ПолучитьПериодРассчитанныхИтогов() < ПредпочтительнаяДатаРасчетаИтогов Тогда
СписокРегистровКРассчету.Добавить(МетаданныеРегистра.ПолноеИмя());
КонецЕсли;
КонецЕсли;
КонецЕсли;
КонецЦикла;
// Если пересчитывать ничего не нужно, программа должна выйти
Если СписокРегистровКРассчету.Количество() = 0 Тогда
// ВНИМАНИЕ: Здесь часто забывают снять монопольный режим!
Возврат;
КонецЕсли;
Важный момент: если в коде выше был вызван УстановитьМонопольныйРежим(Истина), но список регистров к расчету оказался пустым (например, итоги уже были рассчитаны ночью фоновым заданием), программа просто завершает выполнение процедуры командой Возврат. При этом монопольный режим остается включенным! Первый зашедший пользователь становится «единоличным владельцем» базы.
Рассмотрим правильный подход к исправлению. Нам необходимо гарантировать, что при любом выходе из процедуры (будь то штатное завершение или отказ пользователя от операции) монопольный режим будет снят. Добавим вызов УстановитьМонопольныйРежим(Ложь) в критических точках.
Исправленный фрагмент кода будет выглядеть так:
Если СписокРегистровКРассчету.Количество() = 0 Тогда
// Обязательно снимаем режим перед выходом
Попытка
УстановитьМонопольныйРежим(Ложь);
Исключение
// Игнорируем, если режим и не был установлен
КонецПопытки;
Возврат;
КонецЕсли;
Если Вопрос("Рекомендуется выполнить расчет итогов. Выполнить сейчас?",
РежимДиалогаВопрос.ДаНет) = КодВозвратаДиалога.Нет Тогда
// И здесь тоже нужно снять блокировку!
Попытка
УстановитьМонопольныйРежим(Ложь);
Исключение
КонецПопытки;
Возврат;
КонецЕсли;
Использование блока Попытка...Исключение при снятии режима является хорошим тоном, так как попытка снять монопольный режим, который не был установлен, может вызвать программную ошибку в некоторых версиях платформы.
Проанализировав ситуацию, мы понимаем, что выполнение таких тяжелых операций, как расчет итогов, при запуске клиентского приложения — это устаревший и небезопасный подход. Разберем, как сделать работу системы более стабильной:
РегламентноеЗадание, укажите расписание (например, ежедневно в 03:00) и назначьте метод, который будет выполнять УстановитьПериодРассчитанныхИтогов() для всех необходимых регистров.РольДоступна("ПолныеПрава").ЖурналРегистрации перед установкой монопольного режима и после его снятия. Это поможет системному администратору быстро понять причину блокировки базы в будущем.Таким образом, проблема «База данных заблокирована» при одном активном пользователе чаще всего является следствием нарушения логики управления монопольным доступом. Своевременный поиск метода УстановитьМонопольныйРежим в коде конфигурации и добавление парного вызова со значением Ложь полностью решает данную проблему.