Планировщик

Вы можете пропустить этот раздел при первом знакомстве с системой.

Планировщик выполняет периодические задачи: раз в минуту он просыпается, отбирает задачи, чьё расписание совпало с текущей минутой, и отправляет их в пул из пяти потоков (значение по-умолчанию).

Задачи задаются двумя способами:

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

  • в конфигурации сервера ключами scheduler.task.*.

Оба набора выполняет один и тот же планировщик, в одном пуле потоков.

Одну и ту же задачу нельзя держать одновременно в оснастке и в конфигурации — она будет выполнена дважды за минуту. При переносе задачи из конфигурации в оснастку блок scheduler.task.* нужно удалить.

Планировщик целиком выключается параметром scheduler.start=0. Это делается на тех инстансах, где периодические задачи выполнять не нужно.

В планировщике может быть запущен любой объект класса, реализующий интерфейс java.lang.Runnable. Класс, который дополнительно реализует ru.bgcrm.core.scheduler.task.ScheduledTask, попадает в список выбора при создании задачи. Класс может быть статическим либо динамическим. Для разового запуска произвольного класса есть также запуск класса.

Оснастка «Планировщик»

Список задач

Администрирование⇒Планировщик показывает список задач. В строке видно название, класс, расписание словами («Пн–Пт, 09–18, каждые 15 мин», «каждую минуту», «09:05, 21:05»), время следующего запуска и результат последнего с его длительностью.

Отметки в строке сообщают о состоянии задачи:

  • выполняется на <сервере> — задача работает прямо сейчас, в отметке назван сервер, который её взял;

  • запуск запрошен — нажата кнопка разового запуска, планировщик заберёт задачу в течение минуты;

  • класс не найден — класс задачи отсутствует, запускать нечего. Такую задачу нужно либо починить, либо выключить;

  • выключена — задача по расписанию не запускается, но её можно запустить вручную.

Фильтры над списком:

Таблица. Фильтры списка

Фильтр Что отбирает

Все, Включённые, Выключенные

Состояние задачи.

Сломанные

Класс задачи не найден — запускать нечего.

Выполняются

Идут прямо сейчас, на любом сервере кластера.

Удалённые

Удалённые задачи, их можно восстановить.

Результат последнего запуска

Успешно, ошибка, пропущено либо «не запускалась».

Длительность последнего запуска

Запуски дольше выбранного порога — так находятся потяжелевшие задачи.

Расписание

Вид расписания: каждую минуту, ночные, еженедельные, ежемесячные.

Поиск над фильтрами ищет подстроку по названию, классу и комментарию.

Создание и редактирование задачи

Кнопка Добавить задачу открывает форму; она же открывается кнопкой правки в строке. Обязательны два поля — название и класс, остальное можно оставить пустым.

Форма разбита на четыре части по вопросам, на которые они отвечают: «Что запускать» — название, класс, настройки и комментарий; «Когда запускать» — маски расписания и политика пропущенного запуска; «Где выполнять» — сервер и таймаут; «Что при ошибке» — повторы. Последние две свёрнуты, пока в них ничего не задано, а в их заголовке видна текущая настройка, например «любой инстанс, без таймаута». Над масками показано расписание словами, под ними — три ближайших запуска; они считаются по часам браузера и нужны как подсказка, а не как сверка с сервером.

Таблица. Поля задачи

Поле Назначение

Название

Произвольное, видно в списке и в журналах. До 255 символов.

Класс задачи

Полное имя класса. Вписывается вручную либо выбирается кнопкой «Выбрать», см. Выбор класса. До 255 символов.

Дни месяца, Дни недели, Часы, Минуты

Маски расписания, см. ниже.

Выполнять на инстансе

Идентификатор сервера — значение его server.instance.id. Пусто — задачу возьмёт любой сервер с включённым планировщиком.

Таймаут, секунд

Запуск дольше указанного прерывается. Пусто — без предела.

Пропущенный запуск

Что делать, если в нужную минуту сервер был недоступен: «Забыть» или «Выполнить один раз».

Повторов при ошибке

Сколько раз повторить упавший запуск, не дожидаясь расписания. Пусто или 0 — не повторять.

Пауза перед повтором, секунд

Пауза перед первым повтором, дальше удваивается. По умолчанию 60.

Настройки задачи

Параметры вида ключ=значение, по одной паре в строке.

Комментарий

