В процессе разработки на платформе 1С:Предприятие 8 программисты часто сталкиваются с необходимостью управления конкурентным доступом к данным. Если с пессимистической блокировкой всё кажется интуитивно понятным (вызываем метод Заблокировать() и объект «наш»), то с оптимистической блокировкой часто возникает путаница. Главный вопрос: каким кодом её установить? Разберем эту тему подробно, проанализируем внутренние механизмы платформы и выясним, почему для оптимистической блокировки не существует отдельной команды.
Давайте сразу внесем ясность: оптимистическая блокировка не устанавливается программно в том смысле, в котором мы привыкли работать с объектами. Это не действие, которое мы инициируем кодом, а стратегия поведения платформы. Само название «блокировка» здесь несколько условно. Правильнее называть это оптимистическим контролем версий.
Суть стратегии заключается в предположении (оптимизме), что пока один пользователь редактирует данные в форме, другой пользователь вряд ли будет менять этот же объект. Поэтому платформа не ставит никаких реальных «замков» в базе данных в момент открытия формы или начала редактирования. Вместо этого система просто запоминает состояние объекта в момент чтения и проверяет его перед самой записью.
Рассмотрим, на чем базируется этот механизм. У каждого ссылочного объекта (справочники, документы и т.д.) в 1С существует системный реквизит ВерсияДанных. Это скрытое поле, которое обновляется платформой автоматически при каждой успешной записи объекта в базу данных.
Проанализируем техническую реализацию:
rowversion. Это постоянно инкрементируемое число, уникальное в рамках всей базы.ВерсияДанных.WHERE не сработает, и 1С выдаст знаменитую ошибку: «Данные были изменены или удалены другим пользователем».Для того чтобы лучше понять природу оптимистического контроля, давайте сопоставим его с пессимистическим подходом. Посмотрим на таблицу различий:
Пессимистическая блокировка:
Заблокировать() или ЗаблокироватьДанныеДляРедактирования().Оптимистическая блокировка:
Хотя метода «установить блокировку» не существует, начиная с версии 8.3.15, у программистов появилось гораздо больше инструментов для взаимодействия с этим механизмом. Рассмотрим основные сценарии.
1. Получение версии данных через запрос
Раньше поле ВерсияДанных было недоступно в языке запросов. Теперь мы можем получить его напрямую, чтобы сравнить с имеющимся у нас значением, не считывая весь объект в память:
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Номенклатура.ВерсияДанных КАК ТекущаяВерсия
|ИЗ
| Справочник.Номенклатура КАК Номенклатура
|ГДЕ
| Номенклатура.Ссылка = &Ссылка";
Запрос.УстановитьПараметр("Ссылка", СсылкаНаОбъект);
Результат = Запрос.Выполнить().Выбрать();
Если Результат.Следующий() Тогда
Если Результат.ТекущаяВерсия <> ВерсияВПамяти Тогда
Сообщить("Внимание! Объект уже был изменен в базе.");
КонецЕсли;
КонецЕсли;
2. Ручная проверка перед тяжелыми операциями
Если ваша обработка выполняет сложные вычисления на основании данных объекта, прежде чем записывать результат, разумно проверить, не изменился ли объект за время расчетов. Для этого можно использовать свойство объекта ВерсияДанных.
ОбъектСпр = СсылкаНаОбъект.ПолучитьОбъект();
// ... какие-то длительные расчеты ...
// Перед записью проверяем версию еще раз (программное перечитывание)
Если ОбъектСпр.ВерсияДанных <> ПолучитьАктуальнуюВерсиюИзБазы(СсылкаНаОбъект) Тогда
ВызватьИсключение "Данные устарели, расчеты неактуальны!";
КонецЕсли;
ОбъектСпр.Записать();
3. Использование в независимых регистрах сведений
В новых версиях платформы оптимистическая блокировка стала доступна и для наборов записей независимых регистров сведений. Для этого используется метод УстановитьВерсиюДанных() у набора записей. Рассмотрим пример:
Набор = РегистрыСведений.ЦеныНоменклатуры.СоздатьНаборЗаписей();
Набор.Отбор.Номенклатура.Установить(ВыбраннаяНоменклатура);
// Указываем версию, которую мы прочитали ранее.
// Если в базе версия уже другая — Записать() выдаст исключение.
Набор.УстановитьВерсиюДанных(ПрочитаннаяРанееВерсия);
Попытка
Набор.Записать();
Исключение
Сообщить("Ошибка: данные регистра были изменены другим процессом.");
КонецПопытки;
Важно не путать объектную оптимистическую блокировку с блокировкой данных в транзакции (объект БлокировкаДанных).
Оптимистическая блокировка защищает от ситуации, когда два пользователя редактируют один и тот же справочник в течение 10 минут и один затирает изменения другого.
Транзакционная блокировка (пессимистическая по своей сути) защищает данные в течение миллисекунд или секунд, пока выполняется код на сервере (например, ОбработкаПроведения), чтобы в этот момент никто не смог изменить остатки, которые мы сейчас проверяем.
Проанализируем ситуацию: если вы открыли объект, наложили на него пессимистическую блокировку методом Заблокировать(), а в это время другой сеанс (например, регламентное задание) программно изменил этот объект (не используя форму), то при вашей попытке записи вы все равно получите ошибку оптимистической блокировки. Это происходит потому, что пессимистическая блокировка защищает только от интерактивного изменения через ЗаблокироватьДанныеДляРедактирования, но контроль версий (оптимистическая блокировка) работает на более низком уровне и является абсолютным гарантом целостности.
Выясним главные правила работы с этим механизмом:
Записать(), если данные реально изменились.Попытка...Исключение, так как вероятность конфликта оптимистической блокировки существует всегда, особенно в высоконагруженных системах.If-Match, который использует ВерсияДанных как ETag для реализации оптимистического контроля в веб-сервисах.Таким образом, оптимистическая блокировка — это невидимый, но надежный фундамент 1С, который позволяет системе работать быстро без лишних ожиданий на уровне СУБД, обеспечивая при этом сохранность данных при параллельной работе пользователей.