В 2022 году с приложениями Netflix для iOS и Android произошли серьезные изменения. Мы перевели мобильные приложения Netflix на GraphQL с нулевым временем простоя, что повлекло за собой полное перепроектирование от клиента до уровня API.

До недавнего времени наши мобильные приложения работали на внутреннем API-фреймворке Falcor. Теперь они поддерживаются Federated GraphQL — распределенным подходом к API, при котором доменные команды могут независимо управлять и владеть определенными разделами API.

Сделать это безопасно и без сбоев для 100 миллионов клиентов — чрезвычайно сложная задача, особенно учитывая множество аспектов изменений. В этой статье мы расскажем о широко применяемых методах (помимо GraphQL), которые мы использовали для осуществления этой миграции. Мы обсудим три стратегии — A/B-тестирование, Replay Testing и Sticky Canaries.

Детали миграции

Прежде чем перейти к рассмотрению этих техник, давайте вкратце проанализируем план миграции.

До появления GraphQL: Монолитный API Falcor, реализованный и поддерживаемый командой API 

До перехода на GraphQL наш слой API состоял из монолитного сервера, построенного с помощью Falcor. Команда разработчиков API поддерживала как Java-реализацию фреймворка Falcor, так и сервер API.

Этап 1

Создание GraphQL Shim Service поверх существующего монолитного API Falcor.

К лету 2020 года многие UI-инженеры были готовы к переходу на GraphQL. Вместо того чтобы приступить к полномасштабной миграции сверху вниз, мы создали GraphQL Shim поверх существующего API Falcor. GraphQL Shim позволила инженерам быстро перейти на GraphQL, разобраться с проблемами на стороне клиента, такими как нормализация кэша, поэкспериментировать с различными клиентами GraphQL и исследовать производительность клиента, не будучи заблокированными миграциями на стороне сервера. Для безопасного запуска этапа 1 мы использовали A/B-тестирование.

Этап 2

Отказ от GraphQL Shim Service и Legacy API Monolith в пользу GraphQL-сервисов, принадлежащих доменным командам.

Мы не хотели, чтобы устаревший Falcor API существовал вечно, поэтому мы обратились к Federated GraphQL, чтобы использовать единый GraphQL API с несколькими серверами GraphQL.

Мы также могли поменять реализацию с GraphQL Shim на Video API с помощью директив интеграции. Чтобы безопасно запустить второй этап, мы использовали Replay Testing и Sticky Canaries.

Стратегии тестирования: Резюме

Наши стратегии тестирования определялись двумя ключевыми факторами:

  • Функциональные и нефункциональные требования

  • Идемпотентность

Если мы тестировали функциональные требования, например точность данных, и если запрос был идемпотентным, мы полагались на Replay Testing. Мы знали, что можем протестировать один и тот же запрос с одними и теми же входными данными и неизменно ожидать одних и тех же результатов.

Мы не могли повторно протестировать запросы GraphQL или мутации, которые запрашивали неидемпотентные поля.

И мы определенно не могли воспроизвести тестирование нефункциональных требований, таких как кэширование и логирование взаимодействия с пользователем. В таких случаях мы тестировали не данные об ответах, а общее поведение. Поэтому мы полагались на более высокоуровневое тестирование на основе метрик: A/B-тестирование и Канареечное тестирование.

Давайте обсудим эти три стратегии тестирования подробнее.

Инструмент: A/B-тестирование

Netflix традиционно использует A/B-тестирование, чтобы оценить, насколько новые фичи нравятся покупателям. На первом этапе мы использовали систему A/B-тестирования, чтобы разделить сегмент пользователей на две группы общей численностью 1 миллион человек. Трафик контрольной группы использовал устаревший стек Falcor, а экспериментальная группа использовала новый клиент GraphQL и была направлена на GraphQL Shim. Чтобы определить влияние на клиентов, мы могли сравнить различные показатели, такие как количество ошибок, задержки и время рендеринга.

Мы провели A/B-эксперимент на стороне клиента, в ходе которого протестировали Falcor в сравнении с GraphQL и собрали грубые показатели качества обслуживания (QoE). Результаты A/B-эксперимента намекнули на то, что корректность GraphQL не соответствует унаследованной системе. Следующие несколько месяцев мы погружались в эти высокоуровневые метрики и исправляли такие проблемы, как TTL кэша, ошибочные клиентские предположения и т. д.

