Skip to main content

Планировщик задач DocScheduler (CRON_TABLE)

Планировщик DocScheduler предназначен для периодического запуска задач (CSP‑сервисов или DB‑функций) по расписанию, задаваемому с помощью SQL‑выражений. Управление задачами осуществляется через документы типа CRON_TABLE.

Планировщик автоматически отслеживает выполнение запущенных процессов, фиксирует ошибки, рассчитывает следующее время запуска и при необходимости переводит задачу в неактивное состояние (по превышению лимита ошибок или невалидному расписанию).


Документ CRON_TABLE: поля и их назначение

Для создания задачи необходимо добавить документ с dockind = "CRON_TABLE" и статусом "CRON_TABLE_ACTIVE".
Поддерживаются следующие свойства:

Поле Тип Описание
NUM число/строка Идентификатор (номер) задачи.
DESCR строка Описание задачи (назначение).
SYS_PROCESS_ID число ID запущенного системного процесса (заполняется автоматически).
DB_FUNCTION строка Имя DB‑функции для вызова (если не используется CSP).
CSP_SERVICE строка Имя CSP‑сервиса для вызова.
CSP_SERVICE_PARAMS строка (JSON) Параметры вызова CSP‑сервиса в формате JSON.
INTERVAL строка SQL‑выражение, вычисляющее следующую дату/время запуска (см. ниже).
INTERVAL_ERROR строка SQL‑выражение для следующего запуска после ошибки (если не задано, используется INTERVAL).
MAX_COUNT_ERROR число Максимальное количество последовательных ошибок, после которого задача переводится в статус CRON_TABLE_ERROR.
COUNT_ERROR число Текущий счётчик последовательных ошибок (заполняется автоматически).
FIRST_RUN datetime Первый запланированный запуск (опционально, используется для проверки).
LAST_RUN datetime Время последнего запуска (заполняется автоматически).
NEXT_RUN datetime Следующее время запуска (рассчитывается автоматически).
DAILY_START_TIME datetime Время фактического начала выполнения задачи (заполняется автоматически).
DAILY_END_TIME datetime Время окончания выполнения задачи (заполняется автоматически).
ERROR_TEXT строка Текст последней ошибки (заполняется автоматически).
PERIODICAL_RUN не используется (зарезервировано)
DEL_ON_FINISH не используется (зарезервировано)

Важно: Для запуска задачи необходимо указать либо CSP_SERVICE (с возможными CSP_SERVICE_PARAMS), либо DB_FUNCTION.


Принцип работы планировщика

1. Загрузка задач

При старте и далее каждые 10 минут или при изменениях в CRON_TABLE (таймер reloadTimer) планировщик перечитывает все активные документы CRON_TABLE_ACTIVE.
Также перезагрузка происходит при любом изменении статуса системного процесса (через триггер trgProcessChanged) — например, после завершения задачи.

2. Цикл проверки расписания

Каждые 30 секунд (таймер checkTimer) планировщик:

  • Собирает задачи, у которых NEXT_RUN <= текущего времени и SYS_PROCESS_ID отсутствует (т.е. процесс не запущен).
  • Запускает полную перезагрузку задач и их обработку, при наличии собранных задач

3. Обработка задачи (_processTask)

Для каждой активной задачи выполняется логика:

Если NEXT_RUN в будущем → пропустить
Иначе если SYS_PROCESS_ID отсутствует → запустить задачу (_runTask)
Иначе если процесс с таким ID не найден в sysprocs → запустить задачу заново
Иначе если процесс завершён с ошибкой → вызвать _finishTask с текстом ошибки
Иначе если процесс завершён успешно → вызвать _finishTask без ошибки
Иначе процесс ещё выполняется → ничего не делать

4. Запуск задачи (_runTask)

  • Создаётся новый системный процесс:
    • тип csp – для вызова CSP‑сервиса (параметры берутся из CSP_SERVICE_PARAMS);
    • тип db – для вызова DB‑функции.
  • В документе CRON_TABLE проставляется SYS_PROCESS_ID и текущее время в DAILY_START_TIME.
  • После этого планировщик перезагружает списки задач и процессов.

