---
title: Статья 11 — Эталонные архитектуры сервисов
source: подготовлено 2026-09-17, TASK-45.51; каркас — brief.md §11; основа — classic-designs.md §1–4 (полные эталоны); сверка — Alex Xu гл. 8, 11–12, 14 (TASK-45.38), DDIA 2-е изд. гл. 2 (TASK-45.39), статья Яндекса (TASK-45.5)
---

# Статья 11: эталонные архитектуры сервисов

Одиннадцатая статья серии — сборочная. Разделы 01–10 дали словарь и строительные блоки: фреймворк ведения секции, арифметику, лестницу масштабирования, хранилища, репликацию и шарды, кэш, очереди, отказоустойчивость, согласованность, прикладные компоненты. Критерий «умею, если» из брифа собирает всё это в одно требование: **«собираю полный разбор ленты за 15 минут по фреймворку из 6 этапов»**. Не «знаю паттерны», а «могу провести весь ответ — от требований до эксплуатации — как связный рассказ с числами и трейдоффами». Это ровно формат нашей секции: «крупноблочная схема сервиса уровня инстаграм/твиттер + отказоустойчивость», а инстаграм/твиттер — это лента новостей (§3 этой статьи) плюс медиапайплайн (§5).

Отношение статьи к [`../classic-designs.md`](../classic-designs.md) — главный вопрос, на который она отвечает. Эталонник — это **полные разборы**: таблица диалога с интервьюером, SQL-схемы, псевдокод горячего пути, таблицы отказов, сверки с Сюем и DDIA. Здесь — **ход решения**: у каждого сервиса одна большая идея, из которой вытекает всё остальное, скелет из 6 этапов, узкое место и чем отличают сильный ответ. Порядок работы с двумя документами: эта статья читается **до** эталонов (задаёт карту), эталонник — **после** тренировочного раунда (сверка; перед раундом не перечитывать — раунд засчитывается только «из головы», [`../methodology.md`](../methodology.md) §6.1). Формулы и числа-константы — [`../knowledge-base.md`](../knowledge-base.md) (далее KB); каркас 6 этапов — статья 01.

## 1. Как читать четыре разбора: каркас и ритм

Все четыре эталона рассказываются **одним и тем же ритмом** — 6 этапов из статьи 01. Сквозной ответ — это не «сначала requirements, потом design», а **кольцо с двумя возвратами**: числа этапа 1 становятся ресурсами этапа 5, а лист требований этапа 1 закрывается в финале этапа 6. Универсальный каркас, общий для всех сервисов:

| # | Этап | Что должно появиться | Откуда берём |
|---|------|----------------------|--------------|
| 1 | Требования | Функциональные/нефункциональные, числа-гипотезы вслух (DAU, RPS чтение/запись, SLO, консистентность), минус-объём; лист на доске не стирается | статья 01 §4, статья 02 §4 |
| 2 | Потоки и API | Сущности, 3–5 сигнатур, **control plane vs data plane** | статья 01 §3 |
| 3 | Крупноблочная схема | 5–8 блоков, путь горячего запроса проговариваем по стрелке | статья 03 §6 |
| 4 | Детали + БД | Устройство 1–2 главных блоков, хранилища, ключевой алгоритм, главный трейдофф | статьи 04–07, 09 |
| 5 | Ресурсы и узкие места | Back-of-envelope: ноды, storage, bandwidth; **узкое место дизайна** — где система реально напрягается | статья 02 |
| 6 | Эксплуатация | Прогонка отказов по узлам, деградации, метрики, релизы; **возврат к листу требований** | статья 08, статья 12 |

Формат каждого разбора ниже: **одна большая идея** (решающий ход, из которого вытекает остальное) → скелет 6 этапов таблицей → ключевые ходы и трейдоффы → узкое место и финал → куда углубляться. Большая идея — это то, что должно всплыть **на этапе 1–2, сразу после чисел**: она определяет схему, а не украшает её. Интервьюер ждёт её услышать как «точку, вокруг которой собран ответ» — и каждый эталон ниже выбран так, чтобы эта точка была своя.

## 2. URL shortener — эталон Яндекса

**Одна большая идея: чтение доминирует с отношением 50 000:1 — поэтому система делится на два почти несвязанных контура.** Data plane (500 тыс. RPS `GET /{id}` → 302) — это чистое чтение из реплицированного KV-зеркала; control plane (создание ссылок, аккаунты, статистика — единицы RPS записи) — обычное приложение над реляционной СУБД. Всё элегантное в этом эталоне — следствия этого деления.

