2020-04-05 11:29:46 +00:00
|
|
|
|
# scheduler
|
|
|
|
|
|
|
|
|
|
Пакет `scheduler` реализует планировщик системы. `scheduler.Scheduler` хранит полное состояние кластера
|
|
|
|
|
и принимает решение на каком воркере и какой джоб нужно запустить.
|
|
|
|
|
|
|
|
|
|
Шедулер является точкой координации между воркерами и билдами. Бегущие билды обращаются к шедулеру,
|
|
|
|
|
передавая джобы в функцию `ScheduleJob`. Воркеры забирают джобы из шедулера вызывая функцию `PickJob`.
|
|
|
|
|
|
2020-04-21 22:15:29 +00:00
|
|
|
|
После того, как воркер завершил выполнение джоба, он вызывает функцию `OnJobComplete`. Эту функцию
|
|
|
|
|
могут вызвать даже для того джоба, который никто не шедулил. В этом случае планировщик просто должен
|
|
|
|
|
запомнить, что результаты джоба сохранены в кеше на воркере.
|
|
|
|
|
|
2020-04-21 22:17:51 +00:00
|
|
|
|
Фукнция `LocateArtifact` должна возвращать имя любого воркера, который хранит в кеше заданный артефакт.
|
|
|
|
|
Эта функция не нужна в этой задаче, но он потребуется вам для реализации передачи артефактов между
|
|
|
|
|
воркерами.
|
|
|
|
|
|
2021-05-06 16:43:04 +00:00
|
|
|
|
Для того, чтобы зачесть домашнее задание, достаточно реализовать упрощённый алгоритм планирования с
|
2022-03-06 02:06:00 +00:00
|
|
|
|
одной глобальной очередью. Функция `ScheduleJob` должна помещать `job` в очередь или возвращать ссылку на существующий
|
2021-04-25 13:10:08 +00:00
|
|
|
|
`pendingJob`. Функция `PickJob` должна извлекать первый элемент из очереди. Обратите внимание, что функция `PickJob`
|
|
|
|
|
принимает контекст. Поскольку это блокирующая операция, она должна поддерживать отмены. Если вы забудете
|
2021-04-25 18:16:24 +00:00
|
|
|
|
реализовать отмену в этом месте, интеграционные тесты будут зависать.
|
2020-04-05 11:29:46 +00:00
|
|
|
|
|
|
|
|
|
## Алгоритм планирования
|
|
|
|
|
|
2021-04-25 13:10:08 +00:00
|
|
|
|
*Далее описывается продвинутый алгоритм планирования. Алгоритм проверяется в отдельной задаче `smartsched`.
|
2021-05-06 16:43:04 +00:00
|
|
|
|
Для зачёта по домашнему заданию, реализовывать этот алгоритм необязательно.*
|
2021-04-25 13:10:08 +00:00
|
|
|
|
|
2020-04-05 11:29:46 +00:00
|
|
|
|
Планировщик поддерживает множество очередей:
|
2021-04-25 13:10:08 +00:00
|
|
|
|
|
|
|
|
|
1. Одна глобальная очередь.
|
2020-04-05 11:29:46 +00:00
|
|
|
|
2. По две локальные очереди на воркер.
|
|
|
|
|
|
2022-03-06 02:06:00 +00:00
|
|
|
|
При запросе нового джоба воркер выбирает случайную джобу из трех очередей - глобальной и двух локальных, относящихся
|
2020-04-05 11:29:46 +00:00
|
|
|
|
к этому воркеру. Случайная очередь выбирается одним вызовом `select {}`.
|
|
|
|
|
|
|
|
|
|
Ожидающий исполнения джоб всегда находится в первой локальной очереди воркеров, на которых есть
|
|
|
|
|
результаты работы этого джоба.
|
|
|
|
|
|
2022-04-07 11:39:26 +00:00
|
|
|
|
Если выполнено одно из следующих условий, то джоб попадает во все вторые локальные очереди воркеров, на которых есть
|
|
|
|
|
хотя бы один артефакт из множества зависимостей этого джоба:
|
|
|
|
|
|
|
|
|
|
1) В момент вызова `ScheduleJob` джоба не было в кеше ни на одном из воркеров
|
|
|
|
|
2) Джоб ждёт выполнения дольше `CacheTimeout`
|
2020-04-05 11:29:46 +00:00
|
|
|
|
|
|
|
|
|
Определения первой и второй локальной очереди не зависят от того, в каком порядке в шедулер пришли джобы
|
2022-03-06 02:06:00 +00:00
|
|
|
|
и информация о кеше артефактов. То есть, если джоб уже находится в глобальной очереди и в этот момент приходит
|
2022-04-07 11:39:26 +00:00
|
|
|
|
информация, что этот джоб находится в кеше на воркере w<sub>0</sub>, то джоб должен быть добавлен
|
|
|
|
|
в первую локальную очередь w<sub>0</sub>.
|
2020-04-05 11:29:46 +00:00
|
|
|
|
|
2022-04-07 11:39:26 +00:00
|
|
|
|
Если джоб ждёт выполнения дольше `DepsTimeout`, то он помещается в глобальную очередь. Отсчет этого таймаута начинается
|
|
|
|
|
уже после обработки предыдущего условия, то есть не нужно вычитать из `DepsTimeout` никакое другое число.
|
2020-04-05 11:29:46 +00:00
|
|
|
|
|
|
|
|
|
## Тестирование
|
|
|
|
|
|
|
|
|
|
Вместо реального времени, юниттесты шедулера используют библиотеку `clockwork`. Это накладывает ограничения
|
|
|
|
|
на детали вашей реализации. Ожидание `CacheTimeout` и `DepTimeout` должно быть реализовано как `select {}` на
|
2022-04-07 11:39:26 +00:00
|
|
|
|
канале, который вернула функция `TimeAfter`. Мы считаем, что `CacheTimeout < DepsTimeout`, и ожидание этих
|
|
|
|
|
таймаутов происходит отдельно в двух разных вызовах `select {}`:
|
|
|
|
|
```
|
|
|
|
|
select {
|
|
|
|
|
case <-TimeAfter(timeout):
|
|
|
|
|
...
|
|
|
|
|
...
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Среди двух условий попадания во вторые локальные очереди, если выполнено первое из них, делать ожидание `CacheTimeout`
|
|
|
|
|
через `select {}` не нужно, иначе ваша реализация может проходить тесты с недетерминированным исходом.
|