TL;DR
  • Было: CQRS с Kafka между write/read-БД, фасад Page Data и K/V-слой (KVDAL) с near-cache. Узкое место — цепочка I/O и обновление кэшей → лаги публикации и медленный предпросмотр (eventual consistency).

  • Стало: контур чтения перевели на RAW Hollow — объектное in-memory-состояние с компрессией; весь рабочий датасет хранится в памяти каждого сервиса (Page Construction, поиск, персонализация) → синхронный доступ ~O(1), меньше внешних зависимостей.

  • Зачем: нужен read-after-write для мгновенного предпросмотра; Hollow по умолчанию eventual, но даёт возможность включать сильную консистентность на уровне отдельного запроса.

  • Результат: время сборки главной страницы уменьшилось примерно с ~1.4 s до ~0.4 s; предпросмотр правок — секунды вместо минут. Хранение ~3 лет данных — ≈130 МБ (≈25% от объёма в Iceberg).

  • Цена вопроса: более тесная связка логики страниц с in-memory-состоянием Hollow; аккуратно с эволюцией схем и шарингом клиента между командами.

  • Выводы: CQRS хорошо масштабирует, но платишь конечной согласованностью; I/O — главный враг латентности; кэширование сложно, а «держать всё в памяти» снимает целый класс проблем.

Tudum.com — официальный фан-портал Netflix, который позволяет зрителям глубже погружаться в любимые шоу и фильмы Netflix. Tudum предлагает эксклюзивные обзоры, материалы со съёмок, интервью с актёрами, живые события, гиды и интерактивные форматы. «Tudum» назван в честь звукового логотипа, который вы слышите при нажатии Play на шоу или фильме Netflix. У Tudum уже более 20 миллионов пользователей ежемесячно, его задача  — обогащать опыт просмотра, предоставлять дополнительный контекст и инсайты по контенту, доступному на Netflix.

Изначальная подход к архитектуре

В конце 2021 года, когда мы продумывали реализацию Tudum, мы рассматривали архитектурные паттерны, которые были бы поддерживаемыми, расширяемыми и понятными инженерам. С целью построить гибкую, конфигурационно-управляемую систему мы обратились к подходу «интерфейс, управляемый сервером» (SDUI) как к привлекательному решению. SDUI — это подход к проектированию, при котором сервер определяет структуру и содержимое интерфейса, позволяя динамически вносить изменения и настраивать UI без модификации клиентского приложения. Клиентские приложения — веб, мобильные и ТВ-устройства — выступают движками отрисовки данных SDUI. После того как наши команды всё взвесили и тщательно проверили детали, мы в итоге остановились на подходе, близком к «разделению ответственности команд (command) и запросов» (CQRS). У Tudum есть два основных сценария, которые CQRS способен решить наилучшим образом:

  • Редакционная команда Tudum приносит эксклюзивные интервью, фотографии первых кадров, видео со съёмок и множество других форматов контента для поклонников и собирает всё это в страницы на сайте Tudum.com. Этот контент попадает в Tudum в виде отдельно публикуемых страниц и элементов контента внутри страниц. Для поддержки этого в архитектуре Tudum предусмотрен путь записи для хранения всех этих данных, включая внутренние комментарии, правки, историю версий, метаданные ассетов и настройки расписания публикаций.

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

Исходная архитектура данных Tudum
Исходная архитектура данных Tudum

Эта высокоуровневая схема фокусируется на хранении и распространении контента и показывает, как мы использовали Kafka для разделения баз данных записи и чтения. База данных записи хранила внутреннее содержимое страниц и метаданные из нашей CMS. База данных чтения хранила оптимизированные для чтения представления страниц, например: URL-адреса изображений в CDN вместо внутренних идентификаторов ресурсов, а также реальные названия фильмов, краткие описания и имена актёров вместо заглушек. Этот конвейер загрузки контента позволял по требованию регенерировать весь пользовательский контент, применяя новую структуру и данные, например изменения глобальной навигации или брендинга. Сервис загрузки данных Tudum преобразовывал внутренние данные CMS в формат, оптимизированный для чтения, применяя шаблоны страниц, выполняя проверки, проводя преобразования данных и публикуя отдельные элементы контента в топик Kafka. Потребитель сервиса данных (Data Service Consumer) получал элементы контента из Kafka, сохранял их в отказоустойчивой СУБД (Cassandra) и выступал API-слоем для сервиса конструирования страниц (Page Construction) и других внутренних сервисов Tudum для получения контента.

