В процессе разработки в среде 1С:Предприятие часто возникает классическая дилемма: как обеспечить корректную работу системы, когда один и тот же объект (например, документ) одновременно пытаются изменить два разных субъекта. В нашем случае это Пользователь 1, открывший форму документа, и Пользователь 2, запустивший обработку, которая ищет и модифицирует этот же документ. Чтобы избежать потери данных и ошибок типа «Объект изменен или удален другим пользователем», нам необходимо разобраться в механизмах блокировок. Рассмотрим подробнее, как это работает и какой код необходимо написать.
Для начала проанализируем, чем отличаются основные подходы к блокированию данных. В 1С существует два уровня контроля доступа к объекту в пользовательском режиме.
Оптимистическая блокировка работает по принципу версионности. Когда Пользователь 1 открывает форму, система считывает текущую версию данных (скрытое поле ВерсияДанных). Система не запрещает другим пользователям читать или даже записывать этот объект. Однако в момент, когда Пользователь 1 нажмет кнопку «Записать», платформа сравнит версию в его форме с версией в базе данных. Если за это время Пользователь 2 (через обработку) уже изменил документ, версии не совпадут, и Пользователь 1 получит ошибку. Это максимально «свободный» режим, который минимизирует время ожидания, но может раздражать пользователей потерей введенных данных.
Пессимистическая блокировка, напротив, захватывает объект сразу. Как только Пользователь 1 начинает редактировать данные в форме (или вызывается метод ЗаблокироватьДанныеФормыДляРедактирования), другие пользователи не смогут захватить этот объект для изменения. Попытка программно заблокировать такой объект в обработке приведет к исключению.
Выясним, какой код должен написать программист в обработке, чтобы корректно взаимодействовать с документом, который может быть открыт у другого пользователя. Использование простого метода ПолучитьОбъект() и последующей записи недостаточно, так как это может привести к конфликту версий.
Посмотрим на пример правильной реализации логики в обработке:
// Получаем объект документа по ссылке, найденной запросом
ДокументОбъект = СсылкаНаДокумент.ПолучитьОбъект();
Попытка
// Пытаемся установить пессимистическую блокировку на уровне кластера 1С
ДокументОбъект.Заблокировать();
// Если блокировка прошла успешно, вносим изменения в табличную часть
НоваяСтрока = ДокументОбъект.Товары.Добавить();
НоваяСтрока.Номенклатура = Справочники.Номенклатура.НайтиПоНаименованию("Услуга");
НоваяСтрока.Количество = 1;
// Записываем объект. После успешной записи или отмены транзакции блокировка снимется
ДокументОбъект.Записать(РежимЗаписиДокумента.Запись);
Исключение
// Сюда мы попадем, если Пользователь 1 уже редактирует этот документ
Сообщить("Не удалось изменить документ " + СсылкаНаДокумент + ". Причина: " + ОписаниеОшибки());
КонецПопытки;
В данном примере метод Заблокировать() крайне важен. Если Пользователь 1 уже открыл форму документа и начал вносить изменения, этот метод мгновенно выдаст ошибку, и обработка Пользователя 2 не «затрет» изменения Пользователя 1. Мы корректно обрабатываем это в блоке Исключение.
Для того чтобы уменьшить время блокировки и повысить параллельность работы, в современных конфигурациях на платформе 8.3 используются управляемые блокировки. Они работают внутри транзакции и позволяют защитить данные на уровне СУБД.
Рассмотрим ситуацию: обработка Пользователя 2 сначала ищет документы запросом по определенному условию, а затем меняет их. Чтобы между поиском и изменением данные не изменились кем-то третьим, мы можем использовать объект БлокировкаДанных.
Разберем по шагам, как это реализовать:
БлокировкаДанных.Документ.РеализацияТоваровУслуг).Заблокировать() перед началом транзакции записи.Пример кода для управляемой блокировки внутри транзакции:
Блокировка = Новый БлокировкаДанных;
ЭлементБлокировки = Блокировка.Добавить("Документ.РеализацияТоваровУслуг");
ЭлементБлокировки.УстановитьЗначение("Ссылка", СсылкаНаДокумент);
ЭлементБлокировки.Режим = РежимБлокировкиДанных.Исключительный;
НачатьТранзакцию();
Попытка
// Устанавливаем блокировку в транзакции
Блокировка.Заблокировать();
ОбъектДляИзменения = СсылкаНаДокумент.ПолучитьОбъект();
// ... логика изменения ...
ОбъектДляИзменения.Записать();
ЗафиксироватьТранзакцию();
Исключение
ОтменитьТранзакцию();
Сообщить("Ошибка при выполнении транзакции: " + ОписаниеОшибки());
КонецПопытки;
Один из вопросов в обсуждении касался уменьшения времени удержания блокировки. Это критично, если пользователей много. Проанализируем основные стратегии:
НачатьТранзакцию() или получения объекта на редактирование. Внутри транзакции должен быть только «чистый» код записи.ПодключитьОбработчикОжидания будет проверять активность пользователя и программно освобождать блокировку (метод РазблокироватьДанныеФормыДляРедактирования), если правок не было внесено за долгое время.Правильная стратегия совместной работы пользователя и программного кода в 1С строится на комбинации трех уровней защиты:
1. Пессимистическая блокировка в форме: защищает Пользователя 1 от того, чтобы кто-то другой начал редактировать документ одновременно с ним в интерфейсе.
2. Метод Объект.Заблокировать() в коде: обязателен для использования в обработках и фоновых заданиях. Это «вежливый» способ спросить у системы: «Не занят ли сейчас этот документ кем-то другим в форме?».
3. Управляемые блокировки (DataLock): необходимы для защиты данных в момент самой записи внутри транзакции, чтобы обеспечить целостность базы данных на уровне СУБД.
Соблюдение этих правил позволит избежать конфликтов записи и сделает работу системы стабильной даже при высокой интенсивности изменений одних и тех же объектов.