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