Если на этапе запуска возникает исключение, вызывается _finishTask с текстом ошибки.

5. Завершение задачи / расчёт следующего запуска (_finishTask)

Шаги:

  1. Определение интервала

    • Если есть ошибка → используется INTERVAL_ERROR (или INTERVAL, если INTERVAL_ERROR не задан).
    • Если ошибки нет → используется INTERVAL.
  2. Обновление счётчика ошибок

    • При ошибке: COUNT_ERROR увеличивается на 1.
    • При успехе: COUNT_ERROR сбрасывается в 0.
  3. Проверка лимита ошибок

    • Если COUNT_ERROR > MAX_COUNT_ERROR → задача переводится в статус CRON_TABLE_ERROR и больше не запускается.
  4. Расчёт следующего времени запуска

    • Выполняется SQL‑запрос: SELECT (<INTERVAL_выражение>) as RESULT.
    • Результат преобразуется в dayjs‑объект.
    • Если выражение пустое, результат невалиден или запрос не удался → задача переводится в статус CRON_TABLE_PASSIVE (отключена).
    • Иначе NEXT_RUN устанавливается на вычисленную дату/время.
  5. Обновление документа

    • Сбрасывается SYS_PROCESS_ID, проставляется DAILY_END_TIME, ERROR_TEXT, новый NEXT_RUN и COUNT_ERROR.
    • При необходимости изменяется статус документа (на CRON_TABLE_ERROR или CRON_TABLE_PASSIVE).
    • Выполняется перезагрузка списков задач и процессов.

Настройка расписания (INTERVAL и INTERVAL_ERROR)

Планировщик не использует классические cron‑выражения. Вместо этого для вычисления следующей даты запуска применяются произвольные SQL‑выражения, возвращающие timestamp.

Формат

Выражение должно быть валидным SQL‑выражением для СУБД, на которой работает MorphCluster. Рекомендуется использовать NOW() как точку отсчёта.

Примеры

Цель SQL‑выражение для INTERVAL
Каждые 5 минут NOW() + INTERVAL '5 minutes'
Каждый час NOW() + INTERVAL '1 hour'
Ежедневно в 03:00 DATE_TRUNC('day', NOW()) + INTERVAL '1 day' + INTERVAL '3 hours'
Каждый понедельник в 09:00 NEXT_DAY(NOW(), 'MONDAY') + INTERVAL '9 hours' (зависит от диалекта SQL)
Через 1 день после успешного выполнения NOW() + INTERVAL '1 day'

Особенность при пустом INTERVAL

Если INTERVALINTERVAL_ERROR) не заданы, то:

  • После успешного выполнения задачи NEXT_RUN останется NULL.
  • При следующем цикле проверки планировщик будет считать, что NEXT_RUN уже наступил (условие task.NEXT_RUN && task.NEXT_RUN.isAfter(...) не сработает, т.к. NEXT_RUNnull).
  • В результате задача будет запускаться повторно сразу после предыдущего завершения (зацикливание).

Рекомендация: всегда задавайте явное INTERVAL или переводите задачу в неактивный статус вручную после разового выполнения.


Обработка ошибок и отказоустойчивость

  • Счётчик ошибок (COUNT_ERROR) увеличивается при любом сбое:
    • Ошибка запуска задачи (исключение в _runTask).
    • Завершение системного процесса со статусом failed.
  • Раздельный интервал после ошибки позволяет задать более частое повторение (например, NOW() + INTERVAL '5 minutes') или более длительную паузу.
  • Лимит ошибок (MAX_COUNT_ERROR): по достижении лимита задача автоматически деактивируется (статус CRON_TABLE_ERROR). Это предотвращает бесконечные попытки заведомо ошибочной задачи.
  • Невалидное расписание (ошибка вычисления NEXT_RUN или пустой INTERVAL) переводит задачу в статус CRON_TABLE_PASSIVE — она исключается из обработки до ручного вмешательства.

