Блокировка от параллельного выполнения функционала на примере регламентных заданий

Как не допустить одновременного выполнения каких-либо функций в системе. Например, есть 2 регламентных задания, которые выполняют один и тот же функционал параллельно.

У меня есть регламентное задание, которое выполняет функцию каждые 15 минут. Также функцию может вызвать вручную пользователь. Если функция уже выполняется, то пользователь, запустив ее вручную, рискует получить ошибки в данных.

Чтобы контролировать процесс, первое, что приходит в голову — записывать время старта и время окончания и проверять, выполняется ли функция. Но тут есть вопросы, связанные с обработкой ошибок, выключением сервера или даже падением сессии регламентных заданий.

Придется писать проверки, которые смотрят за статистикой выполнения и если интервал превышен, то…

Но есть способ гораздо проще. Это стандартные блокировки объектов 1С. Для этого у меня есть справочник:

Поле ТипОбъектаКонтроля — это перечисление в котором находятся все разделы функционала вызываемые регламентными заданиями. Стандартная функция, которая осуществляет блокировку:

ОбъектСправочника.Заблокировать();

Если мы в функции, которая выполняется будем проверять блокировку и блокировать элемент справочника. То даже если сессия упадет, или закончится ошибкой, у нас не возникнет ситуации "вечной блокировки".

Немного кода. Очень важно вернуть в вашу функцию объект блокировки. Если этого не сделать, блокировка снимется автоматически.
 

 
&НаСервере
Функция ЗаблокироватьПоТипуОбъектаКонтроля(_ТипОбъектаКонтроля, _Комментарий = "") Экспорт

Результат = Неопределено;

Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| БлокировкиФункционала.Ссылка
|ИЗ
| Справочник.БлокировкиФункционала КАК БлокировкиФункционала
|ГДЕ
| БлокировкиФункционала.ПометкаУдаления = ЛОЖЬ
| И БлокировкиФункционала.ТипОбъектаКонтроля = &ТипОбъектаКонтроля";

Запрос.УстановитьПараметр("ТипОбъектаКонтроля", _ТипОбъектаКонтроля);

РезультатЗапроса = Запрос.Выполнить();

ВыборкаДетальныеЗаписи = РезультатЗапроса.Выбрать();

Если  Не ВыборкаДетальныеЗаписи.Количество() = 1 Тогда
Возврат Результат;
КонецЕсли;

Пока ВыборкаДетальныеЗаписи.Следующий() Цикл
БФ = ВыборкаДетальныеЗаписи.Ссылка;

БФО = БФ.ПолучитьОбъект();

Попытка
БФО.Заблокировать();
БФО.ДатаБлокировки = ТекущаяДата();
БФО.Комментарий  = _Комментарий;
БФО.Записать();
Исключение
Результат = Неопределено;
КонецПопытки;

Результат = БФО;
КонецЦикла;

Возврат Результат;

КонецФункции

&НаСервере
Функция РазблокироватьПоТипуОбъектаКонтроля(_ТипОбъектаКонтроля, _Комментарий = "") Экспорт

Результат = Неопределено;

Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| БлокировкиФункционала.Ссылка
|ИЗ
| Справочник.БлокировкиФункционала КАК БлокировкиФункционала
|ГДЕ
| БлокировкиФункционала.ПометкаУдаления = ЛОЖЬ
| И БлокировкиФункционала.ТипОбъектаКонтроля = &ТипОбъектаКонтроля";

Запрос.УстановитьПараметр("ТипОбъектаКонтроля", _ТипОбъектаКонтроля);

РезультатЗапроса = Запрос.Выполнить();

ВыборкаДетальныеЗаписи = РезультатЗапроса.Выбрать();

Если  Не ВыборкаДетальныеЗаписи.Количество() = 1 Тогда
Возврат Результат;
КонецЕсли;

Пока ВыборкаДетальныеЗаписи.Следующий() Цикл
БФ = ВыборкаДетальныеЗаписи.Ссылка;

