В процессе разработки на платформе 1С:Предприятие 8.3 часто возникает необходимость обеспечить изоляцию объекта во время его редактирования. Использование стандартного метода ЗаблокироватьДанныеДляРедактирования в процедуре ПриСозданииНаСервере — это классический подход для реализации пессимистической блокировки. Однако этот метод имеет существенный недостаток: если у пользователя внезапно выключается компьютер, пропадает интернет или происходит аварийное завершение процесса 1cv8.exe, сеанс на сервере может «зависнуть». Вместе с сеансом остается активной и блокировка, не позволяя другим сотрудникам работать с объектом.
В этой статье мы подробно разберем, как бороться с этой проблемой, рассмотрим административные настройки сервера и спроектируем программный механизм «мягких» блокировок, который лишен недостатков стандартного метода.
Прежде чем приступать к написанию кода, проанализируем настройки самой платформы. Сервер 1С умеет самостоятельно завершать «брошенные» сеансы, но по умолчанию параметры очистки настроены на очень длительные интервалы. Если сеанс переходит в состояние «спящий», он продолжает удерживать все установленные блокировки данных.
Разберем по шагам, как изменить это поведение через консоль администрирования кластера:
600–1200 секунд (10-20 минут).Также важно обратить внимание на параметры самого кластера. В свойствах рабочего сервера существуют настройки -pingPeriod и -pingTimeout. Эти параметры определяют, как быстро сервер поймет, что связь с клиентом разорвана. Если сетевое соединение было разорвано некорректно, сервер будет ждать ответа от клиента, прежде чем пометить сеанс как неактивный.
Если административные настройки не помогают или специфика бизнеса требует мгновенного освобождения объекта при повторном входе, рассмотрим создание собственного механизма управления блокировками. Этот способ считается наиболее гибким и «красивым» с точки зрения разработки.
Суть метода заключается в использовании Регистра Сведений (например, с именем БлокировкиОбъектов), где мы будем фиксировать, кто, когда и с какого компьютера занял объект.
Шаг 1. Создание структуры регистра
Создадим непериодический независимый регистр сведений со следующими полями:
Объект (Тип: ЛюбаяСсылка)Пользователь (Тип: СправочникСсылка.Пользователи)ИдентификаторСеанса (Тип: Число)ДатаБлокировки (Тип: Дата)ИмяКомпьютера (Тип: Строка)Шаг 2. Реализация захвата объекта
Проанализируем ситуацию: при открытии формы нам нужно проверить, не занят ли объект кем-то другим, и если занят — проверить, не является ли эта блокировка «протухшей».
Функция ЗахватитьОбъект(СсылкаНаОбъект) Экспорт
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| БлокировкиОбъектов.Пользователь,
| БлокировкиОбъектов.ДатаБлокировки,
| БлокировкиОбъектов.ИдентификаторСеанса
|ИЗ
| РегистрСведений.БлокировкиОбъектов КАК БлокировкиОбъектов
|ГДЕ
| БлокировкиОбъектов.Объект = &Объект";
Запрос.УстановитьПараметр("Объект", СсылкаНаОбъект);
Результат = Запрос.Выполнить().Выбрать();
Если Результат.Следующий() Тогда
// Проверяем, жив ли сеанс
СписокСеансов = ПолучитьСеансыИнформационнойБазы();
СеансЖив = Ложь;
Для Каждого ТекущийСеанс Из СписокСеансов Цикл
Если ТекущийСеанс.НомерСеанса = Результат.ИдентификаторСеанса Тогда
СеансЖив = Истина;
Прервать;
КонецЕсли;
КонецЦикла;
// Если сеанса нет в списке активных или прошло более 2 часов
Если Не СеансЖив Или (ТекущаяДата() - Результат.ДатаБлокировки > 7200) Тогда
ОсвободитьОбъект(СсылкаНаОбъект);
Иначе
Возврат Ложь; // Объект реально занят живым сеансом
КонецЕсли;
КонецЕсли;
// Создаем новую запись о блокировке
МенеджерЗаписи = РегистрыСведений.БлокировкиОбъектов.СоздатьМенеджерЗаписи();
МенеджерЗаписи.Объект = СсылкаНаОбъект;
МенеджерЗаписи.Пользователь = Пользователи.ТекущийПользователь();
МенеджерЗаписи.ИдентификаторСеанса = НомерСеанса();
МенеджерЗаписи.ДатаБлокировки = ТекущаяДата();
МенеджерЗаписи.Записать();
Возврат Истина;
КонецФункции
Шаг 3. Использование механизма Heartbeat (пульсация)
Чтобы блокировка не снималась преждевременно, пока пользователь просто долго читает текст на экране, внедрим обработчик ожидания. В форме объекта будем раз в несколько минут обновлять ДатаБлокировки в регистре.
Рассмотрим пример кода в модуле формы:
&НаКлиенте
Процедура ПриОткрытии(Отказ)
Если Не ЗахватитьОбъектНаСервере(Объект.Ссылка) Тогда
Сообщить("Объект заблокирован другим пользователем!");
ЭтаФорма.ТолькоПросмотр = Истина;
Иначе
ПодключитьОбработчикОжидания("ОбновитьБлокировкуОбъекта", 300); // каждые 5 минут
КонецЕсли;
КонецПроцедуры
&НаКлиенте
Процедура ОбновитьБлокировкуОбъекта()
ОбновитьДатуБлокировкиНаСервере(Объект.Ссылка);
КонецПроцедуры
Для автоматической очистки «мусора» в таблице блокировок полезно создать регламентное задание, которое будет запускаться, например, раз в 15 минут. Это позволит системе самоочищаться даже без участия пользователей.
Выясним причину, по которой это необходимо: если пользователь просто закрыл программу «крестиком», сеанс может исчезнуть не сразу, а запись в регистре останется. Регламентное задание проанализирует все записи в РегистрСведений.БлокировкиОбъектов и сопоставит их с текущими активными сеансами через метод ПолучитьСеансыИнформационнойБазы().
Проанализируем пример кода для регламентного задания:
Процедура ОчисткаУстаревшихБлокировок() Экспорт
АктивныеСеансы = ПолучитьСеансыИнформационнойБазы();
ТаблицаСеансов = Новый ТаблицаЗначений;
ТаблицаСеансов.Колонки.Добавить("НомерСеанса");
Для Каждого Сеанс Из АктивныеСеансы Цикл
ТаблицаСеансов.Добавить().НомерСеанса = Сеанс.НомерСеанса;
КонецЦикла;
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Блокировки.Объект КАК Объект
|ИЗ
| РегистрСведений.БлокировкиОбъектов КАК Блокировки
|ГДЕ
| НЕ Блокировки.ИдентификаторСеанса В (&НомераАктивныхСеансов)";
Запрос.УстановитьПараметр("НомераАктивныхСеансов", ТаблицаСеансов.ВыгрузитьКолонку("НомерСеанса"));
Выборка = Запрос.Выполнить().Выбрать();
Пока Выборка.Следующий() Цикл
// Удаляем записи о блокировках, чьи сеансы больше не существуют
Набор = РегистрыСведений.БлокировкиОбъектов.СоздатьНаборЗаписей();
Набор.Отбор.Объект.Установить(Выборка.Объект);
Набор.Записать();
КонецЦикла;
КонецПроцедуры
Мы рассмотрели несколько уровней решения проблемы зависших объектов. Первый уровень — административный — позволяет минимизировать время жизни «спящих» сеансов штатными средствами платформы 1С. Второй уровень — программный — дает полный контроль над процессом блокировки и позволяет реализовать логику «перехвата» объекта, если предыдущий пользователь аварийно завершил работу.
Важные рекомендации:
ЗаблокироватьДанныеДляРедактирования (для защиты от одновременной записи в транзакции) и «мягкую» блокировку через регистр (для визуального информирования пользователей).Использование данных методов позволит сделать работу пользователей в системе 1С более комфортной и избавит вас от необходимости постоянно «выкидывать» зависшие сеансы через консоль администрирования вручную.