Pull to refresh

Comments 30

Приятно, что и российские компании постепенно приходят к идее использования современных open-source инструментов и архитектур, отказываясь от тех же исторических WebLogic + Oracle DB в подобных системах

Небольшой вопрос касательно архитектуры: у вас используются Aerospike, MongoDB и HBase, все они в той или иной мере key-value store. Можете пояснить, в чем идея такого разбиения и какие принципиальные кейсы вынесены на Aerospike и MongoDB, с которыми не справился бы HBase?
Aerospike — хранит уже готовые профили пользователей, например таблица visitor_id; audiences[]. Когда в DSP приходит RTB-запрос, то используется именно Aerospike. Здесь пока ни одна другая база не показывала такие результаты быстродействия, низкий latency, и низкую загрузку процессора.

Mongo — хороша для кодеров, когда нужно сохранить объект в базу. Здесь Mongo со своей документ-ориентированной архитектурой вне конкуренции. Нагрузки практически не держит. В общем эта база только под специфические задачи осталась.

HBase — у нас пришла на смену Mongo, но пока не везде смогла вытеснить ее из-за ограничений. HBase интегрирован в кластер Hadoop и ее реально можно настроить на высокую отказоустойчивость и быстродействие. Также очень важна рандомная запись/чтение, что Mongo ну совсем никак не настроить

FrostNova Кстати а, что здесь Mongo делает, мы вроде ее уже выпилили?
В MongoDB хранятся аудиторные условия. Учитывая то, что они могут быть представлены фактически любой структурой — MongoDB как нельзя кстати.
Вы в статье упустили важную часть, как раз про описание аудиторий, а также про модель данных (Event). В общем вся суть для чего мы вообще эту архитектуру задумали) Получилось очень сильно про Spark, но мало — какую задачу решает.
Хорошая архитектура. Я бы сделал почти так же
Исторически так сложилось, что MongoDB мы использовали во многих проектах. Постепенно мы уходим от ее использования, но все же для хранения сложной структуры данных она пока что незаменима. Что касается Aerospike, то тут все просто — для RTB нужен быстрый отклик с учетом обращения к DB и обработки.
За счёт частичного внедрения лямбда архитектуры, повысилось переиспользование кода.

Интересно, каким образом.

Вообще, спарк — какой-то обрезок, пригодный вот для таких SQL-like обработок. Чуть что сложнее — лучше даже не связываться с ним. Одно только остутствие доступа из воркера к конфигурации чего стоит (она ж несереализуема, вплоть до 1.4.0).
Интересно, каким образом

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

Позвольте спросить: зачем Вам доступ к конфигу из воркера? Можно в драйвере конфиг посмотреть и уже через драйвер воркеры могут получить интересующий Вас параметр.
А если у меня этих параметров штук 20, к примеру? И Воркеры — объекты классов, определённых где-то в другом месте.
На счет количества параметров — не вижу никакой проблемы. А для объектов классов для подключаемых библиотек Вы правы — ошибки не избежать. Но кто вам мешает использовать ту самую (1.4.0 и выше) версию?
Скорее всего я неправ, что «наезжаю» на спарк, просто не моё это — сердцу родней старый добрый mr, хотя, дни его сочтены. Скала и вся эта функциональщна всё портит.
А причём тут «лямбда архитектура»? Без неё никак не получалось использовать не только ту же логику но и тот же самый код?
Смысл «лямбда архитектуры» лежит во фразе: «большие и быстрые» данные. Потоковая обработка данных отвечает за «быстрые», пакетная за «большие», а переиспользование кода я считаю частью «лямбда архитектуры», так как в обоих случаях обрабатываются одинаковые данные.
Возможно я неправильно понимаю смысл «лямбда архитектуры» и хотел бы Ваш комментарий по этому поводу.
Я просто к тому, что то же самое можно получить и без неё. Я, честно говоря, далёк от всех этих функциональных штучек.
Spark далеко не обрезок, и, наоборот, по сравнению с классическим MR предоставляет абсолютный контроль над обработкой данных. Напишите пример задачи обработки, чтобы понять, где Spark будет лажать
Спарк всего лишь оптимизирует граф выполнения и позволяет кешировать промежуточные результаты в памяти. Контроля над обработкой данных так столько же, сколько в классическим MR, если не меньше. По крайней мере, в классическим MR многие вещи более логичны и очевидны, в то время как в спарке они реализуется как хаки, с использованием особенностей тех или иных функций (setup/cleanup, двойная сортировка и т.п.).