Зачем задача нужна. Стоит заполнять: через полгода это единственный источник ответа.

Поля «Настройки задачи» и «Комментарий» свёрнуты, пока пусты, и сворачиваются обратно, если уйти из пустого поля.

Задача создаётся включённой. Если расписание ещё не проверено, безопаснее сразу выключить её переключателем в строке и запустить один раз вручную.

Выбор класса

Поле «Класс задачи» — обычное текстовое поле с полным именем класса. Кнопка «Выбрать» рядом с ним открывает окно со списком готовых задач, как у поля «Класс-обработчик» в модуле задач. В список попадают классы, реализующие ru.bgcrm.core.scheduler.task.ScheduledTask, — и из сборки, и из динамического кода. Классы сборки находятся сканированием пакета ru.bgcrm при первом открытии окна, поэтому новая задача появляется в списке без дополнительной регистрации. Абстрактные классы, интерфейсы, вложенные и непубличные классы в список не попадают — планировщик не может их запустить.

Классы в окне сгруппированы по пакетам, у каждого пакета показано число классов. Если класс в поле уже есть в списке, его пакет раскрыт сразу, а сам класс выделен. У класса может быть аннотация SchedulerTaskInfo с названием и описанием: название выводится рядом с именем класса, описание — во всплывающей подсказке. Поиск ищет подстроку в полном имени класса и в названии без учёта регистра, совпадения выделяются. Щелчок по классу подставляет его полное имя в поле и закрывает окно, «Отмена» закрывает окно без изменений.

Класса без ScheduledTask в списке нет, но планировщик его запустит — такой класс вписывается в поле вручную.

Чтобы новая задача появилась в списке с понятным названием, её класс реализует ScheduledTask (или наследует ConfigurableTask, если нужны настройки) и помечается аннотацией SchedulerTaskInfo:

@SchedulerTaskInfo(title = "Рассылки: отправка сообщений",
    description = "Отправляет неотправленные сообщения рассылок подписчикам")
public class MessageSender implements ScheduledTask {
    @Override
    public void run() {
        ...
    }
}

Описание необязательно, оно показывается во всплывающей подсказке у класса.

Расписание

Расписание задаётся четырьмя масками — днями месяца, днями недели, часами и минутами.

Таблица. Маски расписания

Маска Диапазон Пример

Дни месяца

1..31

1,15 — первого и пятнадцатого числа

Дни недели

1..7, 1 — понедельник

1,5 — по понедельникам и пятницам

Часы

0..23

9,21 — в девять утра и в девять вечера

Минуты

0..59

0,30 — в начале часа и в половине

Значения перечисляются через запятую. Пустая маска не ограничивает запуск: задача с пустыми часами и минутами 0 выполнится в начале каждого часа, а задача, у которой пусты все четыре маски, будет выполняться каждую минуту.

Кроме отдельных значений маска понимает диапазоны и шаги:

Таблица. Синтаксис маски

Запись Что означает

1,15

Отдельные значения.

9-18

Диапазон включительно: 9, 10, … 18.

*

Всё допустимое для маски, то же, что пустое значение.

*/5

Каждое пятое от начала диапазона: 0, 5, 10 и так далее.

9-18/2

Каждое второе внутри диапазона: 9, 11, 13, 15, 17.

30/10

От значения до конца диапазона с шагом: 30, 40, 50.

Записи можно смешивать: 0,9-18,30/10. Маска из одних чисел сохраняется отсортированной и без повторов, маска с диапазонами — в том виде, как написана.

Под масками форма показывает подсказку: пустое расписание она отмечает предупреждением «Все маски пусты — задача будет запускаться каждую минуту». Неверную маску сохранить нельзя, поле подсвечивается ошибкой: выход за диапазон, перевёрнутый диапазон 18-9, нулевой шаг */0 и нечисловой мусор отклоняются.

Диапазоны и шаги работают и в оснастке, и в конфигурации. Прежние маски из перечисления чисел продолжают работать без изменений.
Секунды планировщик не поддерживает — минута является наименьшим интервалом. Ключ seconds в старых конфигурациях встречается, но никогда не читался.

Настройки задачи

Поле «Настройки задачи» хранит произвольные параметры в формате ключ=значение, по одной паре в строке. Они передаются задаче, если её класс наследует ru.bgcrm.core.scheduler.task.ConfigurableTask (он и сам реализует ScheduledTask). Обычный Runnable настройки не получает — для него поле бесполезно.

