Перейти к содержимому

Timer Jobs

Timer Jobs

Задания по расписанию (аналог SharePoint Timer Jobs). Разработка пакетов — см. Timer Jobs Portal.

Назначение

Timer Job выполняет C#-код по расписанию (cron), без привязки к событиям домена. Типичные сценарии:

  • ночная очистка / архивация;
  • периодическая синхронизация с внешней системой;
  • отправка дайджестов;
  • постановка пакетных заданий в очередь portal_jobs.

Для реакции на изменения списков используйте Event Receivers.

Жизненный цикл

1. Админ устанавливает .portaltimer
→ timer_job_packages + timer_job_definitions
→ Worker перезагружает DLL (Valkey signal)
2. Админ создаёт экземпляр timer_jobs
→ scheduleType + scheduleConfig → cron_expression
→ config (параметры из configSchema)
3. CronSchedulerWorker (каждые ~60 с)
→ CronSchedulerService.TickTimerJobsAsync
→ если next cron <= now → portal_jobs (pending)
→ обновляет last_run_at
4. JobQueueWorker (каждые ~5 с)
→ JobProcessor → TimerJobPackageLoader.InvokeAsync
→ ITimerJob.ExecuteAsync
→ portal_jobs: completed / failed

Ручной запуск (POST /timer-jobs/{id}/run) пропускает шаг 3 и сразу ставит задание в очередь с manual: true.

Компоненты

КомпонентРасположениеРоль
CronSchedulerServicePortal.Application/Jobs/Преобразует cron → постановка jobs
TimerJobServicePortal.Application/TimerJobs/CRUD экземпляров, RunNow
TimerJobPackageLoaderPortal.Application/TimerJobs/Загрузка DLL из S3, вызов ITimerJob
TimerJobCatalogServicePortal.Application/TimerJobs/Установка/удаление пакетов
JobProcessorPortal.Application/Jobs/Маршрутизация job_type → handler
JobQueueWorkerPortal.WorkerОпрос очереди
CronSchedulerWorkerPortal.WorkerТик планировщика

Переменные Worker:

ПеременнаяПо умолчаниюОписание
WORKER_POLL_INTERVAL_MS5000Интервал опроса очереди
WORKER_BATCH_SIZE10Заданий за один тик
CRON_RELOAD_INTERVAL_MS60000Интервал тика cron-планировщика

Типы расписания

Админка и API принимают scheduleType + scheduleConfig. Платформа вычисляет cron (стандартный формат: мин час день месяц день_недели).

scheduleTypescheduleConfigCronПример
everyMinute{}* * * * *Каждую минуту
everyNMinutes{ "intervalMinutes": 5 }*/5 * * * *Каждые 5 мин (1–59)
hourly{ "minute": 30 }30 * * * *Каждый час в :30
daily{ "time": "09:30" }30 9 * * *Ежедневно в 09:30
weekly{ "daysOfWeek": [1,3,5], "time": "09:00" }0 9 * * 1,3,5Пн, ср, пт в 09:00
monthly{ "dayOfMonth": 15, "time": "09:00" }0 9 15 * *15-го числа
yearly{ "month": 3, "day": 15, "time": "12:00" }0 12 15 3 *15 марта в 12:00

Дни недели (daysOfWeek): 0 = воскресенье, 1 = понедельник, … 6 = суббота.

Время (time): строка HH:mm, локальный часовой пояс Worker.

Список типов: GET /api/v1/timer-jobs/schedule-types.

Примеры создания

Каждую минуту:

{
"title": "Heartbeat",
"definitionId": "uuid-обработчика",
"scheduleType": "everyMinute",
"scheduleConfig": {},
"config": { "message": "tick" },
"isEnabled": true
}

Еженедельно по будням в 08:00:

{
"scheduleType": "weekly",
"scheduleConfig": {
"daysOfWeek": [1, 2, 3, 4, 5],
"time": "08:00"
}
}

API (admin)

