Бэкенд: фреймворк Gin, база данных: PostgreSQL (Citus, используемый в Micosoft) Docker, Docker-compose
Фронтенд: React, JavaScript
Реализован swagger бэкенда админ-панели, сервиса отдачи цен, а так же фронтенд. Также доступна видео-демонстрация работы платформы
- Управление матрицами
- Изменение
- История изменений (history)
- Детальное сравнение двух матриц (аналог git diff)
- Просмотр данных в таблице (поиск по ней)
- Изменение
- Аналитика
- Интерес
- Тенденция цен с графиком конверсии (конверсия - мок)
- Трассировка всего приложения (jaeger)
- Анализ (графики на моках)
- Интерес
- Профиль пользователя (мок)
- Algo
- Оптимизированный поиск цен по baseline
- Оптимизированный поиск цен по discount
- Storage
- Создание storage
- Подготовка storage к production (просчитывание оптимизаций)
- Deploy storage на production
- Реализовать передачу через Postgres
- Просмотр текущего storage (выбранного)
- Price
- Получение
- Быстрое обновление
- Полностью атомарное обновление
- Идея реализации атомарного обновления
- Полностью атомарное обновление
- Deployment
- Dockerfiles (front, admin panel back, price api back)
- Распределённая Citus PostgreSQL
- Шардирование (позволяет сильно оптимизировать нагрузку на postgres)
- k8s
- Оркестрация "подмены" текущего storage на новый
- Перезапуск price api при его "смерти"
- Price api back сам получает текущий storage при перезапуске
Гипотеза: Самых нижних категорий дерева (и, скорее всего, локации) Следствие: Даже если локация есть в матрице цен, а выбранной категории вообще в ней нет, значит, пары (локация, категория) вообще нет Реализация: Аллоцируем 2 массива len(maxIndex-1), где maxIndex - максимальный индекс категории или локации соответственно, и высчитываем "прыжки" из тех локаций/категорий (не пар, а одиночных значений!) которые ВООБЩЕ не встречаются в baseline матрице. Таким образом, мы гораздо быстрее (не делая запрос в postgres) пропускаем несколько шагов (если прыгнули на 3 категории выше и на 2 локации - 6 лишних запросов в БД), таким образом уменьшая нагрузку на postgres и увеличивая скорость.
Гипотеза: Суммарный размер скидочных матриц ощутимо меньше размера baseline матриц Слествие: Савайте засунем все скидочные в hashMap?? Реализация: map[discountMatrixName]map[locationID]map[categoryID]int - price
- Используется в Microsoft
- Управляет шардированием и репликацией
- По источникам из доклада Microsoft Citus в 300 раз быстрее при сложно запросе с JOIN и sum в таблице, в которой более 500 миллионов строк
- С июня 2022 поддерживае query from every node, что позволит не обращаться к master'у, а делать запросы к localhost с price api node, что облегчит нагрузку на сеть и увеличит скорость
- С официального сайта: "...2 million records per core in a second applies to Citus, and that additionally, because of our horizontal scale you can expect 2 million per core in your Citus cluster...", что говорит нам о том, что это решение более чем удовлетворительно для 2 million rpm
Каждая price nod'а имеет свой postgres, который подключён к master'у. Также, каждая price node имеет в runtime название baselineMatrix и []string{discountMatrices}, чтобы понимать, к каким именно матрицам обращаться в Postgres. При изменении storage, мы сначала кладём новые матрицы в Postgres, высчитываем для них оптимизацию по алгоритму, затем кладём оптимизации в postgres (jsonb) и только потом говорим k8s, который будет дёргать ручки каждой price node (ретраить), чтобы она начала отдавать данные из новой матрицы. Из-за citus изменение postgres будет происходить транзакционно. Таким образом, сам момент изменения - это лишь подмена полей структуры в runtime. Из этого не реализован сам citus, k8s и get оптимизации алгоритма из postgres, мы успели сделать так, что preparedStorage отсылается на nodes и там кладётся в структуру, которая ждёт экзекуции.