Техническое устройство и разработка#
Страница объясняет внутреннее устройство менеджера заданий и поддерживаемые способы его использования в прикладном коде. Пользовательский интерфейс описан на странице «Интерфейс менеджера заданий», настройка и диагностика — на странице «Администрирование и диагностика», механизм очистки — на странице «Очистка данных».
Прикладной разработчик должен работать через контракты *Apic. Низкоуровневые классы Quartz и внутренние реализации *Api используются внутри менеджера заданий и не являются основным межмодульным контрактом.
Архитектура#
Менеджер заданий состоит из прикладной модели заданий, Quartz с реализациями GsfJobStore и GsfThreadPool, отдельного процесса jobscheduler и ApplicationServer.
Менеджер заданий
├── Btk_Job (конфигурация задания и общие ограничения)
├── Btk_JobSchedule (расписания задания)
├── Quartz JobStore (технические задания и триггеры)
├── GsfJobStore (регистрация времени получения триггеров)
├── GsfThreadPool (пулы выполнения и очереди конкурентных запусков)
├── jobscheduler (определяет наступившие запуски)
├── ApplicationServer (выполняет прикладную операцию)
├── Btk_JobEvent (состояние и результат попытки выполнения)
└── Btk_JobEventLog и Btk_JobStats (сообщения и статистика)
Quartz 2.3.1 использует GsfJobStore поверх PostgreSQL с префиксом таблиц btk_qrtz_, именем экземпляра PostgresScheduler и делегатом PostgreSQL. Потоки выполнения предоставляет GsfThreadPool. Точная версия зависимости ru.bitec:jobscheduler определяется поставкой.
Схема показывает основные внутренние компоненты jobscheduler, Quartz и пула потоков. Она нужна для понимания взаимодействия планировщика с JobStore и потоками выполнения; прикладной код не должен обращаться к этим компонентам напрямую.