Жизненный цикл задачи (на примере)

  1. Создание
    Добавляется документ CRON_TABLE со статусом ACTIVE, заполняются поля CSP_SERVICE/DB_FUNCTION, INTERVAL, MAX_COUNT_ERROR (опционально).

  2. Первый запуск

    • Если NEXT_RUN не задан или NEXT_RUN <= текущего времени → планировщик запускает задачу.
    • SYS_PROCESS_ID получает ID нового системного процесса.
    • DAILY_START_TIME фиксирует момент старта.
  3. Выполнение
    Планировщик не вмешивается в ход работы процесса. Процесс выполняется асинхронно.

  4. Завершение процесса

    • Системный процесс переходит в статус completed или failed.
    • Триггер trgProcessChanged немедленно вызывает перезагрузку планировщика.
    • Планировщик обрабатывает завершённую задачу через _finishTask:
      • Рассчитывается NEXT_RUN (с учётом ошибки, если была).
      • Обновляются COUNT_ERROR, DAILY_END_TIME, ERROR_TEXT.
      • Сбрасывается SYS_PROCESS_ID.
      • При необходимости меняется статус документа.
  5. Повторный запуск
    Когда текущее время достигнет нового NEXT_RUN, планировщик снова запустит задачу.

  6. Отключение задачи

    • Автоматически: при превышении MAX_COUNT_ERROR или невалидном INTERVAL.
    • Вручную: изменить статус документа на любой, кроме CRON_TABLE_ACTIVE.

Особенности и ограничения

1. Механизм блокировки повторной перезагрузки

  • Используется флаг reloading и отложенный вызов needReload для предотвращения одновременной перезагрузки из разных таймеров/триггеров.

2. Сессия выполнения

  • Задачи запускаются от имени пользователя userId = 5056 с правами администратора (isAdmin: true).
  • Это зашито в коде и не настраивается через CRON_TABLE.

3. Поведение при NEXT_RUN = NULL

Как уже отмечено, это приводит к немедленному повторному запуску после завершения. Если вам нужно однократное выполнение, после успеха следует вручную перевести задачу в статус CRON_TABLE_PASSIVE или CRON_TABLE_ERROR (например, через отдельный процесс).

4. Типы вызываемых функций

  • CSP‑сервис – должен быть зарегистрирован в системе. Параметры передаются через CSP_SERVICE_PARAMS в виде JSON-строки.
  • DB‑функция – хранимая функция в базе данных. Вызов происходит без параметров (но можно передать через глобальные переменные сессии или отдельный механизм).

Рекомендации по настройке

  1. Всегда задавайте INTERVAL даже для периодических задач. Для разовых задач используйте ручное отключение или запланируйте удаление документа после выполнения.
  2. Указывайте MAX_COUNT_ERROR – разумное значение (например, 3–5) для защиты от «зависших» ошибочных задач.
  3. Используйте INTERVAL_ERROR, если после сбоя нужно повторить попытку быстрее, чем обычно.
  4. Проверяйте SQL‑выражения в консоли базы данных перед внесением в INTERVAL.
  5. Избегайте слишком частых запусков менее 30 секунд

Пример документа CRON_TABLE

{
  "dockind": "CRON_TABLE",
  "status": "CRON_TABLE_ACTIVE",
  "props": {
    "NUM": 101,
    "DESCR": "Ежечасная архивация логов",
    "DB_FUNCTION": "archive_logs",
    "INTERVAL": "NOW() + INTERVAL '1 hour'",
    "INTERVAL_ERROR": "NOW() + INTERVAL '5 minutes'",
    "MAX_COUNT_ERROR": 3,
    "FIRST_RUN": "2025-01-01T00:00:00"
  }
}

Этот документ заставит планировщик вызывать DB‑функцию archive_logs каждый час. При возникновении ошибки следующая попытка будет через 5 минут. После трёх последовательных ошибок задача перейдёт в статус CRON_TABLE_ERROR.


Заключение

Планировщик DocScheduler предоставляет гибкий механизм запуска задач по расписанию на основе SQL‑выражений. Настройка через документы CRON_TABLE позволяет динамически добавлять, изменять и отключать задачи без перезапуска сервиса. Важно правильно заполнять поля INTERVAL и контролировать обработку ошибок через MAX_COUNT_ERROR и INTERVAL_ERROR.