Ключевое преимущество разделения путей чтения и записи — возможность масштабировать их независимо. Широко применяемый подход — связывать контуры записи и чтения через событийно-ориентированную архитектуру. В результате правки контента в конечном итоге появлялись на tudum.com.

Проблемы конечной согласованности (eventual consistency)

Обратили внимание на акцент на фразу «в конечном итоге» (eventually)? Главный минус такой архитектуры — задержка между внесением правки и её появлением на сайте. Например, при публикации обновления происходило следующее:

  • Вызвать REST-эндпоинт сторонней CMS для сохранения данных.

  • Дождаться, пока CMS уведомит слой загрузки Tudum через вебхук.

  • Дождаться, пока слой загрузки Tudum запросит по API все необходимые секции, провалидирует данные и ассеты, обработает страницу и опубликует изменённый контент в Kafka.

  • Дождаться, пока Data Service Consumer считает это сообщение из Kafka и сохранит его в базе данных.

  • И наконец, после задержки на обновление кэша эти данные в конечном итоге станут доступны сервису конструирования страниц. Отлично!

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

Наше профилирование производительности показало, что источником задержки был сервис Page Data, выступавший фасадом для лежащей под ним базы данных абстракции «ключ–значение» (Key Value Data Abstraction, KVDAL). Сервис Page Data использовал near-кэш (уточнение: локальный кэш на стороне сервиса/клиента, синхронизируемый с распределённым кэшем/БД) для ускорения построения страниц и снижения задержек чтения из базы.

Этот кэш был реализован, чтобы оптимизировать N+1 обращений по ключам, необходимых для конструирования страниц, удерживая полный набор данных в памяти. Когда инженеры слышат «медленное чтение», первая реакция — «кэшировать», что мы и сделали. Near-кэш KVDAL может обновляться в фоновом режиме на каждом узле приложения. Независимо от того, какая система модифицирует данные, кэш обновляется на каждом цикле обновления. Если у вас 60 ключей и интервал обновления оставляет 60 секунд, near-кэш будет обновлять по одному ключу в секунду. Это создаёт проблемы для предпросмотра свежих правок: изменения отражаются только при очередном обновлении кэша. По мере роста объёма контента Tudum время обновления кэша росло, ещё больше увеличивая задержку.

RAW Hollow

По мере того как эта боль усиливалась, внутри компании разрабатывалась новая технология, которая стала нашей «серебряной пулей». RAW Hollow — инновационная объектная база данных в оперативной памяти, совмещённая с приложением и использующая сжатие, созданная в Netflix. Она рассчитана на работу с малыми и средними наборами данных и поддерживает сильную согласованность чтения после записи (read-after-write). Технология решает задачу стабильной производительности с низкой задержкой и высокой доступностью в приложениях, где данные меняются не слишком часто. В отличие от традиционных SQL-СУБД или полностью «in-memory» решений, RAW Hollow предлагает уникальный подход: весь набор данных распределяется по кластеру приложений и находится в памяти каждого процесса приложения.

Эта архитектура использует методы сжатия, чтобы масштабировать наборы данных до 100 миллионов записей на сущность, обеспечивая крайне низкие задержки и высокую доступность. По умолчанию RAW Hollow предоставляет конечную согласованность, но при необходимости можно включить сильную согласованность на уровне отдельного запроса — это позволяет балансировать между высокой доступностью и строгостью консистентности. Технология упрощает разработку высокодоступных и масштабируемых приложений с состоянием, устраняя сложности синхронизации кэша и внешних зависимостей. Благодаря этому RAW Hollow является надёжным решением для эффективного управления наборами данных в таких средах, как стриминговые сервисы Netflix, где критичны высокая производительность и отказоустойчивость.

