Как обеспечить работу управляемых блокировок в регистре сведений?

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

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

Понимание природы управляемых блокировок

Первое и самое важное, что мы должны уяснить: управляемые блокировки — это логический механизм, реализованный внутри кластера серверов 1С, а не физический запрет на уровне таблиц базы данных (SQL). Рассмотрим ситуацию: когда мы вызываем метод Заблокировать(), платформа не вешает «замок» на таблицу в SQL Server. Вместо этого она делает запись в своей внутренней таблице менеджера блокировок: «Этот сеанс заблокировал этот ресурс».

Проанализируем принцип «джентльменского соглашения», на котором строится работа в управляемом режиме. Если Сеанс №1 установил исключительную блокировку на регистр, а Сеанс №2 просто выполняет запрос к этому регистру без использования объекта БлокировкаДанных, то Сеанс №2 успешно прочитает данные. Это происходит потому, что обычный запрос в управляемом режиме не запрашивает у менеджера блокировок разрешение на чтение. Чтобы блокировка сработала, оба участника (и тот, кто блокирует, и тот, кто читает) должны явно объявить свои намерения через программный код.

Разбор типичных ошибок в коде

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

Выясним причину неэффективности такого подхода:

  1. Неявный режим транзакции. Если в конфигурации или в свойствах метаданных не установлен управляемый режим блокировок по умолчанию, использование простого метода НачатьТранзакцию() может привести к непредсказуемому поведению. Рекомендуется всегда явно указывать режим.
  2. Игнорирование конструкции «ДЛЯ ИЗМЕНЕНИЯ». Многие помнят времена версии 8.1, где в запросе писали ДЛЯ ИЗМЕНЕНИЯ. В управляемом режиме (8.2 и 8.3) эта конструкция в тексте запроса полностью игнорируется платформой.
  3. Отсутствие блокировки на стороне «читателя». Если ваша задача — гарантировать, что никто не прочитает данные, пока вы их обрабатываете, то абсолютно все участники процесса должны использовать объект БлокировкаДанных.

Алгоритм правильной установки управляемой блокировки

Рассмотрим по шагам, как должен выглядеть надежный код для работы с регистром сведений в транзакции. Разберем пример, где мы хотим прочитать записи регистра ОтложенныеОбращенияКВебСервисам, обработать их и быть уверенными, что никто другой в этот момент их не перехватит.


НачатьТранзакцию(РежимУправленияБлокировкойДанных.Управляемый);

Попытка
    // 1. Создаем объект блокировки
    Блокировка = Новый БлокировкаДанных;
    
    // 2. Описываем пространство блокировки
    ЭлементБлокировки = Блокировка.Добавить("РегистрСведений.ОтложенныеОбращенияКВебСервисам");
    
    // 3. Устанавливаем режим. 
    // Исключительный — никто другой не сможет установить блокировку (ни S, ни X).
    ЭлементБлокировки.Режим = РежимБлокировкиДанных.Исключительный;
    
    // 4. Важно: если нужно заблокировать не весь регистр, а часть, указываем измерения
    // ЭлементБлокировки.УстановитьЗначение("Организация", ВыбраннаяОрганизация);
    
    // 5. Вызываем блокировку ДО выполнения запроса
    Блокировка.Заблокировать(); 
    
    // 6. Теперь выполняем чтение данных
    Запрос = Новый Запрос("ВЫБРАТЬ * ИЗ РегистрСведений.ОтложенныеОбращенияКВебСервисам ГДЕ...");
    Результат = Запрос.Выполнить();
    
    // Логика обработки данных
    // ...
    
    ЗафиксироватьТранзакцию();
Исключение
    ОтменитьТранзакцию();
    ВызватьИсключение;
КонецПопытки;

Почему запросы все равно читают «заблокированные» данные?

Проанализируем ситуацию со стороны СУБД. В режиме управляемых блокировок 1С использует уровень изоляции Read Committed (часто с механизмом Snapshot Isolation в SQL Server). Это означает следующее: когда одна транзакция изменяет данные, СУБД не блокирует чтение этих данных для других транзакций. Вместо этого другие транзакции получают «старую», последнюю зафиксированную версию данных из tempdb.

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

Особенности работы с измерениями

Посмотрим на важный нюанс: гранулярность блокировки. Если вы вызываете Блокировка.Добавить("РегистрСведений.ИмяРегистра") без указания измерений, вы блокируете весь регистр. В высоконагруженных системах это может привести к значительным ожиданиям (Timeout).

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


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

В этом случае другой сеанс, работающий с другим узлом, сможет беспрепятственно установить свою блокировку и продолжить работу, не дожидаясь вашей транзакции.

Краткие итоги и рекомендации

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

Соблюдение этих правил позволит вам создавать стабильные и производительные решения, корректно работающие в условиях многопользовательской среды.

← На главную