2. Agenda
● Что делает aviasales
● Что делает рассылка aviasales
● Зачем нам Domain Specific Database
● Детали реализации фильтрующего дерева
● Ограничения решения
● Масштабирование решения
4. Фильтры рассылки
Предпочитаемые
авиакомпании
Пункты
вылета/назначения
Стоимость
билетов
Даты
вылета/прилёта
Страна
назначения
Месяца
вылета/прилёта
Длительность
прибывания
Количество
пересадок
Номера
рейсов
Аэропорты
пересадок
5. Движок рассылки должен
● 1 000 000 раз в сутки
● Проверить 1mb документ
● На соответствие ~1 000 000 предикатам
И не вспотеть!
6. Структура документа
Результаты поиска
Курсы Валют
Информация
о OTA
Информация
о Аэропортах
Предложения
Предложение
Цена OTA 1 Цена OTA 2
Сегмент 1
Перелёт 1
HKT HKG
Перелёт 1
HKG DME
Сегмент 2
Перелёт 1
DME BKK
Перелёт 1
BKK HKT
7. Структура подписок
orig='MOW' and dest='HKT' and price<40000
orig='MOW' and dest_country='US' and price<60000
orig='LED' and switch_count<2 and price<2000
orig='LED' and 'CDG' in switch_airports
И подобных выражений сотни тысяч...
8. Требования к базе данных
● Проверка на соответствие документа всем
предикатам за <50ms
● Низкое потребление ресурсов
● Простота интеграции
● Расширяемость
● Масштабируемость
9. Почему не подходит SQL
● Неоднородные наборы предикатов
● Нетипичные для SQL входные данные –
огромное дерево
● Инверсия логики:
● В SQL хранят данные и выбирают данные
соответствующие предикатам запроса
● Для рассылки в базе надо хранить предикаты
и проверять соответсвие пришедших данных
предикатам
10. Ловушки: Протокол взаимодействия
● Программисты любят придумывать новые
протоколы mysql/postgres/redis/memcached
● Изобретение протокола - трата времени
● Протоколы на все случаи уже изобретены
11. Протокол взаимодействия HTTP
● Не надо писать клиент и сервер
● Удобно дебажить и тестировать
● Проксируется балансируется
кешируется
● Существуют реализации для
всего
12. Ловушка: низкоуровневый язык
● Реализация на Java/C/Go/Erlang чтобы
быстро работало!
● Скорость - следствие хороших
алгоритмов, а не языка
● Низкоуровневые языки громоздки
● Их сложно отлаживать
13. Язык реализации Python
( )
● Можно быстро написать прототип и
проверить эффективность алгоритмов
● Много сторонних библиотек
● Компактный и читаемый код
14. Ловушка: одно решение для всего
● Программисты любят разрабатывать
комбайны решающие все проблемы на
земле
● Потеря фокуса
● Сложно заставить комбайн решать все
задачи одинаково хорошо
● Создание комбайнов ВСЕГДА приводит
к долгострою
16. Структура подписки
orig='MOW' and dest='HKT' and price<4000
● У свойств документа разная сложность вычисления
● Оператор = проще операторов <,>
● Порядок вычисления дизьюнктов не оказывает
влияния на результат
17. Структура подписки
if (dest='HKT')then
if(orig='MOW') then
if(price<4000)
match
● Свойства документа вычисляются лениво
● Пытаемся как можно раньше отсеять как можно
больше поддеревьев
● Переупорядочивание дизьюнктов позволяет экономить
ресурсы
18. Приоритеты операций
Дешевле проверка выше приоритет
= < >
Больше урезается дерево выше
приоритет
ORIGIN, DATE
Проще расчитывается значение
свойства выше приорит
ORIGIN, PRICE
19. свойства + предикаты = узлы дерева
ЦЕНА <
10000 15000 15500 15700
Свойства
Оператор
ВРЕМЯ ПЕРЕСАДКИ >
80мин 95мин 110мин 200мин
Константы
20. Устройство дерева
node
op – string “”
property – string “”
children – hash {} sorted_keys - array []
24. Фильтрующие свойства
Количество пересадок <
0 1 2 3
Результаты поиска
Курсы Валют
Информация
о OTA
Информация
о Аэропортах
Предложения
1 Пересадка 3 Пересадки 0 Пересадок
25. Фильтрующие свойства
Количество пересадок <
0 1 2 3
Результаты поиска
Курсы Валют
Информация
о OTA
Информация
о Аэропортах
Предложения
1 Пересадка 3 Пересадки 0 Пересадок
26. Небольшие уловки
● Все подписки имеют ссылку на родителя для
ускорения операции удаления
● Рядом с деревом хранится сортированный массив
сслылок на все листья дерева для ускорения
операции удаления из дерева
● Результат вычисления свойств документа кешируется
27. Как выглядит система в сборе
TRF
fast in-memory DSD
RailsApp
subscription interface
RailsApp
mail composer & sender
SMTP Server
MySQL
28. Масштабирование по кличеству
обрабатываемых документов
● Правила вставляются в оба дерева
● Обрабатываемый документ отправляется в одно из
деревьев
Rules Documents
Tree1 Tree2
29. Масштабирование по кличеству предикатов в
дереве
Rules Documents
Tree1 Tree2
● Правила вставляются в одно из деревьев
● Обрабатываемый документ отправляется в оба дерева
30. Ограничения решения
● Алгоритм становится неэффективным если один
документ подходит для значительной части дерева
● Дерево хранит данные в памяти, сохранность данных
обеспечивается внешними средствами
● Для добавления новых свойств документа нужен
перезапуск дерева
31. Что получилось: технологии
● Два процесса
● Два ядра CPU
● 1.5gb Памяти
● 1 000 000 документов в сутки
● 150K Писем в день
● Время ответа базы в среднем 28ms
32. Что получилось: бизнес
● Продукт был запущен через два
месяца после начала разработки
● Проект окупился в течении двух
месяцев после запуска
● Технология открыла дорогу новым
фичам продукта