У меня есть регламентное задание, которое выполняет функцию каждые 15 минут. Также функцию может вызвать вручную пользователь. Если функция уже выполняется, то пользователь, запустив ее вручную, рискует получить ошибки в данных.
Чтобы контролировать процесс, первое, что приходит в голову — записывать время старта и время окончания и проверять, выполняется ли функция. Но тут есть вопросы, связанные с обработкой ошибок, выключением сервера или даже падением сессии регламентных заданий.
Придется писать проверки, которые смотрят за статистикой выполнения и если интервал превышен, то…
Но есть способ гораздо проще. Это стандартные блокировки объектов 1С. Для этого у меня есть справочник:
Поле ТипОбъектаКонтроля — это перечисление в котором находятся все разделы функционала вызываемые регламентными заданиями. Стандартная функция, которая осуществляет блокировку:
ОбъектСправочника.Заблокировать();
Если мы в функции, которая выполняется будем проверять блокировку и блокировать элемент справочника. То даже если сессия упадет, или закончится ошибкой, у нас не возникнет ситуации "вечной блокировки".
Немного кода. Очень важно вернуть в вашу функцию объект блокировки. Если этого не сделать, блокировка снимется автоматически.
&НаСервере
Функция ЗаблокироватьПоТипуОбъектаКонтроля(_ТипОбъектаКонтроля, _Комментарий = "") Экспорт
Результат = Неопределено;
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| БлокировкиФункционала.Ссылка
|ИЗ
| Справочник.БлокировкиФункционала КАК БлокировкиФункционала
|ГДЕ
| БлокировкиФункционала.ПометкаУдаления = ЛОЖЬ
| И БлокировкиФункционала.ТипОбъектаКонтроля = &ТипОбъектаКонтроля";
Запрос.УстановитьПараметр("ТипОбъектаКонтроля", _ТипОбъектаКонтроля);
РезультатЗапроса = Запрос.Выполнить();
ВыборкаДетальныеЗаписи = РезультатЗапроса.Выбрать();
Если Не ВыборкаДетальныеЗаписи.Количество() = 1 Тогда
Возврат Результат;
КонецЕсли;
Пока ВыборкаДетальныеЗаписи.Следующий() Цикл
БФ = ВыборкаДетальныеЗаписи.Ссылка;
БФО = БФ.ПолучитьОбъект();
Попытка
БФО.Заблокировать();
БФО.ДатаБлокировки = ТекущаяДата();
БФО.Комментарий = _Комментарий;
БФО.Записать();
Исключение
Результат = Неопределено;
КонецПопытки;
Результат = БФО;
КонецЦикла;
Возврат Результат;
КонецФункции
&НаСервере
Функция РазблокироватьПоТипуОбъектаКонтроля(_ТипОбъектаКонтроля, _Комментарий = "") Экспорт
Результат = Неопределено;
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| БлокировкиФункционала.Ссылка
|ИЗ
| Справочник.БлокировкиФункционала КАК БлокировкиФункционала
|ГДЕ
| БлокировкиФункционала.ПометкаУдаления = ЛОЖЬ
| И БлокировкиФункционала.ТипОбъектаКонтроля = &ТипОбъектаКонтроля";
Запрос.УстановитьПараметр("ТипОбъектаКонтроля", _ТипОбъектаКонтроля);
РезультатЗапроса = Запрос.Выполнить();
ВыборкаДетальныеЗаписи = РезультатЗапроса.Выбрать();
Если Не ВыборкаДетальныеЗаписи.Количество() = 1 Тогда
Возврат Результат;
КонецЕсли;
Пока ВыборкаДетальныеЗаписи.Следующий() Цикл
БФ = ВыборкаДетальныеЗаписи.Ссылка;
БФО = БФ.ПолучитьОбъект();
Попытка
БФО.ДатаБлокировки = ОбщийСерверКеш.ПустаяДата();
БФО.Комментарий = "";
БФО.Записать();
БФО.Разблокировать();
Исключение
Результат = Неопределено;
КонецПопытки;
Результат = БФО;
КонецЦикла;
Возврат Результат;
КонецФункции
&НаСервере
Функция ПроверитьБлокировкуПоТипуОбъектаКонтроля(_ТипОбъектаКонтроля) Экспорт
Результат = Ложь;
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| БлокировкиФункционала.Ссылка
|ИЗ
| Справочник.БлокировкиФункционала КАК БлокировкиФункционала
|ГДЕ
| БлокировкиФункционала.ПометкаУдаления = ЛОЖЬ
| И БлокировкиФункционала.ТипОбъектаКонтроля = &ТипОбъектаКонтроля";
Запрос.УстановитьПараметр("ТипОбъектаКонтроля", _ТипОбъектаКонтроля);
РезультатЗапроса = Запрос.Выполнить();
ВыборкаДетальныеЗаписи = РезультатЗапроса.Выбрать();
Если Не ВыборкаДетальныеЗаписи.Количество() = 1 Тогда
Возврат Результат;
КонецЕсли;
Пока ВыборкаДетальныеЗаписи.Следующий() Цикл
БФ = ВыборкаДетальныеЗаписи.Ссылка;
БФО = БФ.ПолучитьОбъект();
Попытка
БФО.Заблокировать();
Исключение
Результат = Истина;
КонецПопытки;
КонецЦикла;
Возврат Результат;
КонецФункции
а вы сами тестировали свой функционал? там на глаз пара дырок точно есть
1.обход выборки циклом! если есть несколько элементов с одним типом, то блокировки на первые сбросятся
2.при установке блокировки, вы не обрабатываете исключение, и если блокировка не произошла вы всеравно возвращаете объект
и др.
не увидел транзакции — это будет работать без них?
(1) Спасибо. Я несколько упростил функции для публикации.
1. Контроль уникальности есть: проверки на дубли значения в справочнике при чтении и записи. Они стопорят функционал и пишут пользователю, в лог на e_mail.
2. Вы правы. Поправлю.
(2) Будет работать без транзакций.
Для таких случаев возможно проще создавать предопределенные элементы спр-ка «БлокировкиФункционала» по одному для каждого нужного задания, тогда не нужно искать запросом нужный элемент для блокировки
а так метод рабочий, используем
(6) Да, спасибо. Действительно так будет проще.
Прекрасная идея.
Но если поставить ключ регламентного задания, оно и так не будет выполнятся одновременно.
Или о чем речь ?
(8) Ну например, у нас интеграция выполняется по регламентному заданию. Так же пользователь может запустить этот же функционал в ручном режиме, когда интеграция еще не отработала, что может вызвать коллизии. На самом деле у нас есть еще ряд интеграции, которые взаимосвязаны и выполняются с разными интервалами.
Ну например, при передаче данных на сайт, есть идея, сначала подготовить все данные — в виде текста SQL запросов, а потом выполнять их. Т.е. блокировать функционал, нужно будет только во время подготовки SQL запросов, а выполнять их можно уже не обращая внимание на изменение данных другими регламентными заданиями. Т.е. блокировка будет действовать, не 5 минут, а 1 минуту.