Победы

Высокоуровневые показатели здоровья: A/B-Тестирование обеспечило нам необходимую уверенность в том, что мы полностью внедрили GraphQL на стороне клиента. Это помогло нам успешно перевести 100% трафика на мобильной главной странице на GraphQL за 6 месяцев.

Проблемы

Диагностика ошибок: При A/B-тестировании мы могли видеть грубые метрики, которые указывали на потенциальные проблемы, но диагностировать точные проблемы было сложно.

Инструмент: Replay Testing — валидация в масштабе

Следующим этапом миграции была реализация существующего API Falcor на сервере GraphQL (Video API Service). API Falcor превратился в монолит, перегруженный логикой, с более чем десятилетним техническим долгом. Поэтому мы должны были убедиться, что переработанный сервер Video API не содержит ошибок и идентичен уже готовому сервису Shim.

Мы разработали инструмент Replay Testing для проверки правильности миграции идемпотентных API из GraphQL Shim в сервис Video API.

Как это работает?

Фреймворк Replay Testing использует директиву @override, доступную в GraphQL Federation. Эта директива указывает GraphQL Gateway на необходимость маршрутизации к одному GraphQL-серверу, а не к другому. Возьмем, к примеру, следующие две схемы GraphQL, определенные сервисом Shim и сервисом Video:

На первом этапе GraphQL Shim определил поле certificationRating (Rated R или PG-13). На этапе 2 мы создали сервис VideoService и определили то же самое поле certificationRating, помеченное директивой @override. Наличие идентичного поля с директивой @override позволило шлюзу GraphQL направить разрешение этого поля новому Video Service, а не старому Shim Service.

Инструмент Replay Tester производит выборку необработанных потоков трафика из Mantis. С помощью этих выборочных событий инструмент может захватить запрос из продакшена и запустить идентичный GraphQL-запрос как к GraphQL Shim, так и к новому сервису Video API. Затем инструмент сравнивает результаты и выводит различия в ответах.

Примечание: Мы не воспроизводим персональную информацию, идентифицирующую личность. Она используется только для нечувствительных фичей продукта в пользовательском интерфейсе Netflix.

После завершения теста инженер может просмотреть диффы, отображенные в виде поля JSON. В круглых скобках слева от запятой указано контрольное значение, а справа — экспериментальное.

/data/videos/0/tags/3/id: (81496962, null)
/data/videos/0/tags/5/displayName: (Série, значение: "S\303\251rie")

Мы зафиксировали два различия: в первом случае отсутствовали данные для поля ID в эксперименте, а во втором — разница в кодировке. Мы также увидели различия в локализации, точности даты и точности плавающей точки. Это дало нам уверенность в репликации бизнес-логики, где планы подписки и географическое положение пользователя определяли доступность каталога для клиента.

Преимущества

  • Уверенность в равноценности между двумя реализациями GraphQL.

  • Возможность настройки конфигураций в случаях, когда данные отсутствовали из-за слишком частых таймаутов.

  • Протестирована бизнес-логика, требующая множества (неизвестных) входных данных, корректность которых трудно определить на глаз.

Проблемы

  • Персональные данные и неэдемпотентные API не должны тестироваться с помощью Replay Tests, и было бы полезно иметь предотвращающий механизм.

  • Вручную составленные запросы хороши лишь настолько, насколько хороши те фичи, которые разработчик не забыл протестировать. В итоге мы получили непроверенные поля просто потому, что забыли о них.

  • Корректность: Идея корректности тоже может быть запутанной. Например, правильнее ли, чтобы массив был пустым или нулевым, или это просто шум? В конечном итоге мы максимально соответствовали существующему поведению, поскольку проверить надежность обработки ошибок клиента было сложно.

Несмотря на эти недостатки, Replay Testing стало ключевым показателем того, что мы достигли функциональной корректности большинства идемпотентных запросов.

Инструмент: Sticky Canary

Хотя Replay Testing подтверждает функциональную корректность новых API GraphQL, оно не дает представления о производительности или бизнес-метриках, таких как общее восприятие взаимодействия с пользователем. Одинаково ли часто пользователи нажимают на кнопки воспроизведения? Успевает ли игра загрузиться до того, как пользователь потеряет к ней интерес? Replay Testing также нельзя использовать для проверки API, не являющегося идемпотентным. Для повышения уверенности мы обратились к инструменту под названием Sticky Canary.