Базовый путь: /api/v1/timer-jobs. Требуется авторизация; операции с пакетами и экземплярами — только admin.

Пакеты

МетодПутьОписание
GET/packagesУстановленные пакеты
POST/packagesmultipart package — файл .portaltimer (≤ 5 МБ)
DELETE/packages/{id}Удалить пакет, определения и связанные экземпляры

Каталог обработчиков

МетодПутьОписание
GET/definitionsСписок (?admin=true — включая выключенные)
PATCH/definitions/{id}{ "isEnabled": true/false }
GET/job-typesТипы для постановки jobs (из пакетов)

Экземпляры заданий

МетодПутьОписание
GET/Все экземпляры
GET/{id}Один экземпляр
POST/Создать (см. CreateTimerJobRequest)
PATCH/{id}Обновить title, schedule, config, isEnabled
DELETE/{id}Удалить
POST/{id}/runРучной запуск → { enqueued, jobId }

Справочники

МетодПутьОписание
GET/schedule-typesТипы расписания для UI

Тело создания экземпляра

{
"title": "Ночная синхронизация",
"definitionId": "uuid-из-timer_job_definitions",
"scheduleType": "daily",
"scheduleConfig": { "time": "02:00" },
"config": {
"message": "nightly run",
"dryRun": false
},
"isEnabled": true
}

Ответ включает вычисленные поля:

{
"id": "...",
"cronExpression": "0 2 * * *",
"scheduleDescription": "Ежедневно в 02:00",
"lastRunAt": null,
"jobType": "contoso.heartbeat",
"serviceKey": "timer-extensions"
}

Админка

Вкладка Timer Jobs (adminTimerJobsPanel.js):

  1. Пакеты — загрузка .portaltimer, список установленных, удаление
  2. Каталог обработчиков — включение/выключение definition
  3. Задания по расписанию — создание, вкл/выкл, ручной Запуск, удаление

Форма создания задания:

  • выбор обработчика из каталога;
  • тип расписания + параметры (время, дни недели, интервал);
  • поля config по configSchema из manifest.

Служба timer-extensions

При установке первого пакета создаётся служба timer-extensions в portal_services. Она должна быть включена (по умолчанию — да).

Проверка: Админка → Службы или GET /api/v1/services.

Отладка

1. Задание не запускается по расписанию

  • Экземпляр isEnabled: true?
  • Definition isEnabled: true?
  • Worker запущен? docker compose ps worker
  • Корректный cron? Смотрите cronExpression в ответе API
  • lastRunAt обновляется? Если нет — смотрите логи CronSchedulerWorker

2. Задание в очереди, но failed

  • Админка → Заданияerror_message
  • Логи Worker: docker compose logs worker
  • Ошибка загрузки DLL: TimerJobPackageLoader пишет в лог при старте Worker
  • Переустановите пакет с увеличенной version

3. Ручная проверка

Окно терминала
# Установка + создание + run (полный цикл)
./scripts/e2e-timer-jobs-test.sh

4. Логи обработчика

api.Log.Info("debug:", someValue);

В логах Worker: [timer-job] debug: ...

Цепочка с очередью jobs

Timer Job может инициировать другие задания:

await api.Jobs.EnqueueAsync("audit", "audit.logEvent", new { action = "nightly" });

Полезно, когда по расписанию нужно запустить тяжёлую или длительную работу отдельным job type (в т.ч. Event Receiver из пакета .portalevent).

См. Job-модули — статусы, повторы, POST /jobs/{id}/retry.

Сравнение с cron служб

В Portal есть два механизма cron:

Timer Jobs (.portaltimer)Службы (portal_services)
КонфигурацияАдминка → Timer JobsАдминка → Службы
КодC# DLL в пакетеВстроенные handlers Worker
Примерыcontoso.heartbeatad-sync, search-index
API в handlerLog + Enqueue jobsПолный доступ службы

Встроенные cron-службы описаны в Service Framework.

Документация для разработчиков