Почему в 1С может зайти только один пользователь и как исправить ошибку «База данных заблокирована»?

Программист 1С v8.3 (Обычные формы)
← На главную

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

В этой статье мы подробно разберем, почему возникает такая ситуация, проанализируем конкретный пример ошибочного кода из отраслевых решений (в частности, от компании «Рарус») и выясним, как программно исправить эту проблему.

Первичная диагностика проблемы

Когда мы сталкиваемся с тем, что в базу может зайти только один человек, первым делом необходимо исключить стандартные административные причины. Проанализируем контрольный список действий:

  1. Проверка консоли администрирования 1С: убедимся, что в списке сеансов действительно нет старых, некорректно завершенных соединений.
  2. Проверка монопольного доступа: если база файловая, один пользователь мог зайти в режиме «Конфигуратор» или запустить обработку, требующую монопольного доступа.
  3. Анализ параметров запуска: проверим ярлыки пользователей на наличие ключа /m, который принудительно запускает сеанс в монопольном режиме.
  4. Регламентные операции: вспомним, не запускались ли регламентные задания по обновлению конфигурации или тестированию и исправлению базы.

Если все вышеперечисленное в порядке, значит, причина кроется внутри кода самой конфигурации. Рассмотрим, как программная логика может «захватить» базу.

Поиск причины в конфигурации

Разберем ситуацию, когда блокировка происходит на уровне встроенного языка. Для этого нам необходимо воспользоваться глобальным поиском по конфигурации (клавиши Ctrl + Shift + F). В строке поиска введем ключевую фразу УстановитьМонопольныйРежим(Истина).

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

Разбор «гениального» кода пересчета итогов

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


ДеньМесяцаНачалаНапоминания = 5;
ДеньМесяцаБезусловногоПересчета = 10;
ТекущийДеньМесяца = День(ТекущаяДата());    
НадоПересчитывать = Истина;

Если ТекущийДеньМесяца < ДеньМесяцаНачалаНапоминания Тогда
    НадоПересчитывать = Ложь;
Иначе
    Если ТекущийДеньМесяца < ДеньМесяцаБезусловногоПересчета Тогда
        ЕстьПользователиВБазе = Ложь;
        Попытка
            УстановитьМонопольныйРежим(Истина);
        Исключение
            ЕстьПользователиВБазе = Истина;
        КонецПопытки;
        
        Если Не ЕстьПользователиВБазе Тогда
            УстановитьМонопольныйРежим(Ложь);
        КонецЕсли;
        НадоПересчитывать = Не ЕстьПользователиВБазе;
    Иначе
        НадоПересчитывать = Истина;
    КонецЕсли;
КонецЕсли;

Проанализируем этот алгоритм. Мы видим, что программа начинает проверять необходимость пересчета итогов с 5-го числа каждого месяца. Если наступило 10-е число, пересчет становится «безусловным». Именно здесь кроется ловушка. Если условие ТекущийДеньМесяца < ДеньМесяцаБезусловногоПересчета не выполняется (то есть на календаре 10-е число или позже), программа переходит в ветку Иначе, где НадоПересчитывать = Истина, но при этом монопольный режим может быть установлен ранее или в последующих процедурах без должной проверки.

Где именно происходит ошибка блокировки?

Выясним причину блокировки в основной части процедуры. Рассмотрим продолжение кода, где происходит непосредственная работа с регистрами:


Если Не НадоПересчитывать Тогда
    Возврат;
КонецЕсли;

ПредпочтительнаяДатаРасчетаИтогов = НачалоМесяца(ТекущаяДата())-1;
СписокРегистровКРассчету = Новый Массив;

// Проход по регистрам накопления
Для Каждого Регистр ИЗ РегистрыНакопления Цикл                               
    МетаданныеРегистра = Метаданные.НайтиПоТипу(Тип(Регистр));
    Если МетаданныеРегистра.ВидРегистра = Метаданные.СвойстваОбъектов.ВидРегистраНакопления.Остатки Тогда
        Если ПравоДоступа("УправлениеИтогами", МетаданныеРегистра) Тогда
            Если Регистр.ПолучитьПериодРассчитанныхИтогов() < ПредпочтительнаяДатаРасчетаИтогов Тогда
                СписокРегистровКРассчету.Добавить(МетаданныеРегистра.ПолноеИмя());
            КонецЕсли;
        КонецЕсли;            
    КонецЕсли;
КонецЦикла;

// Если пересчитывать ничего не нужно, программа должна выйти
Если СписокРегистровКРассчету.Количество() = 0 Тогда
    // ВНИМАНИЕ: Здесь часто забывают снять монопольный режим!
    Возврат; 
КонецЕсли;

Важный момент: если в коде выше был вызван УстановитьМонопольныйРежим(Истина), но список регистров к расчету оказался пустым (например, итоги уже были рассчитаны ночью фоновым заданием), программа просто завершает выполнение процедуры командой Возврат. При этом монопольный режим остается включенным! Первый зашедший пользователь становится «единоличным владельцем» базы.

Как правильно исправить код

Рассмотрим правильный подход к исправлению. Нам необходимо гарантировать, что при любом выходе из процедуры (будь то штатное завершение или отказ пользователя от операции) монопольный режим будет снят. Добавим вызов УстановитьМонопольныйРежим(Ложь) в критических точках.

Исправленный фрагмент кода будет выглядеть так:


Если СписокРегистровКРассчету.Количество() = 0 Тогда
    // Обязательно снимаем режим перед выходом
    Попытка
        УстановитьМонопольныйРежим(Ложь);
    Исключение
        // Игнорируем, если режим и не был установлен
    КонецПопытки;
    Возврат;
КонецЕсли;

Если Вопрос("Рекомендуется выполнить расчет итогов. Выполнить сейчас?", 
            РежимДиалогаВопрос.ДаНет) = КодВозвратаДиалога.Нет Тогда
    // И здесь тоже нужно снять блокировку!
    Попытка
        УстановитьМонопольныйРежим(Ложь);
    Исключение
    КонецПопытки;
    Возврат;
КонецЕсли;

Использование блока Попытка...Исключение при снятии режима является хорошим тоном, так как попытка снять монопольный режим, который не был установлен, может вызвать программную ошибку в некоторых версиях платформы.

Рекомендации по улучшению архитектуры

Проанализировав ситуацию, мы понимаем, что выполнение таких тяжелых операций, как расчет итогов, при запуске клиентского приложения — это устаревший и небезопасный подход. Разберем, как сделать работу системы более стабильной:

  1. Использование регламентных заданий: Перенесите расчет итогов в фоновое задание. Создайте в конфигураторе объект РегламентноеЗадание, укажите расписание (например, ежедневно в 03:00) и назначьте метод, который будет выполнять УстановитьПериодРассчитанныхИтогов() для всех необходимых регистров.
  2. Проверка прав доступа: Убедитесь, что код установки монопольного режима вызывается только для пользователей с административными правами. Обычный пользователь все равно не сможет захватить базу монопольно, но может получить непонятную ошибку доступа. Используйте функцию РольДоступна("ПолныеПрава").
  3. Логирование: Добавьте запись в ЖурналРегистрации перед установкой монопольного режима и после его снятия. Это поможет системному администратору быстро понять причину блокировки базы в будущем.

Таким образом, проблема «База данных заблокирована» при одном активном пользователе чаще всего является следствием нарушения логики управления монопольным доступом. Своевременный поиск метода УстановитьМонопольныйРежим в коде конфигурации и добавление парного вызова со значением Ложь полностью решает данную проблему.

← На главную