Обновлённая архитектура

Tudum идеально подошёл для боевого тестирования RAW Hollow ещё до стадии GA (общей доступности) внутри компании. Высокоплотный near-кэш Hollow заметно сокращает операции ввода-вывода (I/O). Размещение основного набора данных в памяти позволяет различным микросервисам Tudum (конструктор страниц, поиск, персонализация) получать к нему синхронный доступ за константное время O(1), упрощая архитектуру, снижая сложность кода и повышая отказоустойчивость.

Обновлённая архитектура данных Tudum
Обновлённая архитектура данных Tudum

В нашей упрощённой архитектуре мы отказались от серивса Page Data, хранилища «ключ–значение» и инфраструктуры Kafka в пользу RAW Hollow. Встраивая клиент работы с памятью непосредственно в сервисы контура чтения, мы избегаем операций ввода-вывода на каждый запрос и сокращаем время полного цикла запроса.

Результаты миграции

Обновлённая архитектура дала колоссальное сокращение времени распространения данных, а уменьшение I/O дополнительно ускорило обработку запросов. Сжатие в Hollow сняло наши опасения, что «данные слишком велики» для размещения в памяти. Хранение трёх лет неразвёрнутых данных требует всего 130 МБ — это 25% от их несжатого объёма в таблице Iceberg.

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

А как насчёт ускорения ответов на запросы? Диаграмма ниже показывает времена «до и после» при обслуживании запроса на главную страницу Tudum. Все сервисы контура чтения Tudum используют состояние Hollow в памяти, что существенно ускоряет построение страницы и работу алгоритмов персонализации. С поправкой на такие факторы, как TLS, аутентификация, логирование запросов и фильтрация WAF, время построения главной уменьшилось примерно с ~1,4 с до ~0,4 с!

Время построения главной страницы
Время построения главной страницы

Внимательный читатель заметит, что теперь наш Page Construction Service тесно связан с In-Memory-состоянием Hollow. Такая тесная связка используется только в приложениях, специфичных для Tudum. Однако следует проявлять осторожность, если нужно делиться клиентом Hollow In-Memory с другими инженерными командами: это может ограничить вашу гибкость при изменении схемы или выводе частей из обращения.

Ключевые выводы

  • CQRS — мощная парадигма проектирования для масштабирования, если вы готовы мириться с конечной согласованностью.

  • Минимизация количества последовательных операций может значительно сократить время отклика. I/O часто является главным врагом производительности.

  • Кэширование — сложно. Инвалидировать кэш — тяжёлая задачка. Держа весь набор данных в памяти, можно устранить целый класс проблем.

В следующем выпуске мы расскажем, как Tudum.com использует интерфейс, управляемый сервером (Server-Driven UI), чтобы быстро создавать и развёртывать новые впечатления для поклонников Netflix. Следите за обновлениями!


Практическое продолжение — курс Software Architect: систематизируем Event-Driven, CQRS и DDD, разбираем модели консистентности и проектируем отказоустойчивые, масштабируемые системы. В программе — API-first, оркестрация/хореография, наблюдаемость; без кода, но с диаграммами и разбором трейд-оффов. Чтобы узнать, подойдет ли вам программа курса, пройдите вступительный тест.

Также приходите на открытые уроки, которые бесплатно проведут преподаватели в рамках набора:

  • 29 октября: «Способы разделения микросервисов на компоненты». Записаться

  • 11 ноября: «NoSQL в бою: как Cassandra помогает строить отказоустойчивый бэкенд». Записаться

  • 17 ноября: «Архитектурные решения в Backend-разработке». Записаться

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