Включение и выключение

Переключатель в строке. Выключенная задача остаётся в списке со всей историей, но по расписанию не запускается. Разовый запуск для неё по-прежнему доступен — так удобно проверять задачу, не отдавая её расписанию.

Разовый запуск

Кнопка запуска не выполняет задачу сразу: в строке задачи ставится отметка, которую подхватывает тот инстанс, где планировщик включён. Обычно это занимает меньше минуты. Так сделано, чтобы задача выполнилась там же, где она выполняется по расписанию, а не на том сервере, который принял запрос администратора.

Повторно запросить запуск, пока предыдущий не забран, нельзя. Если в момент, когда планировщик добрался до отметки, задача уже выполняется, разовый запуск не состоится и в историю попадёт запись с результатом «Пропущено».

Запрошенный запуск можно отозвать: пока у задачи стоит отметка «запуск запрошен», в строке вместо кнопки запуска показывается кнопка отмены. Это нужно, если запуск запросили по ошибке или он завис из-за того, что забирать его некому. Если планировщик успел забрать отметку между нажатием и обработкой, отмена ответит «разовый запуск уже забран планировщиком» — задача выполнится, и её останется прервать обычным способом. Отмена попадает в «Изменения» отдельной записью.

Привязка к серверу

Поле «Выполнять на инстансе» закрепляет задачу за конкретным сервером. Это нужно, когда задача работает с тем, что есть только на одной машине.

Значение сравнивается с server.instance.id сервера. Опечатка означает, что задачу не возьмёт никто: в списке останется время следующего запуска, но запусков не будет. То же самое произойдёт, если сервер указан верно, но планировщик на нём выключен (scheduler.start=0): сам он задачу не возьмёт, а остальные её не видят — фильтр по привязке отсекает её и для расписания, и для разового запуска, и для повторов, и для догона пропущенного.

Чтобы это не приходилось выяснять по журналам, список сверяет привязку с историей запусков: если указанный сервер не запускал ни одной задачи, отметка с его именем становится красной, а в строке появляется предупреждение «Сервер … не запускал задач ни разу». Проверить идентификаторы можно по колонке «Инстанс» в истории запусков.

Прерывание запуска

Кнопка «Прервать выполнение» появляется вместо кнопки запуска, пока задача работает. Она ставит отметку, которую подхватывает сервер, выполняющий задачу, и прерывает её поток; запуск попадает в историю со статусом «Прервано».

Прерывание — просьба, а не гарантия. Сработает оно только если код задачи реагирует на прерывание потока: спит, ждёт ответа сети, проверяет флаг прерывания. Задача, занятая вычислениями в плотном цикле, продолжит работать до конца, и в истории останется отметка о запросе.

Поле «Таймаут» делает то же самое автоматически: запуск, идущий дольше заданного числа секунд, прерывается планировщиком без участия человека. Ограничения у него те же.

Пропущенные запуски

Расписание — это «в такую-то минуту», а не «не реже чем». Если в нужную минуту сервер был недоступен, обычная задача просто ждёт следующего совпадения: ночная выгрузка, пропущенная из-за перезапуска в 03:00, уедет на сутки.

Поле «Пропущенный запуск» это меняет. «Выполнить один раз» означает: при первой же возможности планировщик выполнит задачу, если её последний плановый запуск был пропущен. Именно один раз — сколько бы совпадений расписания ни было пропущено, навёрстывается только последнее.

Задача, которая ещё ни разу не запускалась, пропущенной не считается: иначе новая задача срабатывала бы сразу после сохранения.

Повторы при ошибке

Упавший запуск можно повторить, не дожидаясь расписания. «Повторов при ошибке» задаёт их число, «Пауза перед повтором» — задержку перед первым; перед каждым следующим она удваивается. При паузе в 60 секунд и трёх повторах попытки будут через минуту, две и четыре.

Номер попытки виден в истории запусков пометкой «2-я попытка». Успешный запуск обнуляет счётчик, исчерпанные попытки — тоже: следующего цикла повторов ждать до очередного срабатывания расписания.

Прерванный вручную запуск повторов не порождает — считается, что человек остановил его осознанно. Запуск, прерванный по таймауту, повторяется наравне с упавшим.

Удаление и восстановление