БФО = БФ.ПолучитьОбъект();

Попытка
БФО.ДатаБлокировки = ОбщийСерверКеш.ПустаяДата();
БФО.Комментарий  = "";
БФО.Записать();

БФО.Разблокировать();
Исключение
Результат = Неопределено;
КонецПопытки;

Результат = БФО;
КонецЦикла;

Возврат Результат;

КонецФункции

&НаСервере
Функция ПроверитьБлокировкуПоТипуОбъектаКонтроля(_ТипОбъектаКонтроля) Экспорт

Результат = Ложь;

Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| БлокировкиФункционала.Ссылка
|ИЗ
| Справочник.БлокировкиФункционала КАК БлокировкиФункционала
|ГДЕ
| БлокировкиФункционала.ПометкаУдаления = ЛОЖЬ
| И БлокировкиФункционала.ТипОбъектаКонтроля = &ТипОбъектаКонтроля";

Запрос.УстановитьПараметр("ТипОбъектаКонтроля", _ТипОбъектаКонтроля);

РезультатЗапроса = Запрос.Выполнить();

ВыборкаДетальныеЗаписи = РезультатЗапроса.Выбрать();

Если  Не ВыборкаДетальныеЗаписи.Количество() = 1 Тогда
Возврат Результат;
КонецЕсли;

Пока ВыборкаДетальныеЗаписи.Следующий() Цикл
БФ = ВыборкаДетальныеЗаписи.Ссылка;

БФО = БФ.ПолучитьОбъект();

Попытка
БФО.Заблокировать();
Исключение
Результат = Истина;
КонецПопытки;
КонецЦикла;

Возврат Результат;

КонецФункции


 
 

 

8 Comments

  1. soft_wind

    а вы сами тестировали свой функционал? там на глаз пара дырок точно есть

    1.обход выборки циклом! если есть несколько элементов с одним типом, то блокировки на первые сбросятся

    2.при установке блокировки, вы не обрабатываете исключение, и если блокировка не произошла вы всеравно возвращаете объект

    и др.

    Reply
  2. VmvLer

    не увидел транзакции — это будет работать без них?

    Reply
  3. 2tvad

    (1) Спасибо. Я несколько упростил функции для публикации.

    1. Контроль уникальности есть: проверки на дубли значения в справочнике при чтении и записи. Они стопорят функционал и пишут пользователю, в лог на e_mail.

    2. Вы правы. Поправлю.

    Reply
  4. 2tvad

    (2) Будет работать без транзакций.

    Reply
  5. fancy

    Для таких случаев возможно проще создавать предопределенные элементы спр-ка «БлокировкиФункционала» по одному для каждого нужного задания, тогда не нужно искать запросом нужный элемент для блокировки

    БФО = Справочники.БлокировкаФункционала.НужныйЭлемент.ПолучитьОбъект();

    а так метод рабочий, используем

    Reply
  6. 2tvad

    (6) Да, спасибо. Действительно так будет проще.

    Reply
  7. Lapitskiy

    Прекрасная идея.

    Но если поставить ключ регламентного задания, оно и так не будет выполнятся одновременно.

    Или о чем речь ?

    Reply
  8. 2tvad

    (8) Ну например, у нас интеграция выполняется по регламентному заданию. Так же пользователь может запустить этот же функционал в ручном режиме, когда интеграция еще не отработала, что может вызвать коллизии. На самом деле у нас есть еще ряд интеграции, которые взаимосвязаны и выполняются с разными интервалами.

    Ну например, при передаче данных на сайт, есть идея, сначала подготовить все данные — в виде текста SQL запросов, а потом выполнять их. Т.е. блокировать функционал, нужно будет только во время подготовки SQL запросов, а выполнять их можно уже не обращая внимание на изменение данных другими регламентными заданиями. Т.е. блокировка будет действовать, не 5 минут, а 1 минуту.

    Reply

Leave a Comment

Ваш адрес email не будет опубликован. Обязательные поля помечены *