Реализовать data locality scheduler
ТЕХНИЧЕСКОЕ ЗАДАНИЕ: Реализовать data locality scheduler
1. Цель задачи
Разработать прототип планировщика (scheduler) для распределённой вычислительной системы, который размещает задачи на узлах, уже хранящих необходимые данные (data locality). Реализовать симуляцию кластера, где данные реплицированы на нескольких узлах, а scheduler выбирает узел с минимальной стоимостью передачи данных. Ключевой результат — снижение объёма сетевого трафика, связанного с перемещением данных, не менее чем на 50% по сравнению со случайным планированием.
Ключевой результат Прототип scheduler’а, который в симуляции (10 узлов, 100 задач, репликация 2–3 копий) показывает снижение сетевого трафика ≥50% относительно baseline‑алгоритма (random placement).
2. Исходные данные
| Что нужно | Откуда взять |
|---|---|
| Симулятор распределённого кластера | Написать самостоятельно на Python (или взять готовый, например, simpy или написать на базе asyncio) |
| Модель данных (датасет, партиции) | Сгенерировать синтетически: N узлов, M шардов данных, каждый шард реплицирован на R узлов |
| Baseline‑алгоритм (random placement) | Реализовать как часть задания |
| Метрики сетевого трафика | Считать объём переданных данных между узлами (в условных единицах) для каждого запуска |
| Распределение задач по данным | Каждая задача требует определённый шард (задать случайно, можно с skew) |
Если нет реального инструмента — симулируем:
- Создать класс
ClusterNodeс полями:id,data_shards(список имён шардов на узле),available_cpus,network_bandwidth(усл. ед./сек). - Создать класс
Schedulerс методомschedule(task, data_shard_name), возвращающимnode_id. - Для симуляции запуска задач использовать событийный цикл: в каждый момент времени узел обрабатывает задачу, занимая ресурсы, а по завершению освобождает.
- Сетевой трафик засчитывать, если задача выполняется не на том узле, где находятся данные (т.е. данные пришлось передавать). Можно упрощённо: трафик = размер шарда (константа), если узел не владеет локальной копией.
3. Технологический стек
| Компонент | Инструменты | Назначение |
|---|---|---|
| Язык программирования | Python 3.10+ | Реализация симулятора и scheduler’а |
| Симуляция времени/asyncio | asyncio или simpy | Моделирование выполнения задач, очередей, загрузки узлов |
| Анализ результатов | pandas, matplotlib | Вычисление метрик, построение графиков |
| Визуализация топологии (опционально) | networkx + matplotlib | Отображение узлов и размещения шардов |
| Тестирование | pytest | Unit‑тесты для scheduler’а и симулятора |
4. Этапы выполнения
Этап 1: Проектирование симулятора (2–3 часа)
Действия
-
Определить сущности
- Узел (
Node): id, доступные CPU, список локальных шардов. - Шард (
Shard): имя, размер (усл. единица), узлы-владельцы. - Задача (
Task): id, имя требуемого шарда, длительность CPU time, момент поступления.
- Узел (
-
Реализовать класс
Cluster- Создать N узлов (например, 10).
- Создать M шардов (например, 50), размер каждого 1 ед.
- Распределить шарды по узлам: для каждого шарда выбрать R=2–3 узла случайно (или детерминированно). Гарантировать, что каждый шард присутствует хотя бы на одном узле.
-
Реализовать базовый генератор задач
- Задачи поступают согласно Poisson‑процессу (например, λ=2 задачи/сек) или просто фиксированный список из 100 задач.
- Каждая задача выбирает случайный шард (можно с неравномерным распределением, например, 20% шардов получают 80% задач — Zipf).
-
Реализовать метрику сетевого трафика
- Если задача выполняется на узле, не содержащем требуемый шард, засчитать трафик = размер шарда (1 ед.) плюс возможная задержка.
- По окончании симуляции вывести суммарный переданный объём и среднюю загрузку узлов.
Ожидаемый результат этапа Работающий симулятор кластера, который умеет запускать задачи наугад (random placement) и выводить метрики трафика.
Этап 2: Реализация random placement (baseline) (30–60 минут)
Действия
-
Создать класс
RandomSchedulerс методомschedule(task, cluster):- Выбрать случайный узел (из всех, не проверяя локальность данных).
- Можно добавить проверку на занятость; если случайный узел занят — выбрать другой случайный (с ограниченным числом попыток).
-
Прогнать симуляцию с random scheduler’ом:
- Запустить 10 симуляций с разными seed, получить средний объём трафика.
- Построить график: номер задачи vs накопленный трафик.
-
Зафиксировать baseline:
- Сохранить результат (например,
baseline_traffic_mean = X).
- Сохранить результат (например,
Ожидаемый результат этапа Числовое значение среднего трафика для random placement (baseline).
Этап 3: Разработка data locality scheduler (3–4 часа)
Действия
-
Реализовать
DataLocalityScheduler- При поступлении задачи определяет требуемый шард.
- Находит все узлы, на которых есть локальная копия этого шарда.
- Если есть хотя бы один такой узел: выбирает узел с наименьшей текущей загрузкой (например, длина очереди задач или свободные CPU).
- Если таких узлов нет (редкая ситуация при отказе реплик) — выбирает узел, где меньше всего передаваемых данных (т.е. ближайший по сетевой топологии). Для простоты: расстояние = 1, если шард есть; 10, если нет.
-
Учесть репликацию и балансировку:
- Дополнительно можно ввести кэширование: после выполнения задачи на узле, если там не было шарда, можно (опционально) оставить копию на время (LRU). Это выходит за рамки ТЗ, но можно как усложнение.
-
Реализовать класс
SimulationRunner:- Принимает scheduler, список задач, кластер.
- Запускает симуляцию событий (по времени) с использованием
asyncio.sleepилиsimpy. - Собирает метрики: по каждой задаче — узел выполнения, локальность (True/False), переданный трафик.
-
Провести эксперимент:
Ожидаемый результат этапа Функция run_simulation(scheduler, tasks, cluster), возвращающая словарь метрик, и значение % снижения трафика.
Этап 4: Анализ и визуализация (1–2 часа)
Действия
-
Собрать статистику по 20 запускам с разными seed:
- Средний трафик, медиана, min/max.
- Процент задач, выполненных локально (data‑local hit ratio).
-
Построить графики:
-
Составить краткий отчёт:
- Таблица с основными метриками.
- Вывод: достигнуто ли снижение ≥50%?
Ожидаемый результат этапа Jupyter Notebook (или скрипт, генерирующий png) с графиками и выводами.
Этап 5: Покрытие тестами и фиксация (1 час)
Действия
-
Написать unit‑тесты:
test_data_locality_chooses_local_node_if_available().test_random_scheduler_does_not_guarantee_locality().test_traffic_counting_correct().test_simulation_runs_with_given_tasks().
-
Проверить корректность метрик:
- Вручную рассчитать трафик для двух задач и убедиться, что симулятор считает правильно.
-
Зафиксировать код в репозитории (README с инструкцией по запуску).
Ожидаемый результат этапа Набор тестов (pytest) и документация.
5. Критерии приемки (Definition of Done)
- Симулятор поддерживает произвольное число узлов, шардов и реплик (задаётся в конфиге).
- Random scheduler — полностью работоспособен и даёт baseline трафика.
- Data locality scheduler выбирает узел с локальными данными, если это возможно, иначе минимизирует перенос.
- В симуляции (10 узлов, 50 шардов, репликация 3, 100 задач) data locality scheduler снижает общий трафик на ≥50% относительно random.
- Написаны минимум 3 unit‑теста.
- Результаты визуализированы (графики).
- README содержит описание алгоритмов, запуск и пример вывода.
6. Ожидаемый результат
Основной артефакт Python‑пакет (один модуль или несколько) с классами Cluster, Task, RandomScheduler, DataLocalityScheduler, SimulationRunner.
Содержание
cluster.py— модели узлов и шардов.schedulers.py— реализации планировщиков.simulation.py— симулятор, сбор метрик.analysis.ipynbилиanalyze.py— анализ и графики.tests/— тесты.
Дополнительно
- Скриншот графика, подтверждающий снижение трафика.
- Краткий отчет (PDF/Markdown) с метриками.
7. Возможные сложности и их решение
| Сложность | Решение |
|---|---|
| Симуляция времени (блокирующие вызовы) | Использовать asyncio и asyncio.sleep для неблокирующей очереди; или упрощённо — не учитывать время, только факт выбора узла. |
| Дисбаланс загрузки узлов при locality | Добавить в scheduler второй критерий: из узлов с локальными данными выбирать наименее загруженный. |
| Измерение трафика: что считать «передачей» | Договориться: если задача выполняется не на узле с данными, считаем, что данные передаются с одного из узлов-владельцев (выбирается случайно). Размер шарда константен. |
| Воспроизводимость результатов | Фиксировать seed для генерации кластера и последовательности задач. Использовать numpy.random.seed. |
| Отсутствие локальных данных (все реплики на занятых узлах) | В реальности крайне редко при R≥3; упрощаем — если ни один локальный узел не может взять задачу (все очереди переполнены), разрешаем передачу с ближайшего узла. |
8. Бюджет времени (оценка)
| Этап | Время |
|---|---|
| Этап 1: Проектирование симулятора | 2–3 ч |
| Этап 2: Random placement (baseline) | 30–60 мин |
| Этап 3: Data locality scheduler | 3–4 ч |
| Этап 4: Анализ и визуализация | 1–2 ч |
| Этап 5: Тесты и фиксация | 1 ч |
| Итого | 8–11 ч |
Примечание: время дано для первого выполнения. При наличии опыта с simpy или asyncio — сокращается до 6–8 ч.
9. Связанные вопросы из базы знаний
| Вопрос | Тема |
|---|---|
| 23 | Data locality в Hadoop (HDFS + YARN) |
| 45 | Replication strategies (raft, gossip) |
| 87 | Sharding and consistent hashing |
| 112 | Load balancing in distributed systems |
| 201 | Network topologies and latency models |
| 315 | Task scheduling algorithms (FIFO, fairness, capacity) |
| 409 | Locality‑aware scheduling in MapReduce |
| 522 | Caching and data prefetching |
| 678 | Simulating distributed systems (discrete‑event) |
| 812 | Metrics for data‑intensive computing (throughput, traffic) |
10. Чек-лист самопроверки
- Я реализовал симулятор, который можно запустить одной командой (
python simulate.py). - Random scheduler и data locality scheduler используют одинаковые сценарии (seed) для честного сравнения.
- Я проверил, что data‑local scheduler не выбирает узел без данных, если есть доступный узел с данными.
- Я построил хотя бы один график, наглядно показывающий снижение трафика.
- Я написал 3 unit‑теста, и они проходят.