```
control:  клиенты → REST API → RDBMS (источник правды) ──репликация──▶ KV-зеркало
data:     клиенты → IPVS → воркер (веб-сервер + L1 LRU + ко-локальный KV) → 302
фон:      очередь → LAT actuator / RPS limiter / TTL-GC (асинхронно)
```

| Этап | Ход решения |
|------|-------------|
| 1. Требования | Вытягиваем диалогом: 500k RPS чтения в пике, 10–20 RPS записи, 1 млрд ссылок, 150 мс p95, четыре девятки **на чтение**, новые ссылки — строго, модификации — лаг в секунды, free-лимит 1000 req/min. Вывод: «дизайн целиком про чтение; строгая согласованность — только для новых ссылок» |
| 2. Потоки и API | Data plane — один `GET /{id}` → **302**, никаких синхронных вызовов в горячем пути; control plane — REST (аккаунты, статистика, правка). ID: **random base36, 6+1=7 символов** (36⁶≈2.2 млрд; +1 разряд против перебора), не инкремент |
| 3. Схема | Два контура физически раздельны: IPVS → воркеры → KV; REST → RDBMS; синхронизация СУБД → KV асинхронная |
| 4. Детали | RDBMS — источник правды (сотни ГБ, **без шардирования** — репликация); KV-зеркало ~100 ГБ — **реплицировать, не шардировать** (100k RPS на сервер, CPU не тратит) → **ко-локация** инстанса KV на каждом воркере; лимиты — **снимок таблицы RPS в разделяемой памяти воркера** (обновление раз в ~100 мс), счётчики — в памяти воркера, батчами; вместо 2PC — **коммит после большинства реплик KV + фоновая GC**; read-your-writes новых ссылок — L1 LRU на воркере |
| 5. Ресурсы | Дефицит — **TLS-handshake**: ~20k RPS на сервер → 500k/20k = 25 воркеров; KV тянет 100k, но кушает память — ко-локация решает оба; бюджет латентности: ~130 мс из 150 (120 мс сеть клиент↔сервер + ≤10 мс воркер) |
| 6. Эксплуатация | Топология **от отказа ДЦ, не от пика**: 9 воркеров × 4 ДЦ + 4 IPVS + 3 СУБД = **43 сервера**; таблица отказов (воркер → размазалось по ДЦ; primary СУБД → страдает только control, GET жив; IPVS → запас); перегрузка чтения: очередь на воркерах, антифрод/blacklist, понижение криптостойкости SSL; финал — каждое требование закрыто и меряется |

**Ключевые ходы, которые отличают сильный ответ:** 302 против 301 — у нас платные правки ссылок (у Сюя вторая грань: 301 кэшируется браузером → клики не считываются — проговорить обе); случайные ID против инкремента — перебор чужих ссылок; **«не нужно для ответа пользователю → в память воркера, батчами во внешний мир»** (accessed, счётчики, лимиты) — формула, которая переносится в любой сервис; лимитер — **leaky bucket со burst budget** и состоянием в основной СУБД (не строим ещё один распределённый сервис ради умеренного потока обновлений). **Узкое место** — не БД и не KV, а TLS на воркерах: система CPU-bound в data plane и I/O-light везде. **Финал:** «150 мс p95 — бюджет 130 мс; четыре девятки — N+1 и 4 ДЦ; строгие новые ссылки — L1 + majority; каждое требование — с метрикой».

**Глубина:** [`../classic-designs.md`](../classic-designs.md) §1 (полный конспект статьи Яндекса: диалог, SQL-схемы, алгоритм GET по шагам, таблица отказов, «чему учит эталон»); Сюй гл. 8 — два способа генерации кода (hash + фильтр Блума против base62(ID)) и оценка 1160 QPS записи; конспект — [`../materials/alex-xu-vol1/ch-08-url-shortener.md`](../materials/alex-xu-vol1/ch-08-url-shortener.md).

## 3. Лента новостей (Instagram/Twitter) — главный билет нашей секции

**Одна большая идея: узкое место не «написать пост», а «доставить его 300 подписчикам за 5 секунд» — и весь дизайн держится на выборе fan-out on write против fan-out on read.** Постинг 2.3k QPS при 300 подписчиках = **700 тыс. вставок/с** в ленты — на два порядка больше, чем сам постинг. Числа этапа 1 (DAU 100 млн, 10 открытий ленты → ~35k QPS чтения в пике) сами выпрямляют ответ: чтение ×15 к записи, лента обязана быть готовой в кэше.

```
чтение:  клиент → LB → Feed API → кэш лент (Redis, post_id-списки) ─miss→ merge из таймлайнов
постинг: клиент → Post API → БД постов (источник правды) → событие в Kafka → fan-out воркеры → кэш лент
медиа:   → S3 → транскодинг → CDN (отдельный контур, §5)
```