Удаление мягкое: задача пропадает из списка и перестаёт запускаться, но строка остаётся в базе вместе со всей историей запусков и записями аудита. Выполняющуюся прямо сейчас задачу удалить нельзя — сначала дождитесь конца запуска.

Удалённые показывает фильтр «Удалённые»; у такой задачи вместо обычных кнопок доступны «Восстановить» и история запусков. Восстановленная задача возвращается выключенной, даже если удаляли её включённой: так она не начнёт работать раньше, чем администратор проверит расписание.

Удаление и восстановление попадают в «Изменения задачи» отдельными видами записей, поэтому по журналу видно, кто и когда убрал задачу.

История запусков

История раскрывается в строке задачи. По каждому запуску видно результат, время начала, длительность, инстанс сервера и то, кем запуск был инициирован — расписанием либо конкретным пользователем. У завершившихся ошибкой разворачивается текст ошибки.

Таблица. Результаты запуска

Результат Что означает

Выполняется

Запуск начат и ещё не завершён.

Успешно

Задача отработала без исключения.

Ошибка

Задача завершилась исключением, стек сохранён в записи (до 16 000 символов).

Пропущено

Запуск не состоялся: предыдущий запуск этой же задачи ещё не завершился.

Таймаут

Запуск прерван, потому что шёл дольше заданного предела.

Прервано

Запуск остановлен человеком кнопкой «Прервать выполнение».

Задачи, которые ещё живут в конфигурации, тоже попадают в историю, но без привязки к строке списка — их запуски ищутся по имени класса, для этого в истории есть переключатель «Все запуски этого класса». Тот же переключатель показывает запуски удалённых задач.

Изменения задачи

Рядом с историей запусков есть вкладка «Изменения» — кто и что менял в самой задаче: создание, правку расписания и настроек, включение, выключение, запрос разового запуска, удаление и восстановление. Для правки показывается список изменившихся полей со старым и новым значением.

Запуски и изменения намеренно разделены: запуски делает планировщик, изменения — люди.

Права доступа

Раздел «Планировщик» в дереве прав содержит отдельные пункты на просмотр, создание, правку, включение и выключение, разовый запуск, удаление и восстановление. Список классов для окна выбора входит в пункт «Поиск задач планировщика и классов для них»: кто может искать задачи, тот видит и классы. Просмотр и поиск не логируются, изменяющие действия — логируются.

После обновления права не выдаются автоматически. Пока пункт «Планировщик» не выдан пользователю или набору прав, раздел не виден в меню, а запросы к нему отвечают «Действие запрещено». Права кэшируются до конца суток, поэтому после выдачи инстанс нужно перезапустить.

Работа на нескольких инстансах

Задачи из оснастки живут в базе, а база у инстансов общая, поэтому планировщик, включённый сразу на нескольких серверах, видит один и тот же список. Чтобы задача не выполнилась дважды, инстансы договариваются через саму запись задачи: перед запуском строка помечается именем инстанса и сроком аренды, и запускает задачу только тот, кому эта пометка досталась. Остальные в эту минуту задачу пропускают.

В списке у выполняющейся задачи видно, на каком сервере она идёт. Фильтр «Выполняются» тоже работает по всему кластеру, а не по тому серверу, который открыл страницу.

Если инстанс остановлен или упал во время выполнения задачи, его аренда протухает по истечении срока (по умолчанию пять минут, ключ scheduler.claim.lease.sec), после чего задачу может взять другой сервер — но не за ту же минуту расписания, повтор пропущенного запуска не производится. Запуски, оборванные остановкой сервера, при следующем старте помечаются ошибкой «Инстанс остановлен во время выполнения задачи», чтобы они не висели в истории как выполняющиеся.

Договорённость работает, только если у серверов различаются идентификаторы (server.instance.id). Два инстанса с одинаковым идентификатором считают аренду друг друга своей.

Трассировка

