← все задачи

Задача 5 из 10

Лента новостей

Спроектируйте ленту постов от подписок: пользователь открывает приложение и видит свежие записи тех, на кого подписан.

Уровень: Средний На собеседовании: 45–60 минут fanout on write / on readкэш ленткурсорная пагинация

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

  • Опубликовать пост
  • Лента подписок в обратном хронологическом порядке
  • Подписаться и отписаться
  • Подгрузка следующей страницы ленты

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

  • 10 млн активных пользователей в сутки
  • Чтений на два порядка больше, чем записей
  • Лента открывается за 200 мс на p99
  • Средний пользователь подписан на 200 аккаунтов, у популярных — миллионы подписчиков
  • Задержка появления поста в ленте до нескольких секунд допустима

Нарисуйте архитектуру

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

Блок из палитры — добавить. Тащите мышкой или пальцем. Двойной клик по названию — переименовать. «Связь» — щёлкнуть по одному блоку, потом по другому: получится стрелка.

Схема сохраняется в этом браузере — вкладку можно закрыть и вернуться позже.

1 С чего начать, если ступор
  • Два подхода: разложить пост по лентам подписчиков при публикации (fanout on write) или собирать ленту при чтении (fanout on read).
  • Fanout on write ломается на аккаунтах с миллионами подписчиков: одна публикация — миллион записей.
  • Fanout on read ломается на пользователе с тысячей подписок: тысяча запросов на каждое открытие ленты.
  • Гибрид: обычных авторов раскладываем заранее, «звёзд» подмешиваем при чтении.
  • Прикиньте объём кэша: 10 млн лент × 200 записей — сколько это памяти.
2 Эталонная схема

Ленты обычных авторов раскладывает воркер, и они лежат готовыми в Redis — чтение становится одним запросом. Посты «звёзд» не разносим по миллионам лент: их подмешивает сервис ленты при чтении.

Это один из рабочих вариантов, а не единственно верный. Если у вас иначе, но вы можете объяснить почему — на собеседовании это ровно то, что нужно.

3 Разбор: что должно прозвучать

Стратегия fanout

  • Fanout on write: чтение дешёвое, запись дорогая, кэш лент занимает много памяти
  • Fanout on read: запись дешёвая, чтение дорогое и медленное
  • Гибрид с порогом по числу подписчиков — тот ответ, которого ждут; важно назвать сам порог как настраиваемый

Оценка объёма

  • 10 млн лент × 200 записей × ~50 байт ≈ 100 ГБ — это уже кластер Redis, а не один инстанс
  • Держать ленту целиком не нужно: хватает первых страниц, остальное собирается по требованию
  • Ленты неактивных пользователей можно не поддерживать вовсе и строить при заходе

Пагинация

  • Offset ломается: пока пользователь читает, лента сдвигается и записи дублируются или теряются
  • Курсор по (время, id) или по идентификатору последней увиденной записи
  • Курсор должен переживать вставку новых постов сверху

Консистентность и отставание

  • Лента eventually consistent — это записано в требованиях, и на это можно опираться
  • Свой пост пользователь должен видеть сразу: обычно его подмешивают локально
  • Что делать, если воркер fanout отстал на 10 минут: деградация в fanout on read

Горячие точки

  • Публикация звезды создаёт всплеск на миллион операций — его надо размазывать
  • Подписки на звёзд читаются постоянно: их посты кэшируются отдельно
  • Отписка не должна требовать перестроения всей ленты

Куда копать дальше, если спросят

  • Ранжирование вместо хронологии и как оно меняет всю схему
  • Удаление поста: чистить миллион лент или фильтровать при чтении
  • Восстановление ленты после потери кэша
  • Шардирование лент и постов, ключ шардирования

Следующая задача: Бронирование мест

Задача не про масштаб, а про конкурентность: 500 человек одновременно жмут на одно место, и продать его можно ровно один раз.