Remote Config и A/B-эксперименты: история разработки и основные возможности
Привет, Хабр! Меня зовут Леша Жиряков, я техлид backend-команды витрины онлайн-кинотеатра KION: путь от разработчика до этой должности прошел за три года. Сейчас я продолжаю готовить свою серию статей и докладов про Python — всегда топлю за него, хочу, чтобы этот ЯП использовали в проде как можно чаще и больше. Сегодня расскажу про наш сервис Remote Config и A/B-эксперименты — это переработка одного из моих докладов. Если что, задавайте вопросы в комментариях — постараюсь на все ответить.
Что такое Remote Config и зачем он нам нужен
В приложении KION при запуске происходит запрос на сервер для получения конфигурации. На основе этих данных приложение определяет, в каком эксперименте оно участвует, какие витрины и полки должны отображаться, и применяет соответствующие настройки. Если серверная конфигурация недоступна или возникает ошибка, используется дефолтный Config, встроенный в приложение. Но Remote Config позволяет применять и то, чего нет в коде.
Как выглядит Config
Конфигурация в KION представлена в формате JSON, содержащем множество параметров. Среди передаваемых параметров есть два ключевых — hash и список экспериментов:
Hash используется для проверки актуальности конфигурации на устройстве. Это позволяет определить, нужно обновить конфигурацию или оставить текущую.
Список экспериментов передается устройству для идентификации, где именно оно участвует. Эта информация включается в каждое продуктовое событие, которое отправляется на сервер.
В аналитической базе данных сохраняются все события. Это позволяет оценивать ключевые показатели и учитывать участие устройства в экспериментах. Например, помогает определить, как повлияла на пользователя та или иная витрина или полка. В конечном счете все это важно для принятия продуктовых решений. Вот тестируем мы новую модель построения персональной витрины и видим результаты A/B-эксперимента. Если группа, у которой работала эта витрина, смотрела больше, чаще и дольше, мы оставляем модель в проде.
Для чего нужен Remote Config
Получать конфигурацию важно для обновлений без деплоя — так можно обойтись без yuml-файлов и перевыкатки. Это удобно, чтобы управлять фича-флагами — например, оперативно выключить что-то в пятницу. На бэке при поломке мы просто выключаем фичу, нам не нужно ничего выкатывать. Результат — в выходные ничего не поломается, и команда будет отдыхать, а не работать.
Конфигурация нужна и для проведения A/B-экспериментов и включения и выключения функций в реальном времени. Еще один плюс — настройка контуров окружений: prod, stage, dev. Общий Config наследуется, а отличия задаются только для отдельных параметров, таких как IP-адреса или хосты.
Почему свой продукт
Мы начали разрабатывать Remote Config из-за санкционных рисков. Была вероятность, что в какой-то момент все может перестать работать. К тому же создание собственного решения позволило вносить доработки под наши нужды.
Раньше использовался известный Remote Config от ИТ-гиганта, связанного с поиском, но он имел ряд ограничений. Например, сложности с таргетингом по версиям, проблемы с black- и white-листами, из-за чего некоторые пользователи просто не попадали в нужные списки. Собственный продукт избавляет от этих ограничений и позволяет нам развиваться на наших условиях.
Плюс свое решение можно сильно кастомизировать для себя, например применять списки топ-менеджеров, чтобы они не попадали в группы активных экспериментов. Потому что часто топ-менеджмент бывает не готов к авангардным решениям, которые тестируются. К примеру, вырезать просмотренный тайтл из баннерной карусели. Если это Originals или эксклюзив, в который много проинвестировали, и его не будет на главной полке витрины, могут не понять и сначала принять оргмеры, а потом разбираться (шутка).
Анализ готовых решений показал, что большинство из них ориентировано на веб. Среди их преимуществ — визуализация, интеграция с метриками, построение графиков и даже прогнозирование результатов A/B-экспериментов. Но они не подходят для нашего случая, так как мы работаем с шестью платформами, включая множество приложений, а не только веб.
Среди минусов — сложность доработки open-source-решений и ограниченность в проведении параллельных A/B-экспериментов, что важно для KION. Учитывая эти лимиты и относительную простоту реализации, мы решили разработать собственный Remote Config.
Главные фичи нашего Remote Config
Теперь расскажу подробнее об основных возможностях продукта. У системы изначально были такие ключевые функции:
Изменение конфигурации устройств в реальном времени. Позволяет оперативно вносить изменения без обновления приложения.
Система нотификации в Telegram. Уведомления о том, какой конкретный пользователь изменил параметр конфигурации.
Ролевая модель в рамках одного неймспейса. Поддержка администраторов с полными правами и юзеров с ограничениями на чтение или запись.
Независимые namespaces. Возможность создавать отдельные пространства конфигураций. Например, бэковые системы KION, продукты внутри МТС и все приложения KION используют собственные пространства.
A/B-эксперименты. Поддержка проведения тестов для анализа влияния изменений.
Наследование конфигураций. Позволяет не переписывать полностью конфигурацию для каждого окружения, а вносить изменения только в отдельных строках. Это полезно как для настройки production-конфигурации, так и для работы над новыми фичами. Можно создавать отдельные конфиги для тестирования, а потом интегрировать изменения в основной при релизе.
Есть и другие фичи:
полное кэширование данных, высокая скорость ответа;
вариативность конфигов по версии, модели, ОС;
создание своих вариаций;
разделение параметров по продуктовым командам;
поэтапная раскатка;
белые и черные списки.
Кэширование данных и вариативность конфигурации по версии и модели операционной системы обеспечивают гибкость и точность работы. Можно учитывать особенности разных платформ, где смарты получают индивидуальные конфигурации. Например, если новая фича недоступна для устаревших устройств, конфиг применяется только к актуальным моделям.
Система также поддерживает поэтапную раскатку. Это значит, что новая версия сначала применяется на 10% устройств, и это позволяет мониторить продуктовые метрики. А потом — убедиться, что приложение работает стабильно и количество сбоев не увеличивается. После успешного тестирования раскатка продолжается на остальные устройства.
Механизм белых и черных списков — обязателен. Онуправляет доступом к экспериментам. Ключевые пользователи (топы, которые хотят тестировать, и продакт-менеджеры) совершенно точно включаются в эксперименты. Причем даже в том случае, если проводится долгосрочное тестирование — например, фиксированное поведение для 1% юзеров без новых фич.
На иллюстрации выше — стандартная упрощенная схема работы. Приложения отправляют запросы на сервер. Все это построено на Django, за которым — Cache, PostgreSQL и Celery. События отправляются в Kafka и логируются через GrayLog.
Еще на схеме показан юзер — это человек, который управляет конфигурацией. Изначально использовалась Django-админка. Но со временем ее возможностей стало недостаточно, нам был нужен удобный поиск. Поэтому начали разрабатывать собственный GUI.
Как работает Remote Config
Дальше поговорим именно про удаленный Config. Реализация включает общие настройки Core. От них наследуются Config. Например, можно создавать продовый или стейджевый конфиг, указывая: «Наследуйся от такого Core».
Есть эксперименты и специальные параметры:
эксперименты — это когда задаются параметры для конкретного устройства с определенной версией;
специальные параметры — то же самое, но для фиксирования настроек после экспериментов. Когда новая фича с какой-то версией становится постоянной, ее нужно отдавать без разделения. Так что параметры собираются и передаются устройствам.
Здесь представлена детальная схема. Видно, что Core выступает базовой конфигурацией, от которой наследуется, например, продовый конфиг.
Основные параметры включают модель устройства, операционную систему, версию и client id — он используется как ключ для экспериментов. При создании необходимо указать, на что ориентироваться: client id, device id или другие параметры. Эти данные задаются для корректной работы экспериментов и настройки конфигураций.
Как собирается конфиг
Все достаточно просто. Например, телевизор делает запрос. У него есть client id. SDK указывает модель, версию, в ответ ему возвращаются параметры. Список экспериментов и параметр hash нужны для понимания, применять этот параметр или нет, есть ли там что-то новое.
Вот более подробно про механизм наследования. Есть дефолтная конфигурация с двумя ключами. Видно, что потом создается еще один Config, где перезаписывается второй ключ.
У каждого эксперимента есть Core и параметры — это работает так же, как с обычным Config. Например, при запуске эксперимента с двумя группами нужно задать 20 параметров, из которых 19 — общие. Чтобы не прописывать их дважды, создается общий Config, от которого наследуются группы. К каждой указывается: «Наследуйся от этого, тут такой атрибут, а здесь — другой».
Дальше применяются специальные атрибуты, которые перезаписываются в зависимости от наследования. Последовательность настроек соблюдается в этом порядке.
A/B-эксперименты
С Remote Config они вполне обычные.
Попадает ли человек в покрытие эксперимента? Например, если эксперимент раскатывается на 10%, то при поступлении запроса система должна определить, входит ли человек в эти 10%. Для этого проверяется, соответствует ли запрос установленным критериям покрытия.
Какой Config ему вернуть и в какой эксперимент он попал? После проверки покрытия определяется, в каком из экспериментов участвует пользователь. Дальше возвращается соответствующий Config, включающий параметры для его группы.
Это достаточно просто вычислить. При создании каждого эксперимента формируются соль покрытия и соль Config, которые представляют собой хэши.
Механизм работает так:
приходит client id;
client id складывается с солью покрытия, затем берется их хэш;
полученное число делится на 100. Берется остаток от деления, который дает значение от 0 до 100;
если это число меньше процента покрытия, например 10%, пользователь не попадает в эксперимент. Если больше, то да.
Какой Config возвращается клиенту? Здесь уже роль играет не соль разбиения, а соль Config, которая тоже сгенерирована хэшем. Берется ключ разбиения, который зависит от контекста. Например, если эксперимент в авторизованной зоне, используется profile id, если нет — device id. Складываем ключ разбиения с солью Config, считаем хэш, остаток от деления и определяем, в какой чанк попал клиент.
Под капотом система работает не с процентами, а именно с чанками, и в зависимости от результата возвращается нужный Config. Благодаря соли Config можно проводить независимые эксперименты. Например, на витрине могут одновременно идти 2–3 из них. Для нового генерируется своя соль, что гарантирует равномерное распределение групп. В итоге влияние предыдущих исключается, и результаты текущего эксперимента становятся максимально точными.
Работа с чанками позволяет делить аудиторию на равные доли. Например, когда нужно разделить на три одинаковые группы, это делается легко благодаря механизму чанков. В одном из случаев мы запускали два эксперимента, но по важным причинам потребовалось срочно разделить один из них на два дополнительных. Используя соль Config, это удалось реализовать быстро и без проблем.
Группы не перемешиваются и продолжают участвовать в эксперименте.
Ну и, соответственно, задаем белые и черные списки. Это все применяется на этапе получения конфигурации.
Заведение параметров
Заведение параметров тоже реализовано удобно. Часто нужно загрузить какой-нибудь сложный JSON, и важно, чтобы он не ломался, если вдруг возникает ошибка. Это позволяет минимизировать риски и обеспечить стабильность системы.
Для удобства работы с параметрами реализована подсветка синтаксиса. Это дает возможность сразу видеть ошибки, например отсутствие запятой. Еще можно задавать тип данных — число или строку. Тогда на клиенте не будет ошибок при обработке данных.
Удобство заведения параметров дополнено историчностью. Обязательно фиксируется, кто и когда изменил атрибуты. Мы добавили поле для указания причины, чтобы не нужно было искать по чатам, кто и почему принял такое решение. Теперь при изменении параметра это поле обязательно заполняется и отображается. Так что становится понятно, кто и что именно изменил — и почему он это сделал.
Этих Config действительно много, как и продуктовых вертикалей и команд. Раньше были проблемы с документацией, но мы решили их, добавив описание к каждому параметру и указание соответствующей вертикали.
Нотификация
Теперь при открытии Config сразу видно, какая команда за него отвечает и для чего он нужен. Еще настроена нотификация: если кто-то вносит изменения, уведомление приходит сразу. Это удобно, так как команды следят, чтобы ничего не изменялось после 17:00, особенно в пятницу — выше уже писал, что на выходных сотрудникам нужно отдыхать.
Про производительность: при работе с кэшем система выдерживает на одно ядро 1 000 RPS с задержкой в 30 миллисекунд. Такая конфигурация полностью удовлетворяет нашим требованиям. Настроен автоскейлинг.
Вот и все: поделился всем, чем хотел. Вопросы оставляйте в комментариях!
Еще мои посты: