Всем привет! Меня зовут Саша Борзых, я работаю в отделе эффективности платформы Яндекс Такси, руковожу сектором разработки ценообразования.
В статье расскажу, как мы переделали платформу динамического ценообразования, сокращая time to market, то есть уменьшая время с момента появления идеи до ее проверки в продакшене. Посмотрим, что у нас получилось и какой опыт из этого можно почерпнуть. Поделюсь нашей игрой Surge, в которой можно в тестовом формате понять, как работает ценообразование сервисов в разных ситуациях.
Как работает бизнес Такси
Такси — такой вид организации рынка, когда у вас с одной стороны пользователи, которые хотят получить услугу, а с другой — исполнители заказов, которые могут эту услугу предоставить. В этом бизнес Такси похож на другие наши сервисы: в Еде есть курьеры, в Лавке есть курьеры, в Доставке тоже есть курьеры. Мы организуем рынок внутри платформы.
![](/posts/dynamic-pricing-platform-2024-06-15/images/1.jpg)
Здесь важен баланс. Нарушение баланса — это, например, большой спрос при нехватке предложения. В этом случае у нас возникает дефицит: люди заходят в приложение Такси и видят «Нет свободных машин». И наоборот, если цены неадекватно высокие, то возможна ситуация, когда люди заходят, машины есть, но по такой цене большинство не поедет, а на линии оказывается огромное количество исполнителей без заказов. Всё это неэффективно.
Любому маркетплейсу важно адекватно поддерживать баланс за счет цены, и мы ищем равновесную цену, при которой у нас всегда есть свободные исполнители, а цены настолько низкие, насколько возможно.
Что такое Surge
Surge (мы говорим «сурж») — это коэффициент повышения спроса. Его расчетом занята целая система, которая в реальном времени следит за тем, что происходит в бизнесе: сколько желающих уехать, сколько исполнителей на линии и так далее. И эта система выставляет соответствующие коэффициенты, поддерживает баланс спроса и предложения.
В ядре Surge — формула, которая принимает на вход множество параметров: точки А и Б заказа, число исполнителей в окрестности, тариф, число желающих уехать из точки А... На выходе — число, коэффициент, который нужно применить к базовой цене поездки или услуги. Пользователи Яндекс Go видят это так: открывают приложение, там появляется значок молнии, и фича «Индекс спроса» показывает, почему цена сейчас выше.
![](/posts/dynamic-pricing-platform-2024-06-15/images/2.jpg)
![](/posts/dynamic-pricing-platform-2024-06-15/images/3.jpg)
Водители в приложении Яндекс Про видят тепловую карту Surge — она у них фиолетовая. Карта влияет на поведение водителей: если они видят, что сейчас в их зоне повышенный спрос, — это мотивирует их оставаться там. И наоборот, если в их зоне спроса нет, а где-то в двух километрах он возник, водители перемещаются туда.
Если хотите посмотреть, как работает Surge на практике, зайдите в нашу игру и проверьте, сможете ли вы поддерживать баланс цен.
Старый и новый флоу разработки
В отделе эффективности платформы, где создаются Surge и Dispatch (система назначения водителей на заказ), типичная команда разработки состоит из тимлида, нескольких разработчиков и аналитика, который обычно одновременно и product owner. Вместе они постоянно думают, как что улучшить: смотрят метрики, выдвигают гипотезы, работают, в общем.
Разработка нового функционала выглядит так:
![](/posts/dynamic-pricing-platform-2024-06-15/images/4.jpg)
Большинство задач, создаваемых в Surge, — это изменение формулы расчета. Конечно, сам сервис — это не только формула. Там есть кеши, источники данных, разные статистики. Но большая часть всех задач от всех аналитиков — именно на расчет формулы. И мы подумали: почему бы не отделить формулу от основного сервиса? Дадим аналитикам инструменты для ее изменения, и если они касаются только формулы, они смогут быстро воплощать гипотезы без привлечения разработки.
![](/posts/dynamic-pricing-platform-2024-06-15/images/5.jpg)
В этом случае после появления идеи и заведения тикета аналитики сами меняют код расчета. Изменения проходят аудит: другие аналитики смотрят, всё ли в порядке с формулой. И если да — мы включаем ее и радуемся. Сравним старый флоу и оптимизированный.
До | После |
---|---|
— идея; | — идея; |
— тикет; | — тикет; |
— обсуждение с разработкой; | — изменение кода расчета; |
— планирование; | — аудит изменений; |
— разработка с feature-flag; | |
— code review; | — включение; |
— релиз; | — успех. |
— включение; | |
— успех; | |
— тикет удаления feature-flag. |
И мы эту очень заманчивую схему реализовали! Основной упор при этом сделали на то, чтобы отселить всё, связанное с формулой расчета Surge, в отдельный сервис. Аналитики правят формулу, а разработчики занимаются рантаймом: тем, чтобы формула выполнялась, в нее прокидывались дополнительные данные из сторонних сервисов. Так и поделили ответственность.
Архитектура новой платформы
Surge нужен всем, и нет смысла делать его только для Такси. Мы решили воплощать платформенное решение. В каждом похожем бизнесе — в Еде, Доставке, Лавке — будет команда из аналитика и разработчиков, которые смогут эту платформу использовать, не изобретая собственные велосипеды.
Подобьём требования для создания платформы:
- Быстрый расчет. Нужно уметь считать Surge быстрее, чем за 300 мс. Нагрузка на Surge в Такси — больше 10 000 RPS, для достижения этой скорости мы всё напишем на C++.
- Низкий time to market. Это смысл всей затеи: делать большую часть работы аналитиков без привлечения разработки.
- Возможность dry run. Dry run — режим проведения эксперимента, когда новый код запускается параллельно с основным, не влияя на расчеты в проде. Аналитик смотрит на логи продового и нового вычислений, подтверждает, что это ожидаемое поведение, и копирует изменения уже в прод.
- Показ актуальной карты спроса в Яндекс Про. Усложняется тем, что исполнителей на линии много: сегодня RPS на отдачу карты спроса больше 200 000.
- Мониторинг. Нужно с минимальными задержками понимать, когда в системе что-то не так. Сам сервис Calculator, может быть, и не «пятисотит», но если при этом коэффициент Surge считается некорректно, то это однозначно приведет к дисбалансу рынка. Нас завалит заказами, если Surge низкий, а если наоборот — заказы упадут.
Так выглядит ландшафт новой платформы:
![](/posts/dynamic-pricing-platform-2024-06-15/images/6.jpg)
- сервис Calculator занимается расчетом Surge;
- сервис Calculator-storage хранит результаты этих расчетов;
- фронтенд-интерфейс позволяет аналитикам в админке менять код расчетов;
- во внешнем S3-хранилище лежат карты Surge, которые отображаются в приложении, — отдельный сервис Renderer отдает эти карты в приложение Яндекс Про.
Поговорим отдельно о компонентах. В основе платформы лежит библиотека JS-pipeline, созданная специально для этой задачи, но тем не менее не привязанная конкретно к Surge. Библиотека подробно описана на Хабре.
![](/posts/dynamic-pricing-platform-2024-06-15/images/7.jpg)
JS-pipeline просто дает нам запускать в плюсовом сервисе JS-код, написанный аналитиком, при помощи движка V8. А сервис Admin-pipeline хранит внутри себя весь JS-код, написанный аналитиками, со всей историей драфтов. Аналитик сделал изменение — создается драфт, некий патч к основному коду, другой аналитик проверяет его на предмет аналитической вшивости, а разработчики проверяют на undefined и всё остальное, в чём они понимают больше. И убедившись, что с изменением все хорошо, нажимают на кнопочку в интерфейсе.
Для админки сервис Admin-pipeline просто предоставляет REST-интерфейс с очень небольшой нагрузкой — 1 RPS. При этом библиотека JS-pipeline периодически выкачивает из этой админки, из ее базы, актуальный код.
![](/posts/dynamic-pricing-platform-2024-06-15/images/8.jpg)
Сервис Calculator общается с сервисом базового прайсинга. Когда клиент Яндекс Go открывает приложение и вбивает точки А и Б, этот запрос падает в сервис базового прайсинга, а он идет в Calculator, чтобы узнать тот самый коэффициент повышенного спроса, который нужно применить к цене. Calculator внутри использует библиотеку JS-pipeline и реализует весь код, необходимый для работы этой формулы.
Иногда аналитики просят добавить в формулу новые сущности или данные, полученные из сторонних сервисов, например, какие исполнители сейчас находятся рядом с точкой А и расстояние до них. Так как из JS мы не даем ходить во внешний мир, то это делают разработчики: пишут код на C++, который пробрасывает эти данные для формулы.
Сервис Calculator общается с сервисом Calculator-storage, где хранятся расчеты Surge. А еще Calculator пишет свои вычисления в Message Broker. У нас это Logbroker, у вас может быть Kafka, не принципиально.
![](/posts/dynamic-pricing-platform-2024-06-15/images/9.jpg)
Внутри Calculator — просто ручка с огромной обвязкой. Есть ручка Calc-surge, и базовый прайсинг приходит именно в нее. Мы параллельно запускаем два пайплайна: текущий продовый, который видят все, и измененный продовый, который аналитик хочет выкатить экспериментально. Пайплайны запускаются параллельно, мы дожидаемся результатов и наружу отдаем продовый расчет, а себе в Logbroker пишем результаты обоих и складываем продовые значения в Calculator-storage. Этот сервис держит 10 000 RPS.
![](/posts/dynamic-pricing-platform-2024-06-15/images/10.jpg)
Сервис Calculator-storage агрегирует расчеты Surge, сохраняет их себе в Redis и периодически генерирует карту спроса по расчетам в разных точках: интерполирует, рисует карту и складывает ее в хранилище S3. S3 выбран потому, что карты получаются довольно тяжелыми — 30–70 МБ, и удобнее использовать объектное хранилище, чем, например, складывать напрямую себе в Postgres. Сервис держит порядка 400 RPS, поскольку не все расчеты, которые приходят в Calculator, нужно сохранять для генерации карты.
![](/posts/dynamic-pricing-platform-2024-06-15/images/11.jpg)
И последний сервис — Renderer — выкачивает из S3 карты, которые сгенерировал Calculator-storage, и в виде PNG-тайлов отдает клиенту Яндекс Про.
![](/posts/dynamic-pricing-platform-2024-06-15/images/12.jpg)
Почему именно PNG? Причины две:
- Раньше мобильные телефоны у исполнителей были не очень быстрые, и отдавать задачи рендеринга на них не хотелось.
- Мы хотим уметь влиять на отображение карты: банально менять цвета или, возможно, гексагональную сетку на другую полигональную. И мы решили это делать в своем сервисе.
Renderer сейчас отдает все карты спроса всем пользователям Яндекс Про и держит порядка 200 000 RPS.
Профиты новой платформы
У нас получился калькулятор, который можно использовать в любом похожем сервисе со спросом и предложением:
- Можно буквально скопировать калькулятор себе и получить аналогичный, например, для Доставки.
- После этого можно создать пайплайн в админке Admin-pipeline и сказать, что ваш калькулятор будет запускать пайплайны из нее.
- При необходимости можно доработать Renderer так, чтобы изменить отображение карт спроса, дописать логику: если пришел исполнитель из Еды — одно отображение, из Такси — другое. Это возможно, поскольку все наши исполнители работают через Яндекс Про.
Немаловажный блок посвящен аналитике. Как перемещаются данные, показано на картинке:
![](/posts/dynamic-pricing-platform-2024-06-15/images/13.jpg)
- клиент Яндекс Go обращается в базовый прайсинг — прайсинг идет в Calculator;
- Calculator все логи пишет в Message Broker;
- логи перекладываются в ClickHouse;
- Atlas web UI по данным ClickHouse отображает метрики уже перед аналитиком.
Метрики выглядят просто. В ClickHouse лежат все расчеты по всем географиям. Surge — просто некая цифра, и можно взять, например, все расчеты в конкретном городе и построить два графика: экспериментальный пайплайн и продовый. Аналитики видят, что в экспериментальном пайплайне есть изменение, открывают UI, он строит по данным из ClickHouse два графика. Специалисты сравнивают, видят, что Surge начал считаться иначе (как они и ожидали), и переносят изменение в продовый пайплайн.
Цифры и выводы
27 процессов уже используют платформу JS-pipeline. Хотя сама библиотека очень активно применяется в Surge, но не привязана к нему напрямую. В Райдтехе ее используют для расчета цен и в других местах.
С новым флоу аналитик, не дергая разработчиков, за 30 минут проводит эксперимент, который раньше занимал минимум 3 часа. Если раньше аналитик хотел поменять в формуле синус на косинус — требовалось завести пул-реквест, пройти тесты, зарелизиться, включить. А за счет того, что мы сделали обособленную библиотеку, стало возможно интегрировать в код сервиса, написанного на C++, отдельную часть на JS, которую можно будет в рантайме менять и использовать в любых областях и для любых задач. По похожей схеме у нас в Такси работает, например, Dispatch, делая скоринг водителей на заказы.
Ключевой вывод: очень полезно исследовать то, как у вас устроены процессы, и то, какими задачами вы по большей части заняты. Начав изучать это, вы, возможно, откроете, что часть задач в написании кода можно переложить на аналитиков или продактов. Научите их джаваскрипту — и таким образом снизите time to market.
Смотрите, как работает Surge на практике, в нашей игре.