/2021-highload-dht

Highload course project

Primary LanguageJavaApache License 2.0Apache-2.0

2021-highload-dht

Курсовой проект 2021 года курса "Проектирование высоконагруженных систем" в Технополис.

Этап 1. HTTP + storage (deadline 2021-09-29 23:59:59 MSK)

Fork

Форкните проект, склонируйте и добавьте upstream:

$ git clone git@github.com:<username>/2021-highload-dht.git
Cloning into '2021-highload-dht'...
...
$ git remote add upstream git@github.com:polis-mail-ru/2021-highload-dht.git
$ git fetch upstream
From github.com:polis-mail-ru/2021-highload-dht
 * [new branch]      master     -> upstream/master

Make

Так можно запустить тесты:

$ ./gradlew test

А вот так -- сервер:

$ ./gradlew run

Develop

Откройте в IDE -- IntelliJ IDEA Community Edition нам будет достаточно.

ВНИМАНИЕ! При запуске тестов или сервера в IDE необходимо передавать Java опцию -Xmx128m.

В своём Java package ru.mail.polis.service.<username> реализуйте интерфейс Service и поддержите следующий HTTP REST API протокол:

  • HTTP GET /v0/entity?id=<ID> -- получить данные по ключу <ID>. Возвращает 200 OK и данные или 404 Not Found.
  • HTTP PUT /v0/entity?id=<ID> -- создать/перезаписать (upsert) данные по ключу <ID>. Возвращает 201 Created.
  • HTTP DELETE /v0/entity?id=<ID> -- удалить данные по ключу <ID>. Возвращает 202 Accepted.

Возвращайте реализацию интерфейса в ServiceFactory.

Используем свою реализацию DAO из весеннего курса 2021-db-lsm, либо берём референсную реализацию, если курс БД не был завершён.

Проведите нагрузочное тестирование с помощью wrk в одно соединение:

  • PUT запросами на стабильной нагрузке (wrk должен обеспечивать заданный с помощью -R rate запросов)
  • GET запросами на стабильной нагрузке по наполненной БД

Почему не curl/F5, можно узнать здесь и здесь.

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

Отпрофилируйте приложение (CPU и alloc) под PUT и GET нагрузкой с помощью async-profiler. Приложите SVG-файлы FlameGraph cpu/alloc для PUT/GET нагрузки.

Объясните результаты нагрузочного тестирования и профилирования и приложите текстовый отчёт (в Markdown).

Продолжайте запускать тесты и исправлять ошибки, не забывая подтягивать новые тесты и фиксы из upstream. Если заметите ошибку в upstream, заводите баг и присылайте pull request ;)

Report

Когда всё будет готово, присылайте pull request со своей реализацией и оптимизациями на review. Не забывайте отвечать на комментарии в PR (в том числе автоматизированные) и исправлять замечания!

Этап 2. Многопоточность (deadline 2021-10-13 23:59:59 MSK)

Обеспечьте потокобезопасность реализации DAO с использованием примитивов java.util.concurrent.*. Прокачаться можно с руководством Java Concurrency in Practice.

Реализуйте асинхронный flush MemTable в SSTable в отдельном потоке, чтобы не блокировать пользовательский запрос, приведший к flush.

Бонус: Реализуйте автоматический фоновый compaction, предварительно поддержав SSTable размером более 2 ГБ.

Сконфигурируйте HTTP сервер, чтобы он обрабатывал запросы с помощью пула из нескольких потоков.

Проведите нагрузочное тестирование с помощью wrk2 в несколько соединений (не меньше 16):

  • PUT запросами на стабильной нагрузке (wrk2 должен обеспечивать заданный с помощью -R rate запросов)
  • GET запросами на стабильной нагрузке по наполненной БД

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

Отпрофилируйте приложение (CPU, alloc и lock) под PUT и GET нагрузкой с помощью async-profiler. Приложите SVG-файлы FlameGraph cpu/alloc/lock для PUT/GET нагрузки.

Объясните результаты нагрузочного тестирования и профилирования и приложите текстовый отчёт (в Markdown).

Report

Когда всё будет готово, присылайте pull request со своей реализацией и проведёнными оптимизациями на review.

Этап 3. Асинхронный сервер (deadline 2021-10-20 23:59:59 MSK)

Вынесите обработку запросов в отдельный ExecutorService с ограниченной очередью, чтобы разгрузить SelectorThreadы HTTP сервера.

Проведите нагрузочное тестирование с помощью wrk2 с большим количеством соединений (не меньше 64) PUT и GET запросами.

Отпрофилируйте приложение (CPU, alloc и lock) под PUT и GET нагрузкой с помощью async-profiler.

Report

Когда всё будет готово, присылайте pull request с изменениями, результатами нагрузочного тестирования и профилирования, а также анализом результатов по сравнению с предыдущей (синхронной) версией.

Этап 4. Шардирование (deadline 2021-10-27 23:59:59 MSK)

Реализуем горизонтальное масштабирование через поддержку кластерных конфигураций, состоящих из нескольких узлов, взаимодействующих друг с другом через реализованный HTTP API. Для этого в ServiceFactory передаётся статическая "топология", представленная в виде множества координат всех узлов кластера в формате http://<host>:<port>.

gradle run теперь стартует Cluster из трёх нод.

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

Реализуйте один из алгоритмов распределения данных между узлами, например, consistent hashing или rendezvous hashing.

Report

Присылайте pull request со своей реализацией поддержки кластерной конфигурации на review. Не забудьте нагрузить, отпрофилировать и проанализировать результаты профилирования под нагрузкой. С учётом шардирования набор тестов расширяется, поэтому не забывайте подмёрдживать upstream.