Пример задачи где спарк будет лажать я уже привёл, банально передать конфигурацию в воркеры. Ну и да, тот случай, когда промежуточных данных больше, чем оперативной памяти.
Наверное, здесь произошла «типичная подмена понятий». Вы имели в виду из executor'а (исполнителя юзерских лямбд)? А какого рода конфигурация нужна? Для своих нужд я вполне обхожусь broadcast'ами, или сериализуемым своим классом с настройками.
Нет, я говорю о воркерах, и о более сложных обработках. Когда эти обработки — не «юзерские лямбды» и не замыкания, а экземпляры классов, имплементирующих хотя бы VoidFunction. Как броадкастить то, что не сериализуется? Зачем педалить свой класс, когда есть конфиг спарка, из контекста можно получить конфиг хадупа и т.п.?
У Вас в драйвере по таймеру запускается updateConditions(), который модифицирует rdd.
1.) Насколько я понимаю, размер этой коллекции должен быть мал, т.к. она должна быть послана через broadcast на всех executor'ов — это так? Если нет — расскажите, пожалуйста.
2.) У меня в приложении есть такая же необходимость — со временем обновлять некий конфиг, и доставлять его на executor'ов. Но, согласно документации, чтобы применилось синхронно на всех executor'ах, это должен быть либо ручной broadcast, либо неявный — через сериализатор лямбд. Недокументировано то, что при обновлении в драйвере, изменения разъедутся по executor'ам. Насколько стабильно/синхронно у Вас применяются эти изменения? Или в Вашем случае допустимо несинхронное применение изменений, и Вы о нем знаете?
1) Да, действительно, размер этой коллекции достаточно мал.
2) Для нашего потокового обработчика некритична небольшая разница в синхронизации. В любом случае пакетный обработчик перестроит все аудитории под текущее состояние условий. На счет стабильности: на драйвере иногда можно встретить OutOfMemoryError. Происходит это из-за того, что при использовании persist() или cache() на драйвере накапливается информация, которая совсем не хочет очищаться автоматически (точные причины почему это происходит мне, увы, не известны). Проблема решилась добавлением System.gc() в конце updateConditions().
А почему не Cassandra? Ее рассматривали?
Если Cassandra вместо Spark, то смысла не вижу, тк основное требование у нас — это гибкость обработки и возможность использовать обычный язык программирования. Что бы там не предлагала Кэсси — мы всегда будем зависеть о ее ограничений. Если заметили, мы в реалтайме еще собираем HyperLogLog каждого аудиторного сегмента.

Из баз данных, которые из коробки предлагают все, что нам нужно было в этой задаче — VoltDB. Но я не могу ручаться за то, что с ней не было бы каких-то косяков и ограничений.
Кассандра вместо HBase, а Spark умеет с данными в ней работать через DataStax-овский коннектор.
Тут скорее ответ выглядит так: у кого какой опыт с той или иной базой. Чтобы потестить базу в High Load проектах, нужно с этой базой в реальных условиях пожить 3-5 месяцев, чтобы узнать все плюсы и минусы. Так что HBase выбрали исходя из предыдущего опыта, а Cassandra испытывалась только на локальном компе)
Есть ли у вас статы и репорты (dims/metrics) — с часовой\дневной\месячной и т.п. гранулярностью? Если да — где вы все это добро храните?
Мы используем HyperLogLog для сохранения статистики по аудиториям. Потоковый обработчик каждые 10 минут обновляет состояние аудиторий за текущий час. Пакетный обработчик сохраняет статистику за весь период. В итоге комбинируя статистику за весь период и часовую можно получить информацию по аудиториям с точностью до 10 минут. Все HyperLogLog храним в MongoDB. Остальные метрики (количество обработанных данных, время работы и т.п.) отправляются на хранение в Graphite.
Интересно измеряли ли вы эффективность вашей рекламы в зависимости от задержки обработки/показа. Очевидно, что при нулевой задержке она будет неэффективна, так как человек еще находтся на целевой странице. На бесконечности тоже нулевая эффективность. Значит где-то есть максимум хоть один. Так вот где он, через 20 секунд, 20 минут, 20 часов или 20 дней?

Ну и конечно вопрос, зачем мне показывать лобзик, который я уже купил? Но это похоже вопрос риторический :-)
На самом деле оба вопроса риторические, но на оба я и отвечу =)
Для рекламных кампаний разных направленностей существуют различные точки эффективности. Когда то это долгосрочный интерес, когда то краткосрочный. Соответственно и пользователь с разными типами интересов по разному реагирует на рекламу.
Приведу один пример человек готовится к путешествию и бронирует себе отель, покупает билеты и прочее. Так вот в этом случае среднее время поиска человека 17 дней, в течении которых происходит 6 сессий поиска. Человек в среднем посещает 18 сайтов и делает 7 кликов по рекламе. Соответственно в других типах интересов, другие цифры. Мы ответственно подходим к исследованию основных шаблонов для различных типов интересов, и уже имеем достаточно информации чтобы обеспечить потребности большинства наших клиентов.
А на второй вопрос ответ проще. С сайтов устанавливающих наш код приходит информация о состоянии пользователя, на какой стадии необходимого действия он остановился. Если целевое действие продать, а пользователь остановился лишь на добавлении товара в корзину, мы используем один тип ремаркетинговых действий, если пользователь остановился на оформлении заказа — другой. А если вы уже купили лобзик и целевой действие совершено, мы вас оставляем в покое. Может только иногда будем показывать рекламу лобзика, чтобы вы были удовлетворенны покупкой ещё больше ( шутка =) )
Sign up to leave a comment.