Планировщик и Quartz#
Btk_Job не является Quartz job. Запись задания хранит прикладную конфигурацию, а техническая запись Quartz создается из нее при синхронизации.
При изменении задания или расписания система устанавливает bSync = 0. После сохранения изменений updateJob обрабатывает несинхронизированные задания пакетами:
Для выключенного задания удаляет техническое представление из Quartz.
Для включенного задания создает или заменяет Quartz job.
Для активных расписаний создает
SimpleTriggerилиCronTrigger.Для выключенных и удаленных расписаний удаляет триггеры.
После обработки устанавливает
bSync = 1.
Данные Quartz job содержат адрес SOAP-точки, идентификатор базы данных, данные аутентификации, пользователя выполнения, JEXL-вызов менеджера заданий и параметры конкурентности. SoapReqJob и SoapReqJobConcurrent служат внутренними Quartz-адаптерами: преобразуют срабатывание триггера в запрос выполнения к ApplicationServer. Прикладной код не выбирает эти классы напрямую.
Для передачи этих данных внутренние Quartz-адаптеры используют JobDataMap. Это техническое хранилище Quartz, а не контракт прикладного задания: не изменяйте его напрямую и не рассчитывайте на сохранение в нем произвольных объектов между версиями планировщика.
Таблицы Quartz являются производными. Изменять их напрямую из прикладного модуля нельзя. Полная пересинхронизация и ее последствия описаны на странице «Администрирование и диагностика».
Цикл выполнения Quartz#
При запуске процесса jobscheduler SchedulerHelper создает и инициализирует SchedulerManager. Менеджер создает Scheduler, а тот — QuartzScheduler и QuartzSchedulerThread. После запуска Quartz поток планировщика начинает обрабатывать триггеры.
Один цикл обработки планового запуска проходит следующие этапы:
QuartzSchedulerThreadчерезJobStore.acquireNextTriggersполучает триггеры в состоянииWAITING, для которых наступило расчетное время в допустимом окне. Полученные триггеры переходят в состояниеACQUIRED.JobStore.triggersFiredфиксирует срабатывание триггеров. Для одноразового триггера без следующего времени устанавливается состояниеCOMPLETE. Для непараллельного задания остальные триггеры этого задания блокируются (BLOCKED), а для параллельного задания следующий запуск остается вWAITING.Для выбранных запусков создаются выполняемые оболочки, которые передаются в
GsfThreadPool. Запись активного срабатывания триггера получает состояниеEXECUTING.Оболочка вызывает SOAP-операцию
executeJexlApplicationServer. Дальнейшее выполнение прикладной операции и сохранениеBtk_JobEventописаны в разделе «Плановое выполнение».После завершения оболочка вызывает
JobStore.triggeredJobComplete. Quartz удаляет запись активного срабатывания и либо рассчитывает следующее время запуска и возвращает триггер вWAITING, либо удаляет завершенный одноразовый триггер.
Состояние Quartz-триггера и состояние события Btk_JobEvent описывают разные уровни выполнения. Например, ACQUIRED означает получение триггера планировщиком, а «Выполняется» — наличие выполняющейся серверной сессии задания.
Хранение триггеров и блокировки#
Quartz хранит технические задания и триггеры в PostgreSQL в таблицах с префиксом btk_qrtz_. Эти таблицы являются производным представлением конфигурации менеджера заданий и заполняются при синхронизации Btk_Job и Btk_JobSchedule.
Для работы с PostgreSQL используется GsfJobStore, основанный на стандартном Quartz JobStore. Его дополнительная функция в системе — регистрация времени получения триггеров в SchedulerStatMonitor. Распределение запусков по потокам и очередям выполняет GsfThreadPool.
Право активного выполнения заданий между несколькими процессами jobscheduler координируется одним из двух режимов:
REST-блокировка ApplicationServer. Планировщик периодически обращается к сервису блокировки. Процесс, получивший блокировку, запускает Quartz. Остальные процессы переводят Quartz в режим ожидания (
standby).PostgreSQL advisory lock. В режиме высокой доступности несколько процессов могут быть запущены одновременно, но выполнять задания может только процесс, которому удалось получить advisory lock в PostgreSQL. При потере блокировки текущий Quartz останавливается, после чего процесс пытается создать его заново.
Конкретный режим определяется конфигурацией поставки. Несколько процессов повышают отказоустойчивость, но не позволяют нескольким master-процессам одновременно выполнять одну и ту же очередь.
Блокировка планировщика координирует право на получение и выполнение Quartz-запусков. Она не заменяет транзакции, блокировки и проверки полномочий прикладного кода, выполняемого в ApplicationServer.
Потоки и конкурентность#
bpConcurrent = true разрешает параллельные экземпляры одного задания. nUnscheduledThreads задает лимит внеплановых потоков для этого задания, а Btk_JobConcurrentThreadSchedule позволяет менять лимит по дню недели и часу. Рассчитанное значение передается планировщику как CONCURRENT_THREADS_NUM.
GsfThreadPool выполняет обычные Quartz job в основном пуле. Для конкурентных заданий он создает отдельные пулы, поэтому лимит одного задания не расходуется другими конкурентными заданиями. Увеличение nUnscheduledThreads повышает параллелизм только соответствующего задания и одновременно увеличивает возможное число выполняющих потоков процесса.
GsfThreadPool распределяет конкурентные запуски по группам очереди, заданным в spGroup. Между непустыми группами действует алгоритм round-robin, внутри каждой группы — FIFO. Round-robin не гарантирует одинакового времени выполнения заданий. Очереди находятся в памяти процесса, поэтому после перезапуска их прежний порядок не сохраняется.
Пропускная способность настраивается количеством потоков основного пула и лимитами отдельных пулов конкурентных заданий. При диагностике и остановке менеджер ищет связанные рабочие сессии на всех узлах кластера ApplicationServer.
Запуск процесса jobscheduler#
jobscheduler запускается отдельно от ApplicationServer. Он поддерживает CLI-режимы -start, -startOnPort и -stopOnPort, а также запуск как Linux- или Windows-служба. Конкретный unit-файл и команда запуска зависят от эксплуатационной поставки. Во всех вариантах процессу нужны конфигурация Quartz, подключение к PostgreSQL, адрес ApplicationServer и данные системной аутентификации.
Несколько процессов могут работать с общим JobStore только при общей настройке координации через REST-lock или PostgreSQL advisory lock. Само добавление процессов не увеличивает лимиты отдельных конкурентных заданий: их определяют параметры, переданные в GsfThreadPool. Нельзя одновременно смешивать разные механизмы блокировки для экземпляров, обслуживающих один JobStore.
Служебные компоненты процесса#
Процесс jobscheduler запускает служебные компоненты, которые предотвращают дублирование запусков и зависание занятых потоков, собирают сведения о состоянии процесса и предоставляют их для диагностики:
При запуске jobscheduler компонент SchedulerManager создает SchedulerStatMonitor, запускает JobThreadWatcher и GsfWebServer. В обычном режиме он также запускает SchedulerLocker, который управляет состоянием Quartz через REST-блокировку. В режиме высокой доступности PostgreSQL advisory lock получает и контролирует сам SchedulerManager.
Компонент |
Назначение |
|---|---|
|
Сопоставляет выполняемые Quartz-запуски с активными рабочими сессиями ApplicationServer и прерывает Quartz job при устойчивом расхождении |
|
Собирает оперативные показатели текущего процесса планировщика |
|
Управляет запуском и режимом ожидания Quartz при координации через REST-блокировку ApplicationServer |
|
Публикует состояние планировщика в диагностическом HTML-интерфейсе и REST-сервисе |
Компоненты используют общий экземпляр Quartz и клиент ApplicationServer, но решают независимые задачи. GsfJobStore и SOAP-задания передают оперативные данные в SchedulerStatMonitor, а GsfWebServer публикует собранное состояние. JobThreadWatcher и SchedulerLocker обращаются к Quartz и ApplicationServer напрямую и не зависят от диагностического веб-сервера.
JobThreadWatcher освобождает поток планировщика, если рабочая сессия ApplicationServer уже исчезла, а Quartz продолжает считать запуск активным. Компонент работает в отдельном потоке: после каждого интервала проверки получает список выполняемых заданий Quartz и список активных рабочих сессий ApplicationServer, затем сопоставляет их по идентификатору триггера. Разовое расхождение не прерывает запуск. При устойчивом расхождении наблюдатель вызывает прерывание соответствующего экземпляра Quartz job. В режиме standby проверка не выполняется. Интервал задает свойство ru.bitec.jobscheduler.jobThreadWatcher.durationBetweenChecks в конфигурации jobscheduler; если свойство отсутствует, используется 300 000 мс.
SchedulerStatMonitor позволяет оценить текущее состояние процесса без анализа его журналов: получает ли планировщик триггеры, занят ли общий пул и возникают ли проблемы связи с ApplicationServer. Для этого он хранит дату запуска процесса, размер и занятость общего пула, времена последних 100 обращений к JobStore за триггерами, количество запросов к ApplicationServer и ошибок соединения, а также сведения о последних запусках и числе активных экземпляров каждого задания. GsfJobStore передает монитору только время обращения за триггерами, а SOAP-задания регистрируют начало, окончание и результат обращения к ApplicationServer. Эти оперативные показатели не заменяют сведения о запусках в Btk_JobEvent и сводную статистику Btk_JobStats. Ошибки прикладного кода, полученные в успешном ответе ApplicationServer, не входят в счетчик ошибок соединения.
SchedulerLocker предотвращает одновременное выполнение заданий несколькими процессами jobscheduler в обычном режиме с REST-блокировкой. Он периодически запрашивает право выполнения у ApplicationServer: при получении блокировки запускает Quartz, при отказе переводит его в standby. Адрес REST-точки и интервал обращения задаются в cQuartzProps параметрами restLockServerOnSchedulerReqStr и restSchedulerLockFrequency. В режиме высокой доступности SchedulerLocker не участвует.
GsfWebServer позволяет администратору или внешней системе мониторинга проверить текущее состояние jobscheduler без доступа к интерфейсу менеджера и анализа журналов. Он предоставляет HTML-интерфейс по пути / и JSON-представление тех же оперативных показателей по пути /rest/getSchedulerInfo. REST-сервис поддерживает метод GET. Порт задает свойство ru.bitec.jobscheduler.webserver.port в конфигурации jobscheduler; если свойство отсутствует, используется порт 8180. Если диагностический веб-сервер не удалось запустить, ошибка записывается в журнал, но работа Quartz продолжается.
Технические идентификаторы#
Объект |
Идентификатор |
|---|---|
Плановый Quartz job |
идентификатор |
Плановый триггер |
идентификатор |
Конкурентный одноразовый Quartz job |
|
Одноразовый триггер |
|
Внутренний TaskBuilder сохраняет задания и триггеры с replace = true. В поддерживаемых API идентификаторы формирует менеджер заданий, поэтому прикладной модуль не должен добавлять случайный суффикс для предотвращения коллизий.
Модель данных#
Btk_Job
Задание хранит:
системное имя, наименование и описание;
признак активности и тип объекта;
JEXL-скрипт;
группу и пользователя выполнения;
срок хранения событий и журналов;
разрешенные интервалы запуска;
общий лимит внеплановых потоков;
признак трассировки;
политику повторных попыток;
признаки синхронизации и логического удаления.
bSync показывает, обработаны ли последние изменения конфигурации при формировании Quartz job. bDeleted отмечает логическое удаление. Окончательное удаление основной записи Btk_Job не следует использовать как публично гарантированный сценарий без дополнительной проверки реализации.
Btk_JobSchedule
Расписание является дочерней записью задания. Оно хранит активность, период действия, секунды, минуты, часы, дни недели, дни месяца, месяцы, тип периодичности, приращение дней и параметры повторных попыток.
Технический идентификатор планового триггера равен идентификатору Btk_JobSchedule.
Btk_JobEvent
Событие представляет один экземпляр запуска. Оно хранит:
задание и состояние выполнения;
даты начала и окончания;
параметры запуска в
jParams;признаки одноразового и конкурентного запуска;
параметры повторных попыток;
идентификатор Quartz-триггера;
пользователя выполнения;
группу очереди.
Плановый запуск создает событие после срабатывания расписания. Одноразовый запуск действует в обратном порядке: сначала создается Btk_JobEvent, затем для него формируется триггер event_<eventId>.
Btk_JobEventLog и Btk_JobStats
Btk_JobEventLog хранит датированные сообщения события. Btk_JobStats хранит последнее событие и среднее время выполнения для отображения в списке заданий.
После завершения запуска система очищает старые события с учетом nPeriodStorageLogs и пересчитывает статистику.
Хранение и восстановление данных#
Данные менеджера имеют разное назначение:
Данные |
Назначение |
|---|---|
|
Исходная прикладная конфигурация |
|
История, состояние запусков и ожидающие одноразовые события |
|
Производная статистика для списка заданий |
|
Рабочее представление Quartz |
Плановые Quartz job и триггеры могут быть повторно сформированы из конфигурации заданий. Триггеры ожидающих одноразовых событий восстанавливаются по Btk_JobEvent. Поэтому резервная копия только таблиц btk_qrtz_* не заменяет резервную копию основной базы данных.
Периодичность, глубина хранения и расположение резервных копий определяются эксплуатационной политикой конкретного контура.
Использование в прикладном коде#
Расписания#
Btk_JobSchedule поддерживает пять типов периодичности:
|
Тип |
Техническое представление |
|---|---|---|
|
Однократно |
|
|
Ежедневно |
|
|
Еженедельно |
|
|
Ежемесячно |
|
|
Пользовательское |
|
Менеджер формирует Quartz Cron-выражение из отдельных полей расписания. Значение дня месяца 32 преобразуется в L, то есть последний день месяца. Последний день нельзя одновременно задавать вместе с конкретными днями месяца.
Основная модель Btk_JobSchedule хранит элементы расписания отдельными полями. JObject-модель Schedule и фасад Btk_SchedulePkg предназначены для механизмов, которые хранят расписание в JSON: они позволяют редактировать его элементы, сформировать Cron-выражение и рассчитать следующую дату выполнения. Они не изменяют Btk_JobSchedule и не заменяют основное расписание менеджера заданий.
Периодический Cron-триггер использует политику пропущенного запуска DoNothing: пропущенный момент не выполняется задним числом. Одноразовый триггер события использует FireNow и выполняется при первой возможности.
Справка по Quartz Cron
Для низкоуровневых интеграций Quartz использует выражение из полей секунды минуты часы день_месяца месяц день_недели; поле года является необязательным. В стандартном интерфейсе менеджера выражение формируется автоматически из полей расписания, поэтому вводить его вручную не требуется.
Примеры выражений Quartz:
Выражение |
Результат |
|---|---|
|
каждые пять минут |
|
каждые пять минут на десятой секунде |
|
в 10:30, 11:30, 12:30 и 13:30 по средам и пятницам |
|
каждые 30 минут с 08:00 до 09:30 пятого и двадцатого числа месяца |
Символ ? означает отсутствие конкретного значения для дня месяца или дня недели. Символ L обозначает последнее значение поддерживаемого периода, например последний день месяца. Выражения для прямого создания Quartz-триггеров применяйте только в специальных интеграциях; для обычного задания используйте Btk_JobScheduleApic. Стандартное расписание менеджера не предоставляет отдельную настройку произвольного интервала и количества повторов SimpleTrigger.
Календари Quartz
Quartz Calendar позволяет исключать даты из расписания, например праздничные дни. В прикладном пакете Btk_QuartzPkg предусмотрены операции добавления, получения и удаления календарей. Календарь применяется к низкоуровневому Quartz-триггеру и не заменяет расписание Btk_JobSchedule в стандартном менеджере заданий.
Шаблоны расписаний
Для прикладного кода используйте Btk_JobScheduleApic. Контракт предоставляет создание произвольного шаблона и типовые варианты:
createTemplate;templateOnceAMinute;templateMinuteInterval;templateOnceADay.
Пример ежедневного запуска в 01:00:
val scheduleTemplate = Btk_JobScheduleApic().templateOnceADay(
hours = 1,
mins = 0,
secs = 0
)
templateMinuteInterval формирует список минут внутри каждого часа. Если значение интервала не делит 60 без остатка, расстояние между последним запуском одного часа и первым запуском следующего часа будет отличаться от заданного интервала.
Не создавайте Quartz-триггер вручную для регистрации обычного задания менеджера.
Регистрация задания#
Поддерживаемая межмодульная точка регистрации — Btk_JobApic().registerSimpleJob.
val scheduleTemplate = Btk_JobScheduleApic().templateOnceADay(
hours = 1,
mins = 0,
secs = 0
)
Btk_JobApic().registerSimpleJob(
sSystemName = "Module_Recalculate".ns,
sCaption = "Пересчет данных модуля".ns,
sAction = "Module_RecalculatePkg().exec();".ns,
scheduleTemplate = scheduleTemplate,
sDescription = "Регулярный пересчет данных".ns,
sGroupName = "Module".ns
)
Если задание с таким системным именем отсутствует, регистрация создает его и добавляет расписание. Если задание уже существует и bRefresh = false, метод возвращает его идентификатор без обновления настроек.
При bRefresh = true метод обновляет поля задания, удаляет существующие расписания и создает одно расписание из переданного шаблона. Используйте этот режим только тогда, когда регистрационный код должен быть источником конфигурации расписания.
Дополнительные параметры контракта позволяют задать срок хранения журналов, тип задания, регистрацию без расписания, лимит внеплановых потоков и исходный объект.
Используйте стабильное системное имя. Не применяйте в новом коде Btk_QuartzPkg.getUniquePostfix, прямые TaskBuilder, withUnscheduledThreadLimit и другие исторические низкоуровневые примеры.
Одноразовый запуск#
Btk_JobApic().createOneTimeTask создает событие и возвращает его идентификатор:
val idvEvent = Btk_JobApic().createOneTimeTask(
rpJob = ropvJob,
mapParams = Map("documentId" -> idpDocument),
bpConcurrent = true,
dpStartDate = dpStartDate,
idpExecUser = idpUser,
bpRestart = 1.nr,
npRestart = 3.nr,
spGroup = spQueueGroup
)
dpStartDate задает момент, не ранее которого может начаться запуск. bpRestart разрешает повторные попытки, а npRestart задает их оставшееся количество. spGroup используется для распределения конкурентных запусков по группам очереди.
Для события создается триггер event_<eventId>. Для неконкурентного запуска идентификатор Quartz job равен идентификатору Btk_Job; для конкурентного используется concurrent_<jobId>.
Если idpExecUser не передан, одноразовый запуск выполняется от имени текущего пользователя, создавшего событие. Переданный пользователь применяется при корректно настроенной аутентификации планировщика.
Выключенное задание нельзя запустить одноразово. Операция «Выполнить задачу» создает новое событие и триггер, но не создает новый Btk_Job.
Для отмены используйте контракт:
Btk_JobApic().deleteOneTimeTask(idvEvent)
Параметр bpDeleteExecuting позволяет явно разрешить остановку уже выполняющегося события. Не удаляйте запись Quartz напрямую.
createOneTimeTaskWithUser устарел и не должен использоваться в новом коде.
Параметры одноразового запуска
mapParams сериализуется в JSON-поле jParams. Текущая реализация преобразует каждое значение в строку, а при разборе возвращает Map[String, NString].
Не рассчитывайте на сохранение исходного Scala-типа. Преобразуйте значение в ожидаемый тип явно после чтения параметров текущего события.
Фоновые процедуры#
Btk_BackgroundTaskPkg использует системное задание RunProcInBackground. Фасад:
Формирует JEXL и параметры фоновой операции.
Создает одноразовый запуск.
Выполняет JEXL от имени заданного пользователя.
Сохраняет результат или стек ошибки в отдельном журнале фоновой операции.
После завершения отправляет уведомление через Rabbit.
Используйте этот фасад, если вызывающей стороне нужны отдельный результат фоновой операции и уведомление. Не создавайте собственный Quartz job для каждой фоновой процедуры.
Выполнение и восстановление#
Плановое выполнение#
Плановый запуск проходит следующие этапы:
Менеджер синхронизирует включенное задание и его активные расписания с Quartz.
jobschedulerполучает из JobStore триггер, расчетное время которого наступило.Планировщик формирует SOAP-запрос
executeJexlк ApplicationServer.Запрос вызывает
Btk_JobApi.execJob(jobId, scheduleId).execJobавтономно создаетBtk_JobEventв состоянии «Запланировано».Btk_JobEventApi.execEventпроверяет, что событие можно выполнять.Система определяет Quartz job и триггер, проверяет доступный интервал и переводит событие в состояние «Выполняется».
При включенном
bEnabledTraceактивируется трассировка серверных методов для текущего события.Обработчик типа задания выполняет JEXL-скрипт или ветку очистки.
Результат, дата окончания, сообщение ошибки и параметры повторного запуска сохраняются автономно.
Система очищает устаревшие события и пересчитывает статистику.
Обычный JEXL-скрипт получает параметр idJob с идентификатором текущего задания:
idJob -> идентификатор Btk_Job
Если момент запуска не входит в доступные интервалы задания, прикладной скрипт не выполняется, а событие завершается ошибкой.
Журналирование и транзакции#
Внутренний метод Btk_JobEventLogApi().writeLog сохраняет сообщение в текущей транзакции менеджера заданий. Он не входит в межмодульный контракт.
Для прикладного межмодульного кода доступна автономная запись:
Btk_JobEventLogApic().writeLogLT(
"Обработка документа завершена".ns
)
writeLogLT создает запись в автономной транзакции и по умолчанию фиксирует ее отдельно. Используйте его, только если сообщение должно сохраниться после отката основной операции.
В JEXL-скрипте задания можно использовать оба метода:
Btk_JobEventLogApi.writeLog("Транзакционная запись 1");
Btk_JobEventLogApi.writeLogLT("Логирующая запись 1");
session.commit();
Btk_JobEventLogApi.writeLog("Транзакционная запись 2");
Btk_JobEventLogApi.writeLogLT("Логирующая запись 2");
session.rollback();
После выполнения скрипта в журнале события появятся следующие сообщения:
Транзакционная запись 1— запись зафиксирована вызовомsession.commit();Логирующая запись 1— запись сохранена в автономной транзакции;Логирующая запись 2— запись сохранена в автономной транзакции, несмотря на последующийsession.rollback().
Записи Транзакционная запись 2 в журнале не будет, поскольку она относится к отмененной транзакции.
Кроме пользовательских сообщений, менеджер заданий автоматически записывает начало и окончание выполнения события. Поэтому в журнале также отображаются записи Начало выполнения и Выполнение закончено. Порядок транзакционной и автономной записей может отличаться от порядка вызовов в скрипте: автономная транзакция фиксируется независимо от основной.
Вызов методов из прикладного Scala-кода также возможен, если код выполняется в контексте события задания. Для межмодульного кода используйте контракт Btk_JobEventLogApic().writeLogLT. Если метод вызван вне задания и для него не определено текущее событие, запись в журнал выполнения не создается.
Значимые границы транзакций:
событие запуска создается автономно;
результат и критическая ошибка события сохраняются автономно;
JEXL планового задания фиксирует выполненную прикладную работу;
автономный журнал может сохраниться при откате основной операции;
остановка серверной сессии может откатить ее незавершенную транзакцию.
Внимание
Ошибка после промежуточной фиксации не отменяет данные, которые задание уже сохранило. Автоматическая или ручная повторная попытка начинает обработку заново и может повторно изменить эти данные. Проектируйте задание идемпотентным либо перед каждым изменением проверяйте, была ли соответствующая часть обработки уже выполнена.
Восстановление и остановка#
Системное задание Btk_RestartJobEvent каждые пять минут сопоставляет события с рабочими сессиями и триггерами. Оно:
завершает ошибкой событие «Выполняется», если связанная сессия отсутствует;
восстанавливает пропавший триггер запланированного одноразового события;
создает повторную попытку, если разрешен перезапуск и значение
nRestartположительное.
Задание восстановления само зависит от работающего планировщика. При полной остановке jobscheduler восстановление начнется только после возобновления его работы.
Остановка выполняющегося события ищет рабочую сессию во всем кластере и завершает ее. Отмена запланированного одноразового события удаляет триггер и переводит событие в состояние «Остановлено».
Безопасность#
Основной способ аутентификации использует JWT типа scheduler: jobscheduler подписывает его приватным ключом, а ApplicationServer проверяет публичным ключом. Устаревшие конфигурации могут использовать логин и пароль; защищенное хранилище требуется только для зашифрованного пароля.
idExecUser задания и idpExecUser одноразового запуска определяют пользовательский контекст выполнения. Планировщик является доверенным системным компонентом, способным инициировать выполнение от имени выбранного пользователя, но прикладные полномочия этого пользователя продолжают ограничивать доступные операции.
Практические правила:
назначайте отдельного пользователя с минимально необходимыми полномочиями;
ограничивайте доступ к ключам,
cQuartzProps,sScriptи выбору пользователя выполнения;не записывайте пароли, токены и ключи в скрипт,
jParamsи журнал события;проверяйте входные параметры до выполнения привилегированной операции;
не утверждайте конкретные роли без проверки полномочий целевой базы данных.
Ограничения#
Несколько экземпляров с общим JobStore должны использовать один согласованный механизм блокировки: REST-lock или PostgreSQL advisory lock.
В каждый момент право выполнения заданий принадлежит одному master-процессу; дополнительные процессы повышают отказоустойчивость, но не увеличивают пропускную способность одной очереди.
Отдельные пулы конкурентных заданий увеличивают суммарное число потоков процесса; их лимиты необходимо согласовывать с ресурсами ApplicationServer и PostgreSQL.
Очереди конкурентных групп находятся в памяти процесса и не сохраняют прежний порядок после перезапуска.
Статистика
SchedulerStatMonitorи сведения о расхожденияхJobThreadWatcherнаходятся в памяти и сбрасываются после перезапуска процесса.GsfWebServerне выполняет встроенную аутентификацию запросов. Ограничивайте доступ к его диагностическому порту средствами инфраструктуры контура.Пропущенный периодический запуск не восстанавливается задним числом.
Параметры одноразового запуска сохраняются как строки.
Повторная попытка не отменяет данные, ранее зафиксированные прикладным скриптом.
Конкретный unit-файл и команда установки
jobschedulerзависят от эксплуатационной поставки; планировщик поддерживает CLI-режимы, Linux- и Windows-службы.