Skip to content

⏰ Scheduler (демон) ​

📌 Описание ​

Планировщик задач внутри демона: выполнение операций по расписанию (cron-синтаксис) без участия платформы. Исполняет две вещи — бекапы из политик приложений и собственный список задач (asc schedule): рестарт ноды, старт/стоп/рестарт/обновление приложения, бекап, shell-команда и HTTP-запрос, с журналом запусков. Платформа кладёт сюда расписания с исполнителем «машина» — они срабатывают, даже когда связи с платформой нет.

🎯 Сценарии использования ​

  • ⏰ Ежедневный бекап в 03:00 в S3 с ротацией 7 копий — политика приложения или задача asc schedule add daily@03:00 backup --app db --storage s3-main --keep 7.
  • 🌐 Раз в 5 минут проверять внутренний адрес, доступный только с ноды: asc schedule add "*/5 * * * *" http --url http://127.0.0.1:8080/health.
  • 🧩 Пользовательская cron-задача: asc schedule add "0 5 * * *" shell --command ./cleanup.sh --app web — в каталоге приложения от имени его владельца.
  • 🔁 Ночной перезапуск приложения или ноды, обновление приложения до свежей версии.
  • ☁️ Расписание, созданное в панели с переключателем «Выполнять на машине», появляется здесь как задача managed_by = "platform".

🏗️ Техническое решение ​

Реализован в src/daemon/scheduler/ (mod.rs — синтаксис и цикл, jobs.rs — задачи), запускается из главного цикла демона.

Исполнитель ​

  • Просыпается в начале каждой минуты; повторы в пределах одной минуты (быстрый цикл, скачок часов) дедуплицируются по минуте настенных часов.
  • Бекапы из политик — отдельный проход в блокирующем пуле, ошибки логируются по каждому приложению отдельно.
  • Каждая подошедшая задача запускается в своей блокирующей задаче — получасовой бекап не задерживает HTTP-проверку той же минуты. Задача, чей прошлый запуск ещё идёт, пропускает тик (ещё один экземпляр поверх себя не стартует); тот же запрет действует для ручного запуска через API.

Синтаксис расписаний (Schedule::parse) ​

hourly (минута 0 каждого часа — пресет платформы), daily@HH:MM, просто HH:MM (то же, что daily) или cron из пяти полей минута час день_месяца месяц день_недели с *, значениями, диапазонами a-b, списками a,b,c и шагами /n; день недели 0–7 (0 и 7 — воскресенье). Когда ограничены оба поля даты, действует правило vixie-cron: срабатывает любое из двух. Время — локальное время ноды; задача с флагом utc считается по UTC (так платформа передаёт свои расписания — её next_run_at тоже в UTC). Schedule::next_after вычисляет следующий запуск (пропуская заведомо неподходящие часы и дни), None — если в пределах года запусков нет.

Задачи (jobs.rs) ​

ДействиеЧто делает
node_rebootsystemctl reboot через 3 секунды — запуск успевает записаться
app_start / app_stop / app_restartкак asc app start/stop/restart
app_updateобновление до последней версии; запущенное приложение останавливается и после обновления запускается снова (в том числе если обновление не удалось)
backupбекап в указанные хранилища (пусто — политика приложения, иначе local) с ротацией keep; архив собирается один раз и отправляется в каждое хранилище
shell/bin/sh -c, stdout и stderr вперемешку в порядке записи; с app — в каталоге приложения от имени его владельца (ASC_APP_ID, ASC_APP_DIR в окружении), без — в / от пользователя демона; по таймауту убивается вся группа процессов
httpGET/POST/PUT/PATCH/DELETE/HEAD с заголовками и телом; ответ ≥ 400 — ошибка запуска
  • Хранение: <data_dir>/schedules.json (0600 — HTTP-задача может нести Authorization), журнал — <data_dir>/schedule-runs.json: последние 50 запусков на задачу, вывод и ошибка обрезаются до 64 КиБ. Файлы переписываются целиком через временный файл и rename под эксклюзивным flock на schedules.lock — CLI, правящий список, и демон, пишущий запуск, не перемешиваются.
  • Валидация: id — [A-Za-z0-9._-]{1,64}, синтаксис триггера, непустые приложение/команда/URL (http:// или https://), таймаут 1–3600 с (по умолчанию 600), keep ≥ 1.
  • Владелец задачи (managed_by): задачи оператора — без метки; платформа пишет свои с managed_by = "platform" через ReplaceManagedSchedules — полную замену только своих задач. Задачу чужого владельца нельзя заменить ни upsert'ом, ни push'ем, а пересечение id с задачей оператора отклоняет push целиком (ничего не записывается).

API (ScheduleService, capability schedules) ​

ListSchedules (с next_run_unix и последним запуском), UpsertSchedule, RemoveSchedule, ReplaceManagedSchedules, RunSchedule (запуск вне расписания; отвечает открытым запуском со статусом RUNNING, не дожидаясь конца), ListScheduleRuns. REST-зеркало: GET /v1/schedules, PUT/DELETE /v1/schedules/{id}, PUT /v1/schedules/managed/{managed_by}, POST /v1/schedules/{id}/run, GET /v1/schedules/{id}/runs (🔌 api).

CLI ​

asc schedule list | add <расписание> <действие> [флаги] | remove | enable | disable | run | runs. run выполняет задачу на переднем плане и печатает вывод; правка задачи платформы разрешена, но CLI предупреждает, что следующая синхронизация её перезапишет. Файлы задач принадлежат root — на системной установке команды выполняются через sudo.

📝 Следующие инкременты ​

  • Одноразовые отложенные задачи; приоритеты и сериализация взаимоисключающих операций над одним приложением; автообновление демона, ожидающее пустую очередь.

🔗 Связанные задачи ​

DMN-012, DMN-114, DMN-009, TASK-001, TASK-003, NODE-050, BE-005 в ROADMAP.md.

Распространяется по лицензии MIT.