| Этап | Ход решения |
|------|-------------|
| 1. Требования | Гипотезы вслух (KB §2.3): DAU 100M, 10 открытий/сутки → 35k QPS чтения в пике; 2 поста/сутки → 2.3k QPS записи; 300 подписчиков → fan-out 700k вставок/с; p95 ≤ 200 мс; 99.9 %; eventual ≤ 5 с, **свой пост автору — сразу**; медиа обязательны (но отдельным контуром) |
| 2. Потоки и API | `POST /posts` (idempotency-key), `GET /feed?cursor=` (курсорная пагинация), follow/like — eventual. ID постов — **snowflake** (время внутри ID → сортировка ленты = сортировка ID). Постинг/чтение — data plane; профили, подписки, модерация — control |
| 3. Схема | Чтение: LB → Feed API → кэш лент → ответ (единицы мс). Постинг: Post API → БД → **очередь** → fan-out воркеры → кэш. Два пути разной частоты: длинный путь постинга в 15 раз реже — его сложность никому не мешает |
| 4. Детали | **Центральный трейдофф таблицей**: push (быстрое чтение, дорогая запись, катастрофа на селебрити, пушим мёртвым) против pull (дешёвая запись, merge на каждый запрос, катастрофа для читателя с 1000+ подписок) → **микс**: push обычным (99.9 %), **pull для селебрити** (>1M); чтение = кэшированный push-список + merge свежих постов селебрити. Кэш лент: Redis по user_id (consistent hashing), **только post_id, глубина ~800**, 80/20 → единицы ТБ; stampede — per-user блокировка + rebuild merge'ем; read-your-writes — свой пост синхронно в свою ленту. Посты — шард по post_id, соцграф — отдельно по user_id; счётчики — INCR в Redis + батч-флаш |
| 5. Ресурсы | Чтение 35k QPS → единицы-десятки нод Feed API; **fan-out 700k вставок/с — это и есть дизайн**: очередь + шардированные воркеры, метрика — lag; storage постов ~70 ТБ/год → шарды; медиа — сотни ТБ/год в S3-класс + CDN. Формулировка: «две системы разной природы — лента (миллионы мелких операций) и медиа (гигантские блобы); смешивать нельзя» |
| 6. Эксплуатация | Приоритеты деградации: **чтение > постинг > счётчики**; перегрузка ×10 → shedding на постинге + **адаптивный fan-out** (пушим только активным за 30 дней, остальные pull'ом); отказ Redis-ноды → rebuild из таймлайнов (медленнее, но работает); отказ fan-out-воркеров → «лента стареет» (graceful degradation), алерт на lag; canary на fan-out-воркерах — регрессия доставки видна в lag до полного выката; финал — возврат к листу |

**Ключевые ходы:** celebrity-проблема **двусторонняя** — (а) пост миллионщика не пушим (merge на чтении, его посты — идеальный кэш-кандидат), (б) горячий ключ кэша реплицируем; фильтры (mute, приватность) применяются **до fan-out**, иначе «размутинг» невозможен; медиапайплайн не блокирует постинг — плейсхолдер сейчас, рендиции доедут фоном (трейдофф «свежесть > комплектность»). **Узкое место** — доставка, не запись: формулировка для этапа 5 из DDIA гл. 2: «1M лёгких записей в Redis против 2M тяжёлых чтений — обмен выгодный». **Финал:** «лента отдаётся из кэша за единицы мс; пост доезжает ≤ 5 с по очереди с метрикой lag; свой пост — сразу; при перегрузке жертвуем счётчиками и свежестью, не чтением».

**Глубина:** [`../classic-designs.md`](../classic-designs.md) §2 (полный разбор: таблица fan-out, псевдокод publish/fanout, память кэша, таблица отказов, сверки с Сюем гл. 11 и DDIA гл. 2); критерий раздела брифа — именно этот разбор собирается за 15 минут.

## 4. Мессенджер

**Одна большая идея: у сообщения два независимых обязательства — пережить (источник правды) и долететь (доставка) — и они строго в этом порядке: сначала хранилище, потом шина.** Пока сообщение в БД диалога, потеря шины, воркера или коннекта — это задержка, а не потеря. Вторая половина идеи: онлайн-доставка живёт на **постоянных соединениях** — gateway-кластер, который ничего не считает, только держит коннекты и знает, где кто сидит.

```
клиент ⇄ gateway (WS, ~500k коннектов/нода) ⇄ Kafka (key=dialog_id) ⇄ delivery-воркеры
             │ registry: user_id → gateway (Redis, TTL+heartbeat)      │
             └ offline: APNs/FCM                          messages store (источник правды, seq per dialog)
```

| Этап | Ход решения |
|------|-------------|
| 1. Требования | DAU 50 млн, ~10M одновременно онлайн; 20 сообщений/сутки → 11.5k msg/s avg, **~35k msg/s пик** (доставок в 2–3 раза больше — группы); p95 доставки ≤ 500 мс; **at-least-once + дедуп на клиенте**; **строгий порядок внутри диалога**, eventual между устройствами; история навсегда; группы до 1000 (каналы-миллионники — минус-объём) |
| 2. Потоки и API | Протокол **WebSocket + protobuf-фреймы**: `send(msg, idem_key)`, серверные `deliver/ack`, `sync(cursor)` для офлайн-догонялки; история — REST `GET /dialogs/{id}/messages?before_seq=`. Коннекты и доставка — data plane; профили, настройки групп — control |
| 3. Схема | Gateway → Kafka → delivery-воркеры → registry lookup → push в gateway получателя **или** push-нотификация офлайн. Проговариваем вслух: «сначала пишу в источник правды — потеря шины не теряет сообщение» |
| 4. Детали | Gateway — stateful, но без бизнес-логики: терминирует TLS/WS, heartbeat; **registry** `user_id → gateway_id` с TTL (мёртвые коннекты зачищаются сами). Порядок: **монотонный seq на диалог назначает хранилище** при вставке; ключ партиции Kafka = dialog_id → порядок на всём пути; «строгость нужна только внутри диалога» (глобальный порядок не строим — трейдофф). Гарантии: at-least-once → **дедуп по (dialog_id, seq)** на клиенте; офлайн — история догонит по `sync(cursor)`. Presence — события в шину → Redis TTL, с троттлингом; группы — fan-out по участникам из соцграфа (тысячи, не миллионы); хранилище write-heavy → **LSM**, партиции по dialog_id |
| 5. Ресурсы | Gateway: 10M коннектов / 500k = 20 нод → с запасом по сценариям ~9×4 ДЦ; шина — тысячи партиций, воркеры горизонтально; storage ~400 ТБ/год текста; латентность считаем **хопами**: gateway→шина→воркер→gateway ≈ 4 × 0.5 мс внутри ДЦ — в бюджете 500 мс с запасом |
| 6. Эксплуатация | Отказ gateway-ноды → 500k reconnect'ов **с jitter** (иначе шторм), недоставленное ждёт в шине/истории — доставка после переподключения; отказ партиции → реплики, лидер-фейловер, метрика lag per-партиция; перегрузка ×10 → приоритет **сообщения > статусы > typing**, shedding на presence, rate limit на отправку; мониторинг: **end-to-end delivery p95** (от send до deliver), lag, reconnect rate; релизы gateway — drain + canary на одну ноду |

**Ключевые ходы:** «писать в хранилище до шины (надёжность) против после (латентность) — выбираю надёжность, сообщение важнее миллисекунды»; seq в хранилище против внешнего координатора (меньше распределённых сущностей); канал-миллионник — pull по указателю (как селебрити в ленте — общий паттерн «миллионы получателей = pull»); reconnect с jitter — тот же урок, что ретраи в статье 08 §5. **Узкое место** — не throughput (35k msg/s для шины копейки), а **lag и штормы переподключений**: система ломается событиями, не потоком. **Финал:** «сообщение не теряется: источник правды + at-least-once + дедуп; порядок внутри диалога — seq и партиция по dialog_id; офлайн догоняет по курсору; presence деградирует первым».

**Глубина:** [`../classic-designs.md`](../classic-designs.md) §3 (псевдокод send/delivery по шагам, presence, группы, таблица отказов); Сюй гл. 12 — mailbox-модель WeChat, синхронизация устройств через `cur_max_message_id`, presence-heartbeat; конспект — [`../materials/alex-xu-vol1/ch-12-chat-system.md`](../materials/alex-xu-vol1/ch-12-chat-system.md).

## 5. Медиа-хостинг (видео)

**Одна большая идея: это не сервис «запрос–ответ», а асинхронный конвейер — байты не должны проходить через наш API вообще.** Загрузка идёт **presigned multipart прямо в объектное хранилище** (чанки 5–20 МБ с retry по чанку), а всё дорогое (антивирус, транскодинг, миниатюры) — фоновые воркеры за очередью. Пользователь видит статус `uploaded → processing → ready` — и это честная eventual-консистентность: минуты обработки — приемлемо, проговариваем.

```
upload:   клиент → presigned multipart → S3 (исходник) → событие → очередь
transcode: транскодинг-фарм (GOP-сегменты параллельно, ladder рендиций) → S3 (HLS/DASH) → CDN
metadata: RDBMS (статусы, счётчики eventual) ◀— события пайплайна
watch:    клиент → CDN → origin shield → S3; счётчики: heartbeats → очередь → батч-писатель
```

| Этап | Ход решения |
|------|-------------|
| 1. Требования | 10k загрузок/сутки × ~1 ГБ → **30–50 ТБ/сутки** с рендициями → десятки ПБ/год (только объектное хранилище); 100k просмотров × 5 мин × 3 Mbps → egress ~10 ТБ/сутки, пик ×3–5 → **CDN обязателен**; старт воспроизведения p95 ≤ 2 с, rebuffer < 1 %; метаданные — строго, **готовность рендиций — eventual, минуты** |
| 2. Потоки и API | `POST /videos` → запись + **presigned multipart URL**; `POST /videos/{id}/complete` (идемпотентен по upload_id); `GET /videos/{id}` → статус + манифест; отдача — CDN. Загрузка метаданных и загрузка файла — **два независимых вызова** (развязка путей) |
| 3. Схема | Upload → S3 → очередь → ферма транскодинга → S3 (рендиции) → CDN; RDBMS держит статусы; счётчики — отдельный events-путь с батч-писателем |
| 4. Детали | Транскодинг: исходник режется на **GOP-сегменты → параллельная обработка** (часовое видео на десятке нод — минуты); на выходе **ladder битрейтов** (1080/720/480/360) в **HLS/DASH**: сегменты 4–10 с + манифест; те же сегменты дают **ABR — интеллект на клиенте, сервер только предлагает лестницу**; оригинал сразу в cold-класс; идемпотентность воркеров + DLQ; CDN: **origin shield** (один промах в origin на сегмент), signed URLs против хотлинка, иммутабельные сегменты → большой max-age; счётчики просмотров — heartbeats → очередь → батч (просмотр никогда не трогает RDBMS синхронно) |
| 5. Ресурсы | Storage — от retention (считать вслух: «десятки ПБ/год — разговор про холодные ярусы»); egress: offload 90 % → origin видит ~1 ТБ вместо 10; ферма: минуты CPU на минуту видео × 10k/сутки → десятки нод, **SLA «ready ≤ 30 мин» определяет размер фермы** — очередь сглаживает суточный пик (задержка вместо роста фермы — трейдофф проговорить) |
| 6. Эксплуатация | Отказ транскодера → retry на другом + DLQ, деградация = «обрабатывается дольше», **потери нет** — асинхронность сама по себе механизм отказоустойчивости; перегрузка просмотров ×10 → CDN масштабируется сам, лимитируем origin; перегрузка загрузок → rate limit + приоритеты, очередь — буфер; метрики: **старт-тайм p95 и rebuffer ratio** (QoE-метрики пользователя), offload %, lag очереди, доля `ready < 30 мин`; релизы транскодеров — canary с автосравнением качества (VMAF) |

**Ключевые ходы:** «API не проксирует гигабайты» — presigned upload; пайплайн — **не одна очередь, а DAG специализированных воркеров** (Сюй гл. 14, модель Facebook: нарезка → параллельные ветки видео/звук/миниатюра → сборка); пост «в обработке» сразу против ожидания готовности — свежесть важнее комплектности; стоимость CDN ($0.02/ГБ × 150 ТБ/день ≈ $150k/день) — **главный OPEX**, популярное в CDN, long tail из origin. **Узкое место** — не throughput, а **консистентность статусов**: пользователь смотрит на «обрабатывается», API отдаёт статус из метаданных, CDN — только готовые сегменты. **Финал:** «отдача — CDN с offload 90 %+; обработка — фоновая, с SLA и метрикой готовности; отказ любого воркера — задержка, не потеря; счётчики не в горячем пути».

**Глубина:** [`../classic-designs.md`](../classic-designs.md) §4 (полный пайплайн, ABR, таблица отказов, трейдоффы сегмента 4 с против 10 с); Сюй гл. 14 — DAG-модель, развязка путей, арифметика OPEX; конспект — [`../materials/alex-xu-vol1/ch-14-youtube.md`](../materials/alex-xu-vol1/ch-14-youtube.md).

## 6. Сквозные паттерны: что переносится на любой билет

Четыре эталона пересекаются не случайно — они собраны из одного набора решений. Именно эти девять паттернов и есть «системный дизайн» на секции: незнакомый билет раскладывается на них за пару минут. Сводка — где какой встретили и как он звучит вслух:

| Паттерн | Где встретили | Как звучит |
|---------|---------------|------------|
| **Источник правды один + производные проекции** | shortener: RDBMS → KV-зеркало; лента: посты → кэш лент; чат: store → шина; медиа: RDBMS-статусы + S3 | «Правда — в одном месте; горячее чтение — из read-only копии с известным лагом» |
| **«Не нужно для ответа — из request-path»** | accessed, счётчики, лимиты (shortener); лайки (лента); доставка событий (чат); view-события (медиа) | «Всё, что пользователь не ждёт в ответе, — асинхронно, батчами» |
| **Очередь — изолятор и буфер** | LAT-actuator (shortener); fan-out (лента); delivery (чат); транскодинг (медиа) | «Очередь развязывает этапы, сглаживает пик, метрика — lag; узкое место видно до отказа» |
| **Счёт от дефицитного ресурса** | TLS → 20k RPS/воркер (shortener); вставки/с fan-out (лента); коннекты/нода (чат); CPU-часы фермы (медиа) | «Считаю не серверы, а дефицитный ресурс — от него ноды» |
| **Топология от отказов, не от пика** | 9×4 ДЦ = 43 сервера (shortener); ~9×4 gateway (чат); canary на самом опасном узле (лента: fan-out) | «Запас закладываю сценариями отказов, а не пиковой нагрузкой» |
| **Control plane ≠ data plane** | короткие ссылки (shortener); профили против лент; коннекты против настроек; загрузка против просмотра | «Управление — единицы RPS, горячий путь — сотни тысяч; не смешиваю их» |
| **Ко-локация по доминантному ресурсу** | KV (память) на воркере (CPU) — localhost вместо сетевого хопа | «Данные не шардировать, а реплицировать, и положить рядом с CPU» |
| **Eventual по умолчанию, strict — по требованию** | новые ссылки строго / модификации лаг (shortener); свой пост сразу / лента ≤ 5 с; порядок в диалоге строго / между устройствами eventual; рендиции — минуты | «Строгость покупается там, где она в требованиях; остальное — eventual с числом» |
| **Деградации спроектированы заранее** | чтение > постинг > счётчики (лента); сообщения > статусы > typing (чат); «обрабатывается дольше» (медиа); «лента стареет» | «Чем жертвуем под перегрузкой — решено до инцидента, это фиче-флаги» |

Две проверочные связки на память: (1) «миллионы получателей → pull, тысячи → push» — селебрити в ленте, каналы-миллионники в чате, long tail видео из origin — один и тот же трейдофф в трёх декорациях; (2) «байты мимо API» — presigned upload в медиа, CDN для статики в ленте, KV на localhost в shortener: гигабайты не должны протекать через вычислительный слой.

## 7. Незнакомый билет: карта «задание → эталон»

На реальной секции билет не будет совпадать с эталоном дословно — он будет гибридом. Работает это так: определяем, какая **большая идея** главная (обычно она одна), берём её скелет, остальные блоки — из других эталонов и раздела 10. Разбор вслух: «ядро — как в ленте; доставку уведомлений возьму из мессенджера; медиа — из видеохостинга» — это звучит как система знаний, а не заученные ответы:

| Задание | Ядро | Дозагрузить из эталонов |
|---------|------|-------------------------|
| Instagram | лента (§3) | медиапайплайн §5; presence не нужен |
| Twitter/X | лента (§3) | акцент на селебрити и запись (лимиты); поиск/тренды — минус-объём или компоненты раздела 10 |
| Мессенджер (WhatsApp/Telegram) | мессенджер (§4) | медиа-обмен — ссылками на S3/CDN (как в §4) |
| YouTube/TikTok | медиа-хостинг (§5) | лента/подписки §3 (pull-часть); рекомендательный скоринг — минус-объём |
| Файлохранилка (Dropbox) | медиа-хостинг без транскодинга (§5) | чанки + дедуп + метаданные; синхронизация — события + очередь (§6) |
| Push-уведомления | delivery-контур мессенджера (§4) | очередь + rate limit + retry-политика (статья 07, статья 08) |
| Аукцион/билеты | строгие записи: linearizability (раздел 09) | burst-очередь, резерв с компенсацией; тренировка — ката Concert Comparison (методичка §6.5) |
| Google Docs/коллаборация | мессенджер (§4) | операции/OT против CRDT — назвать трейдофф, не углубляться без запроса |

Правило границы: если блок из правой колонки тянет на отдельный трейдофф — называем его одной фразой и **помечаем как минус-объём** («звонки отмечаю, не детализирую»). Это тот же приём, что на этапе 1: лучше честно сузить, чем утонуть в чужой детали.

## 8. Как репетировать: эталоны → раунды → мок

Раздел закрывается не чтением, а прогонами. Протокол — [`../methodology.md`](../methodology.md) §6 (формат раунда, рубрика 0–3 за этап, порог готовности 13/18); здесь — его свёртка под четыре эталона:

1. **Порядок: shortener → лента → мессенджер → медиа.** Shortener — самый механический (тренируем ритм 6 этапов на знакомой идее); лента — главный билет (наш формат секции — «инстаграм/твиттер»), на неё нужно максимум раундов; чат и медиа — по одному-два.
2. **Раунд = 60 минут по протоколу §6.1**: доска, голосовая запись, билет «из головы» — эталон до раунда не перечитывать. Спринт 40 минут — те же этапы, детали свёрнуты — если времени мало.
3. **Self-review сразу** по рубрике §6.2, затем сверка с [`../classic-designs.md`](../classic-designs.md) и три улучшения в `training/`.
4. **Контрольное упражнение раздела** — из критерия брифа: полный разбор ленты за **15 минут** (это спринт-формат ×2 скорости: по 2–3 минуты на этап, числа — только головные: 35k QPS чтения, 700k вставок/с fan-out, микс push/pull, lag как метрика). Если 15-минутный прогон идёт без пауз и заканчивается возвратом к требованиям — раздел готов.
5. **Незнакомый бриф** — после эталонов: одна-две каты из методички §6.5 (Concert Comparison — burst и строгие записи; Cheezborger — read-heavy UGC) накануне слота.

## 9. Ошибки этого раздела

Подмножество топ-10 ([`../methodology.md`](../methodology.md) §4) + провалы именно сквозных разборов:

| Ошибка | Как выглядит | Противоядие |
|--------|--------------|-------------|
| Блоки без сборки | Знает fan-out, кэш, очередь — но не может провести ленту от требований до эксплуатации за 15 минут | Тренировать **сквозные раунды**, не темы; критерий раздела — именно сборка |
| Большая идея названа поздно | Требования собрали, схему нарисовали — и только на деталях всплыло «чтение доминирует» | Идея определяется числами этапа 1: посчитал R/W ratio, fan-out, размер — идею называешь сразу |
| Лента и медиа в одном пути | «Посты с видео — в шардированную БД» — гигабайты в транзакционном хранилище | «Две системы разной природы»: лента — мелкие операции, медиа — блобы + CDN (§3 этап 5) |
| Fan-out синхронно | Пост вставляется в 300 лент внутри запроса — 700k вставок/с в request-path | Очередь + воркеры; узкое место — доставка, метрика lag (§3 этап 4–5) |
| Селебрити забыли (или наполовину) | Решили «не пушим миллионщикам», но не решили горячий ключ его кэша | Двусторонняя celebrity-проблема: merge на чтении + реплики горячего ключа (§3) |
| Чат: шина до хранилища | Сообщение ушло в Kafka, потом пишем в БД — сбой между = потеря | «Сначала источник правды, потом шина»; сбой = задержка, не потеря (§4) |
| Транскодинг в запросе / байты через API | Загрузка видео через POST-обработчик, рендиции — на лету | Presigned upload, конвейер за очередью, статусы eventual (§5) |
| Эталон заучен, гибкости нет | Отвечает коротенером на вопрос про чат; вне сценария — ступор | Эталоны = скелет + паттерны §6–7, не сценарий; на секции собираем под требования |
| Числа посчитаны и брошены | Back-of-envelope на этапе 5 не связан с решением | Каждое число → вывод → решение: «TLS дефицит → ко-локация», «700k вставок → очередь» |
| Эксплуатация по остаточному | Отказ по каждой ноде «обсудим потом» | Таблица отказов — обязательный финал каждого разбора; «эксплуатацию не режем никогда» |

## 10. Что назвать на секции (чек-лист раздела)

По одному «якорю» на эталон — фраза, с которой начинается сильный ответ, плюс обязательные числа. Полный чек-лист серии — методичка §7; канон чисел — KB §2.

**Shortener:** «Чтение доминирует 50 000:1 → делю на data plane (GET → 302 из KV-зеркала, ко-локация на воркере) и control plane (REST + RDBMS)». Числа: 500k RPS, 20k/воркер (TLS!), 25 воркеров, 43 сервера от отказа ДЦ. Паттерны: majority-коммит вместо 2PC; снимки в разделяемой памяти; «не нужно для ответа — батчами».

**Лента:** «Узкое место — доставка: 700k вставок/с fan-out → очередь; микс push/pull по порогу селебрити; чтение — кэш post_id-списков». Числа: 35k QPS чтения, 2.3k записи, 300 подписчиков, глубина ~800, eventual ≤ 5 с. Паттерны: snowflake, read-your-writes свой пост, адаптивный fan-out, приоритет «чтение > постинг > счётчики».

**Мессенджер:** «Сначала источник правды, потом шина: потеря доставки — задержка, не потеря; онлайн — gateway-кластер с registry; порядок — seq per dialog, партиция по dialog_id». Числа: 10M коннектов, 500k/нода → 20 нод, 35k msg/s пик, ≤ 500 мс end-to-end. Паттерны: at-least-once + дедуп, sync(cursor), reconnect с jitter, shedding на presence.

**Медиа:** «Это конвейер, не запрос: presigned upload в S3, транскодинг за очередью (GOP-параллельно, ladder рендиций, HLS/DASH), отдача — CDN с origin shield; готовность — eventual минуты со статусом». Числа: 30–50 ТБ/сутки storage, egress пик 3–5 Gbps, offload 90 %+, SLA ready ≤ 30 мин. Паттерны: DAG-воркеры, ABR на клиенте, счётчики heartbeat-батчами, деградация «обрабатывается дольше».

**На любой билет:** большая идея одной фразой до схемы; карта §7 — «ядро такое-то, блоки оттуда-то»; паттерны §6 — как минимум «источник правды + проекция», «не нужно для ответа — асинхронно», «деградации заранее»; финал — возврат к листу требований с метрикой напротив каждого пункта.

## 11. Самопроверка «умею, если»

Критерий раздела из брифа: **собираю полный разбор ленты за 15 минут по фреймворку из 6 этапов**. Проверка с закрытыми материалами: (1) лента за 15 минут по таймеру — все 6 этапов, головные числа с формулами, микс push/pull с селебрити, таблица отказов в финале — без возвратов к материалам; (2) каждый из четырёх сервисов — большая идея одной фразой и узкое место одним словом (shortener: чтение 50k:1 / TLS; лента: доставка / fan-out; чат: гарантии и порядок / lag и штормы; медиа: конвейер / статусы и egress); (3) незнакомый билет («спроектируйте X») раскладывается на эталоны по карте §7 за 2 минуты — с явным «ядро + дозагрузка»; (4) паттерны §6 воспроизводятся списком без подсказки — это и есть переносимый капитал раздела. Если 15-минутная лента идёт потоком и заканчивается закрытым листом требований — раздел готов: именно этот навык сдаётся на секции.

## Связанные документы

- [`00-overview.md`](00-overview.md) — обзорная статья по 12 разделам брифа (TASK-45.40); §11 — карта раздела
- [`01-interview-framework.md`](01-interview-framework.md) — каркас 6 этапов с таймингом, кольцо «числа → ресурсы → возврат к требованиям»
- [`02-estimations.md`](02-estimations.md) — §5–7: сквозные примеры расчётов shortener и ленты, bandwidth видео
- [`03-scaling.md`](03-scaling.md) — §6 типовая схема этапа 3, на которую ложатся все четыре эталона
- [`05-replication-sharding.md`](05-replication-sharding.md) — шардирование соцграфа/постов, hot partition селебрити
- [`06-caching.md`](06-caching.md) — кэш лент, stampede, CDN как уровень кэша
- [`../brief.md`](../brief.md) — бриф: раздел 11 с критерием «умею, если»
- [`../classic-designs.md`](../classic-designs.md) — **полные эталоны** (эта статья — ход решения, там — детали): §1 shortener (эталон Яндекса), §2 лента, §3 мессенджер, §4 медиа-хостинг; сверки с Alex Xu и DDIA
- [`../knowledge-base.md`](../knowledge-base.md) — §2 back-of-envelope с примерами всех четырёх сервисов, §11 шпаргалка «что назвать»
- [`../methodology.md`](../methodology.md) — §6 протокол тренировок (рубрика 0–3, порог 13/18, каты §6.5), §7 карточка на секцию
- [`../materials/yandex-564132.md`](../materials/yandex-564132.md) — источник эталона shortener (статья Яндекса)
- [`../materials/alex-xu-vol1/ch-08-url-shortener.md`](../materials/alex-xu-vol1/ch-08-url-shortener.md) · [`ch-11-news-feed.md`](../materials/alex-xu-vol1/ch-11-news-feed.md) · [`ch-12-chat-system.md`](../materials/alex-xu-vol1/ch-12-chat-system.md) · [`ch-14-youtube.md`](../materials/alex-xu-vol1/ch-14-youtube.md) — сверки эталонов с книгой
- Соседние статьи: `04-data-models-storage.md` (выбор хранилищ для каждого эталона) · `07-queues-streams.md` (очереди fan-out и delivery) · `08-fault-tolerance.md` (таблица отказов — финал каждого разбора) · `09-consistency.md` (eventual против strict в каждом эталоне) · `10-components-patterns.md` (snowflake, rate limiter, WebSocket) · `12-operations-observability.md` (QoE-метрики, canary)
