.. | ||
export_test.go | ||
README.md | ||
scheduler.go | ||
scheduler_test.go |
scheduler
Пакет scheduler
реализует планировщик системы. scheduler.Scheduler
хранит полное состояние кластера
и принимает решение на каком воркере и какой джоб нужно запустить.
Шедулер является точкой координации между воркерами и билдами. Бегущие билды обращаются к шедулеру,
передавая джобы в функцию ScheduleJob
. Воркеры забирают джобы из шедулера вызывая функцию PickJob
.
После того, как воркер завершил выполнение джоба, он вызывает функцию OnJobComplete
. Эту функцию
могут вызвать даже для того джоба, который никто не шедулил. В этом случае планировщик просто должен
запомнить, что результаты джоба сохранены в кеше на воркере.
Вы можете отложить реализацию полной версии шедулера на последний шаг, и реализовать упрощённую версию на одном глобальном канале. Такой реализации будет достаточно, чтобы работали все интеграционные тесты с одним воркером.
Алгоритм планирования
Планировщик поддерживает множество очередей:
- Одна глобальная очередь
- По две локальные очереди на воркер.
При запросе нового джоба воркер выбирает случайную джобу из трех очередей - глобальной, и двух локальных относящихся
к этому воркеру. Случайная очередь выбирается одним вызовом select {}
.
Ожидающий исполнения джоб всегда находится в первой локальной очереди воркеров, на которых есть результаты работы этого джоба.
Если джоб ждёт выполнения дольше CacheTimeout
или если в момент SchedulerJob
джоба не было в кеше ни на одном
из воркеров, то он попадает во все вторые локальные очереди воркеров, на которых есть хотя бы один артефакт
из множества зависимостей этого джоба.
Определения первой и второй локальной очереди не зависят от того, в каком порядке в шедулер пришли джобы
и информация о кеше артефактов. То есть, если джоб уже находится в глобальной очереди, и в этот момент приходит
информация, что этот джоб находится в кеше на воркере W0
, то джоб должен быть добавлен
в первую локальную очередь W0
.
Если джоб ждёт выполнения дольше DepTimeout
, то он помещается в глобальную очередь.
Тестирование
Вместо реального времени, юниттесты шедулера используют библиотеку clockwork
. Это накладывает ограничения
на детали вашей реализации. Ожидание CacheTimeout
и DepTimeout
должно быть реализовано как select {}
на
канале, который вернула функция timeAfter
. Мы считаем что CacheTimeout < DepTimeout
, и ожидание этих
таймаутов происходит последовательно в одной горутине.