Если в конфигурации задан server.telemetry.trace.endpoint (адрес приёмника OTLP по HTTP, например http://<приёмник>:4318/v1/traces), каждый запуск задачи отправляется как трассировка с атрибутами: идентификатор и название задачи, класс, признак ручного запуска, номер попытки. В трассировке видно, на что задача тратит время внутри, — это точнее, чем общая длительность из истории.

Пустое значение ключа выключает отправку целиком, и тогда планировщик работает как раньше.

Метрики

Планировщик отдаёт в Prometheus две метрики:

  • scheduler_task_runs_total — счётчик запусков с тегами class (класс задачи) и status (success, error, skipped, timeout, cancelled). На нём строятся оповещения вида «задача падает подряд N раз»;

  • scheduler_task_running — сколько задач выполняется прямо сейчас. Постоянно ненулевое значение означает, что какая-то задача залипла и держит поток пула.

Счётчик заводится нулями для каждого включённого класса заранее, ещё до первого запуска. Без этого первая же ошибка нового класса не была бы видна запросам вида increase(…​): рост считается между точками, а до первой ошибки точек по этой серии нет.

Тегов с идентификаторами задач в метриках нет намеренно: они дают высокую кардинальность и раздувают хранилище.

Экспортируемые имена серий и общие правила Prometheus приведены в каталоге телеметрии.

Задачи в конфигурации сервера

В конфигурацию добавляются записи вида:

scheduler.task.<id>.class=<class_name>
# все ограничения по времени необязательны, пустое значение не ограничивает запуск
scheduler.task.<id>.minutes=<minutes>
scheduler.task.<id>.hours=<hours>
scheduler.task.<id>.dw=<dw>
scheduler.task.<id>.dm=<dm>
# произвольные параметры задачи, доступны наследникам ConfigurableTask
scheduler.task.<id>.<key>=<value>

Где:

  • <id> — уникальный числовой идентификатор задачи;

  • <class_name> — имя класса запускаемой задачи;

  • <minutes> — минуты, через запятую от 0 до 59;

  • <hours> — часы, через запятую от 0 до 23;

  • <dw> — дни недели, через запятую от 1 до 7, 1 — понедельник;

  • <dm> — дни месяца, через запятую от 1 до 31.

Идентификатор задачи должен быть уникален в пределах всей собранной конфигурации, включая подключённые через include.<id>=1 конфигурации и параметры из файла bgcrm.properties. Совпадение идентификаторов в разных конфигурациях приводит к тому, что одна задача молча замещает другую.

Задача выключается только комментированием всех её строк. Выключенная таким образом задача не видна в оснастке — в этом одно из отличий от задач, заведённых в базе.

Значения масок в конфигурации не проверяются: minutes=60 или опечатка в числе не вызовут ошибки, такая задача просто никогда не запустится. В оснастке маски проверяются при сохранении, и неверное значение сохранить нельзя.

Перенос задачи в оснастку

Порядок важен: пока задача есть и там и там, она выполняется дважды за минуту.

  1. Завести задачу в оснастке с тем же классом, расписанием и настройками, выключенной.

  2. Убедиться, что расписание совпадает: строка в списке показывает время следующего запуска.

  3. Закомментировать блок scheduler.task.<id>.* в конфигурации и применить её.

  4. Дождаться контрольной минуты, в которую задача должна была отработать, и убедиться по истории, что запуска не было.

  5. Включить задачу в оснастке.

  6. Проверить по истории первый запуск: результат, длительность, инстанс.

Так между шагами 3 и 5 возможен только пропуск одного запуска, но не дублирование. Обратный порядок (сначала включить, потом убрать из конфигурации) даёт двойное выполнение — для рассылок и создания процессов это прямой ущерб.

Если задача не выполняется

Список проверок по убыванию частоты:

  1. Задача выключена — в строке отметка «выключена», времени следующего запуска нет.

  2. Класс не найден — отметка «класс не найден». Класса нет в сборке либо в динамическом коде, либо в имени опечатка.

  3. Планировщик выключен на этом сервере — scheduler.start=0. В журнале при старте «Skipping scheduler start.».

  4. Маска не совпадает — сравните «следующий запуск» в строке с ожидаемым временем. Частая ошибка: заполнены часы, но не минуты, отчего задача идёт каждую минуту нужного часа.

  5. Предыдущий запуск не завершился — в истории результат «Пропущено», в строке отметка «выполняется». Задача держит поток пула и не даёт запуститься следующему разу.

  6. Пул занят целиком — все пять потоков заняты длинными задачами, короткие за эту минуту не стартуют.

  7. Задача привязана к другому серверу — поле «Выполнять на инстансе» указывает на сервер, где планировщик выключен, или содержит опечатку. Задачу в этом случае не возьмёт никто, включая кнопку разового запуска; если такой сервер вообще ничего не запускал, отметка с его именем в списке красная.

  8. Задача удалена — включите фильтр «Удалённые»: возможно, её убрали, а не сломали.

Если запуск был, но завершился ошибкой, полный стек лежит в записи истории; он же попадает в errors.log сервера, который выполнял задачу.

Настройки сервера

Поведение планировщика задаётся четырьмя ключами конфигурации.

Таблица. Ключи конфигурации

Ключ По умолчанию Назначение

scheduler.start

1

Рубильник планировщика на этом сервере. Без ключа планировщик работает по умолчанию.

server.instance.id

случайный

Идентификатор сервера: им подписывается аренда задачи, по нему работает привязка задачи к серверу и уборка после перезапуска.

scheduler.pool.size

5

Сколько задач выполняется одновременно. Пул общий для задач из оснастки и из конфигурации.

scheduler.claim.lease.sec

300

Срок аренды задачи. Значения меньше 60 секунд поднимаются до минуты.

server.instance.id нужно задавать явно. Без него подставляется случайный идентификатор, новый при каждом запуске, и сервер перестаёт узнавать собственные записи: зависшие после падения запуски не закрываются, а оставленная аренда держится до истечения срока.

Размер пула и срок аренды перечитываются на каждом тике, то есть раз в минуту, — перезапуск для их изменения не нужен. Срок аренды имеет смысл увеличивать там, где задачи идут дольше нескольких минут: аренда продлевается каждую минуту, и запас в пять минут нужен на случай, когда сервер занят или недоступен.

Тик привязан к границе минуты, а не к паузе после работы. Занятый пул задерживает только те задачи, которым не хватило места, а не весь тик целиком: обслуживание уже идущих запусков идёт в любом случае. Задачи из конфигурации и из оснастки исполняются одинаково, обе ветки шлют телеметрию.

Сам тик идёт на виртуальном потоке, а задачи выполняются на обычных. Так сделано намеренно: прерывание виртуального потока закрывает его сетевые операции, поэтому задача, снятая по таймауту или кнопкой, не смогла бы даже записать в базу результат своего запуска.

Ограничения

Пул планировщика общий для всех задач. Пока все его потоки заняты, новые задачи за эту минуту не запускаются — длинные задачи задерживают короткие. Обслуживание уже идущих запусков при этом продолжается: аренда продлевается, таймауты и запросы на прерывание срабатывают.

Повторный запуск задачи, которая ещё выполняется, не производится. Для задач из оснастки это отслеживается по самой задаче и в пределах всего кластера, поэтому несколько задач с одинаковым классом, но разными настройками, друг другу не мешают. Для задач из конфигурации отслеживание идёт по классу и только в памяти одного сервера: пока выполняется одна, остальные задачи того же класса на этом сервере пропускаются, а второй сервер о них не знает.

Задачи выполняются под служебной учётной записью, а не под тем, кто нажал кнопку разового запуска.

Пропущенный запуск по умолчанию не навёрстывается; изменить это можно полем «Пропущенный запуск» у конкретной задачи.

История запусков не чистится сама по себе: за неё отвечает отдельная задача планировщика, которую нужно завести (см. ниже). Без неё таблица растёт — задача, работающая каждую минуту, добавляет около полумиллиона записей в год.

Чистка истории запусков

Чистку выполняет встроенная задача ru.bgcrm.core.scheduler.task.SchedulerHistoryCleanupTask. Заведите её в оснастке как обычную задачу — например, ежедневно ночью:

Класс:    ru.bgcrm.core.scheduler.task.SchedulerHistoryCleanupTask
Часы:     3
Минуты:   20
Настройки задачи:
days=90

Параметр days задаёт срок хранения в днях, по умолчанию 90. Записи старше указанного срока удаляются целиком, включая тексты ошибок.

Задача удаляет историю всех задач сразу, а не только свою. Заводить её больше одного раза не нужно.

Где лежат данные

Таблица. Таблицы планировщика

Таблица Что хранит

scheduler_task

Сами задачи: название, класс, маски, настройки, признак включённости, отметка удаления и служебные поля захвата задачи инстансом.

scheduler_task_run

История запусков: время начала и окончания, длительность, результат, текст ошибки, инстанс, признак ручного запуска и пользователь.

scheduler_task_log

Изменения задач: кто, когда и что поменял.

Задачи из конфигурации своих строк в scheduler_task не имеют, но их запуски в историю попадают.