Sticky Canary — это инфраструктурный эксперимент, в ходе которого клиенты назначаются либо на канареечный, либо на базовый хост на весь период эксперимента. Весь входящий трафик распределяется между экспериментальным и базовым хостом на основе пользовательского устройства и профиля, подобно тому как выбирается ячейка для некоторого значения при хешировании. Экспериментальный хост обслуживает всех клиентов, участвующих в эксперименте. Посмотрите доклад Chaos Engineering на AWS Reinvent, чтобы узнать больше о Sticky Canaries.  

В случае с нашими API GraphQL мы использовали эксперимент Sticky Canary для запуска двух экземпляров шлюза GraphQL. Базовый шлюз использовал существующую схему, которая направляла весь трафик на GraphQL Shim. Экспериментальный шлюз использовал новую предложенную схему, которая направляет трафик на новейший сервис Video API. Zuul, наш основной пограничный шлюз, распределяет трафик между кластерами в зависимости от параметров эксперимента.

Затем мы собираем и анализируем производительность двух кластеров. Некоторые KPI, которые мы тщательно отслеживаем, включают:

  • время задержки

  • количество ошибок

  • логи

  • использование ресурсов — процессор, сетевой трафик, память, диск

  • метрики QoE (Quality of Experience) устройства

  • метрики состояния потоковой передачи

Мы начали с малого, с крошечных пользовательских выборок для околочасовых экспериментов. Убедившись в эффективности, мы постепенно наращивали масштабы. Мы увеличивали процентную долю выделенных клиентов, вводили многорегиональные тесты и в конечном итоге проводили 12-часовые или эксперименты продолжительностью в день. Валидация в процессе работы очень важна, поскольку Sticky Canaries влияют на живой продакшен трафик и привязываются к определенным клиентам на постоянной основе.

После нескольких экспериментов со Sticky Canaries мы убедились, что второй этап миграции улучшила все основные показатели, и мы могли с уверенностью подключить GraphQL глобально.

Преимущества

Sticky Canaries были необходимы для укрепления уверенности в новых сервисах GraphQL.

  • Неидемпотентные API: эти тесты совместимы с мутирующими или неидемпотентными API.

  • Бизнес-метрики: Sticky Canaries подтвердила, что основные бизнес-показатели Netflix после миграции улучшились.

  • Производительность системы: данные о задержках и использовании ресурсов помогают нам понять, как изменяются профили масштабирования после миграции.

Проблемы

  • Негативное влияние на клиентов: Sticky Canaries могут повлиять на реальных пользователей. Нам нужна была уверенность в наших новых сервисах, прежде чем настойчиво направлять на них некоторых клиентов. Это частично смягчается благодаря обнаружению ошибок в реальном времени, которое автоматически отменяет эксперименты.

  • Кратковременность: Sticky Canaries предназначены для кратковременных экспериментов. Для более длительных тестов следует использовать полноценный A/B-тест.

В заключение

Технологии постоянно меняются, и мы, инженеры, проводим большую часть своей карьеры, выполняя миграции. Вопрос заключается не в том, мигрируем ли мы, а в том, безопасно ли мы мигрируем — с нулевым временем простоя и своевременно.

В Netflix мы разработали инструменты, которые обеспечивают уверенность в этих миграциях, ориентированные на каждый конкретный тестируемый случай использования. Мы рассмотрели три инструмента — A/B-тестирование, Replay Testing и Sticky Canaries, которые мы использовали для миграции GraphQL.

Нашла и перевела: Ксения Мосеенкова

Всех начинающих тестировщиков приглашаем на открытые уроки в январе: узнаем, как выглядит обычный рабочий день ручного тестировщика и познакомимся с тестовой документацией. Уроки пройдут в рамках курса QA Engineer Basic.

Комментарии (2)


  1. WondeRu
    11.01.2024 21:42
    +1

    Мы наши последние проекты делаем на graphql - отлично себя показывает технология! Еще год назад были сомнения, связанные с кэшем, но все порешали и разобрались. Теперь rest вызывает боль.


    1. NerVik
      11.01.2024 21:42

      А как вы тестируете его? С rest все понятно, есть ручка есть поля которые нужно проверить, а с гкл можно кучу комбинаций запроса проверить и все равно не протестировать все.