В практике разработчика 1С часто встречается ситуация: вы создаете или редактируете регламентное задание, которое должно выполняться строго по расписанию (например, один раз в день в 10:00). Однако, как только вы программно или через консоль обновляете его параметры (наименование, расписание или использование), задание тут же инициирует фоновый процесс, даже если время запуска уже прошло или оно уже успешно отработало сегодня. Проанализируем, почему планировщик платформы ведет себя именно так, и разберем эффективные методы борьбы с этим явлением.
Разберем ситуацию с точки зрения системы. Планировщик заданий, работающий внутри менеджера кластера (rmngr), постоянно анализирует список активных регламентных заданий. Когда вы вызываете метод Записать() для объекта РегламентноеЗадание, платформа пересчитывает момент следующего запуска. Если текущее время (например, 11:00) уже превышает время, указанное в ежедневном расписании (10:00), и при этом в параметрах задания не указана будущая ДатаНачала, планировщик делает вывод: "Задание пропустило свой запуск в текущем периоде".
Для платформы перезаписанное задание часто выглядит как "новое" или "обновленное без истории", особенно если меняются его ключевые параметры. В результате система пытается "нагнать" упущенное и ставит задачу в очередь на немедленное выполнение. Рассмотрим подробнее, как мы можем программно повлиять на это поведение.
Один из самых простых и эффективных способов — это вычисление правильной даты начала при каждой записи регламентного задания. Нам нужно определить: если сегодня время планируемого запуска уже прошло, то новое задание должно иметь дату начала — завтрашний день. Если же время еще не наступило — оставляем текущую дату.
Рассмотрим пример реализации функции, которая подготавливает расписание перед записью:
Процедура ПодготовитьРасписаниеБезФальстарта(РегламентноеЗаданиеОбъект, НовоеРасписание)
ТекущаяДатаСервера = ТекущаяДата();
// Предположим, задание должно выполняться раз в день в 10:00:00
// Получаем время начала из расписания (это требует анализа структуры расписания)
ВремяНачалаЗадания = НовоеРасписание.ВремяНачала;
// Формируем момент запуска на сегодня
МоментЗапускаСегодня = НачалоДня(ТекущаяДатаСервера) + (ВремяНачалаЗадания - НачалоДня(ВремяНачалаЗадания));
Если ТекущаяДатаСервера > МоментЗапускаСегодня Тогда
// Время уже вышло, устанавливаем дату начала на завтра
НовоеРасписание.ДатаНачала = НачалоДня(ТекущаяДатаСервера + 86400);
Иначе
// Время еще не наступило, можно запускать сегодня
НовоеРасписание.ДатаНачала = НачалоДня(ТекущаяДатаСервера);
КонецЕсли;
РегламентноеЗаданиеОбъект.Расписание = НовоеРасписание;
КонецПроцедуры
Использование свойства ДатаНачала объекта РасписаниеРегламентногоЗадания позволяет жестко ограничить планировщик. Однако помните, что этот метод хорошо работает для ежедневных заданий. Если ваше задание выполняется каждые 15 минут, логика расчета будет сложнее.
Проанализируем роль свойства Ключ (Key) у регламентного задания. Это строковый идентификатор, который позволяет системе сопоставлять регламентное задание с его историей в фоновых заданиях. Если при каждой перезаписи вы не устанавливаете Ключ или он генерируется заново, платформа теряет связь с предыдущими запусками.
Рекомендация: всегда устанавливайте фиксированный, понятный Ключ для ваших самописных заданий (например, "РассылкаСМС_Основная"). Это помогает планировщику корректно определять, когда задание выполнялось в последний раз, даже после изменения его расписания или наименования.
Посмотрим на ситуацию архитектурно. Если мы не можем на 100% гарантировать, что платформа не инициирует запуск, мы можем запретить самому коду выполнять полезную работу второй раз за день. Для этого нам понадобится "хранилище состояния" — это может быть Константа или Регистр сведений.
Разберем по шагам алгоритм внутри процедуры регламентного задания:
НачалоДня(ТекущаяДата())), процедура просто завершает работу через Возврат.Пример кода обработчика задания:
Процедура ВыполнитьРассылкуСМС() Экспорт
// 1. Проверяем, не выполнялось ли уже сегодня
ДатаПоследнегоЗапуска = Константы.ДатаПоследнейРассылкиСМС.Получить();
Если НачалоДня(ДатаПоследнегоЗапуска) = НачалоДня(ТекущаяДата()) Тогда
ЗаписьЖурналаРегистрации("РегламентныеЗадания", УровеньЖурналаРегистрации.Информация, , ,
"Повторный запуск задания отменен (уже выполнялось сегодня).");
Возврат;
КонецЕсли;
// 2. Основная логика
Попытка
ПровестиРассылку();
// 3. Фиксируем успех
Константы.ДатаПоследнейРассылкиСМС.Установить(ТекущаяДата());
Исключение
ЗаписьЖурналаРегистрации("РегламентныеЗадания", УровеньЖурналаРегистрации.Ошибка, , ,
ОписаниеОшибки());
КонецПопытки;
КонецПроцедуры
Этот подход является наиболее устойчивым, так как он защищает систему не только от "фальстарта" при перезаписи задания, но и от случайных ручных запусков пользователями через консоль заданий.
Если ваша конфигурация построена на базе Библиотеки стандартных подсистем (БСП), крайне рекомендуется отказаться от прямого использования объекта РегламентноеЗадание. В БСП реализована подсистема "Регламентные задания", которая берет на себя управление жизненным циклом заданий.
Проанализируем преимущества работы через БСП. При использовании метода РегламентныеЗаданияСервер.УстановитьРасписаниеРегламентногоЗадания(), библиотека применяет дополнительные проверки и механизмы кэширования, которые минимизируют риск двойного запуска. Кроме того, БСП хранит настройки в специализированных регистрах, что делает управление заданиями более прозрачным для администратора системы.
В обсуждениях на форумах упоминается интересный "лайфхак": при необходимости изменить расписание, сначала программно очищается связь задания с процедурой (или устанавливается признак "Блокировка"), выполняется запись, а затем параметры восстанавливаются. Однако мы считаем этот метод избыточным и потенциально опасным, так как он может привести к потере целостности настроек при возникновении ошибки в момент "промежуточной" записи. Рекомендуем придерживаться Решения 1 (корректировка даты начала) или Решения 3 (проверка внутри кода).
Для того чтобы регламентное задание не выполнялось сразу после записи, мы рекомендуем комбинированный подход:
Ключ задания, чтобы платформа могла отслеживать его историю.ДатаНачала в объекте расписания, если запись происходит после времени планового запуска.Соблюдение этих правил позволит сделать работу ваших автоматизированных процессов предсказуемой и надежной, исключая раздражающие пользователей лишние уведомления или дублирование данных.