title: Статья 10 — Прикладные компоненты и паттерны source: подготовлено 2026-09-17, TASK-45.50; каркас — brief.md §10; развёртка — knowledge-base.md §13–14; источники — Alex Xu Vol. 1 гл. 4, 7, 9, 13 (TASK-45.38), learn-system-design Ultimate Guide (TASK-45.24); отсылки — статьи 01, 04, 07, 08, 09, 11, 12 серии, classic-designs.md
Статья 10: прикладные компоненты и паттерны
Раздел-арсенал: готовые блоки, из которых собираются ответы на типовые вопросы этапов 2 и 4. Всё остальное в серии — про то, как вести секцию (01), считать (02), выбирать хранилища и механизмы (04–09); здесь — шесть именованных компонентов, которые интервьюер может спросить отдельно или которые нужны в билете: дизайн API, rate limiter, генераторы ID, real-time доставка, поисковый краулер, автозаполнение. Критерий «умею, если» из брифа жёсткий и формульный: для любого из компонентов могу в 2 фразах описать схему и трейдофф.
Формула раздела, которая экономит минуты секции: «две фразы: схема + трейдофф». Первая фраза — как устроен механизм (по стрелке данных); вторая — чем за это платим и что получаем взамен. Не лекция, не «могу рассказать подробнее», а именно две фразы — и готовность развернуть любую в детали по запросу. Проверка на себе перед секцией: если про компонент сначала вспомнилось название технологии, а не стрелка данных и цена — компонент не готов.
Место раздела в секции: на этапе 2 (потоки и API) мы фиксируем контракт — и обязаны знать, каким он бывает (REST/gRPC, пагинация, идемпотентность, 429 в контракте ошибок); на этапе 4 (детали) компоненты встраиваются в схему: rate limiter на входе, snowflake для ID сущностей, WebSocket-шлюзы в мессенджере, trie в поиске. На этапе 6 (эксплуатация) у каждого компонента своя метрика: доля 429, число WS-коннектов, лаг пересборки typeahead. Глубина — ../knowledge-base.md §13–14; полные конспекты глав Сюя — ../materials/alex-xu-vol1/ch-04-rate-limiter.md, ch-07-unique-id-generator.md, ch-09-web-crawler.md, ch-13-typeahead.md; в эталонах компоненты уже встроены — ../classic-designs.md.
1. Дизайн API: контракт, который не стыдно показать
Схема в двух фразах. REST поверх HTTP — ресурсный контракт (существительные + глаголы HTTP, кэшируемость и коды статусов из коробки) для публичных API и всего, что ходит через стандартную инфраструктуру; gRPC — бинарный protobuf поверх HTTP/2 со строгими типами и стримингом — для внутренних вызовов сервис–сервис, где важны латентность и контракт. Пагинация коллекций — курсором (opaque-токен «продолжай после этой позиции»), идемпотентность записей — idempotency key, который клиент генерирует и который сервер дедуплицирует.
Трейдоффы, которые надо называть:
- REST vs gRPC: REST — читаемость, отладка curl'ом, HTTP-кэширование, но многословный JSON и никакой схемы (OpenAPI — договорённость, не контракт); gRPC — компактность, генерация клиентов, двунаправленный стриминг, но сложнее браузерам (нужен grpc-web), тяжелее отладка и LB (HTTP/2, long-lived коннекты → least connections, KB §13.2). Правильная формула: «наружу REST, между сервисами gRPC — если есть причины, иначе и внутри REST».
- Offset vs курсорная пагинация:
offset/limitпрост и умеет «страница №N», но при вставках страницы дрейфуют (элемент уезжает между страницами), а глубокий offset — O(offset) в БД. Курсор стабилен и O(1) — «дай после post_id=X, 20 штук», но нельзя прыгнуть на произвольную страницу и нужен стабильный порядок сортировки. Для лент и любых «бесконечных» списков — курсор; для админки с пагинацией по номеру — offset допустим. - Идемпотентность записей: повтор запроса ≠ двойной эффект. Idempotency key (UUID клиента) в заголовке, сервер хранит «ключ → ответ» и на повтор отдаёт тот же результат. Это то же самое «at-least-once + дедуп», что в очередях (статья 07 §5), только на HTTP-границе — и это то, что делает безопасными ретраи клиента и доставку событий.
- Коды ошибок — часть контракта: 4xx — вина клиента и повторять бессмысленно без исправления (кроме 429 — «отступи и повтори по Retry-After»), 5xx — наша вина, ретраить можно (идемпотентно). 429 и
Retry-Afterпроектируются заранее (§2), а не появляются как 500 «internal error» на перегрузке.
Уровень детализации на этапе 2: 3–5 сигнатур, не код — метод, путь, ключевые поля запроса/ответа; control plane (создать/удалить, единицы RPS) отделён от data plane (горячий путь чтения) (статья 01 §3).
2. Rate limiter: считаем и отсекаем на входе
Схема в двух фразах. По ключу (user_id / IP / API key) считаем запросы в скользящем или фиксированном окне/корзине и всё сверх лимита отсекаем кодом 429 с заголовком Retry-After, не тратя ресурсы системы. Стоит он рано — на API-шлюзе/входе в сервис — и решает две задачи: защита от абьюза и защита системы от себя (каскадная перегрузка, статья 08 §7).
Алгоритмы — таблица, которую надо уметь проговорить (KB §14.1, Сюй гл. 4):
| Алгоритм | Идея | Плюсы | Минусы |
|---|---|---|---|
| Token bucket | В корзину капают токены со скоростью R; запрос берёт токен; нет токена — отказ | Допускает burst (копятся токены), гибкие два параметра (ёмкость, скорость) | Burst надо осознанно ограничить ёмкостью |
| Leaky bucket (бриф акцентирует) | Запросы встают в очередь, из неё вытекают с постоянной скоростью | Ровный выходной поток — идеально для защиты слабого бэкенда | Burst режется целиком; по сути те же два параметра |
| Fixed window counter | Счётчик на окно (минута) | Просто, дёшево по памяти | Burst ×2 на границе окон: 100 в конце окна + 100 в начале следующего |
| Sliding window log | Храним таймстампы каждого запроса | Точно | Дорого по памяти при большом RPS |
| Sliding window counter | Взвешиваем предыдущее окно + текущее | Компромисс цены и точности | Оценка, не точный лимит |
Разница token/leaky в одном: token bucket допускает всплески (и это фича для API), leaky bucket выравнивает поток (и это фича для защиты бэкенда с жёстким потолком). На секции безопасная формулировка: «на входе token bucket как контракт для клиента, перед тяжелым бэкендом — leaky bucket как стабилизатор».
Трейдоффы, которые надо называть: точность против памяти — sliding log точен, но хранит каждый запрос; fixed window дёшев, но пропускает burst ×2 на границе окон; точность против распределённости — локальный счётчик мгновенен, но врёт за балансировщиком; общий лимит через Redis — честный, но добавляет ход в горячем пути (и eventual между ДЦ).
Распределённый limiter: счётчики не в памяти воркера, а в Redis, инкремент и проверка лимита — одной Lua-операцией (атомарный check-and-set, иначе гонка между воркерами: каждый насчитал «ещё можно» — пропустили вдвое больше). Мульти-ДЦ: лимитер живёт на edge (GeoDNS ведёт в ближайший узел), между узлами лимит — eventual: суммарный лимит может на секунду превышаться на величину лага — учесть в нагрузочных тестах. Клиентские лимитеры (в SDK) — экономия вызовов; источник правды — серверный.
Эксплуатация: в ответе 429 + Retry-After (и/или X-RateLimit-Remaining/-Limit) — честный клиент отступает сам; мониторим долю заблокированных — высокая доля означает либо абьюз, либо неверно выбранный лимит, оба случая — actionable. Противоядие от «забыли лимитер» на этапе 6: вопрос «что защищает систему от удвоения трафика?» имеет ответ «rate limiter на шлюзе + load shedding в глубине (08 §7)».
3. Генераторы ID: уникальность без координации
Схема в двух фразах. Snowflake — 64 бита: 41 бит — таймстамп в мс (~69 лет), 10 бит — номер ноды (1024 ноды), 12 бит — последовательность (4096 ID/мс на ноду); каждая нода генерирует ID сама, без похода в центр — уникальность дают разложенные биты (время + уникальный номер ноды), а время в старших битах даёт «бесплатную» сортировку по возрастанию. Альтернативы по лестнице простоты: UUID v4 (128 случайных бит, вообще без координации), auto-increment одной БД (строгий порядок, но одна точка), централизованный ticket service с выдачей диапазонов нодам (кэшируем блоки ID — генератор не на критическом пути записи).
Трейдоффы, которые надо называть:
- UUID v4: нулевая координация, но 128 бит раздувает индексы, случайность ломает локальность B-tree и не даёт порядка — нельзя сортировать «по ID» и шардировать по времени. Хорош для correlation-id и записей без порядка, плох для первичных ключей больших таблиц.
- Auto-increment: порядок есть, но генератор — единая точка (и узкое место записи); трюк «чёт/нечет на две ноды» масштабируется плохо и предсказуем (утечка объёмов).
- Snowflake: 4096 ID/мс × 1024 ноды ≈ 4 млрд ID/с — запас на любой билет; цена — зависимость от часов: часы нод расходятся (NTP), при откате времени возможны дубли → откат отрабатываем ожиданием/буфером, а «порядок по ID» — приблизительный (часы разных нод не идентичны, миллисекунды разных нод могут пересекаться — развязка по номеру ноды). И главное — связь со статьёй 09: snowflake обходит консенсус — уникальность без единого источника, но и без строгого глобального порядка; если бизнесу нужен строгий порядок (номер документа, короткая ссылка) — нужен централизованный источник.
- Короткая ссылка ≠ snowflake: 64 бита — это ~11 символов base62 минимум; для URL shortener нужен короткий ключ — base62 от последовательности или случайный 7-символьный с проверкой уникальности (эталон —
../classic-designs.md§1). Snowflake хорош для ID сущностей (посты, сообщения, заказы), где длина не критична.
Формула для секции: «ID сущностей в масштабе — snowflake: без координации, сортировка по времени бесплатно, платим зависимостью от NTP и приблизительностью порядка».
4. Real-time доставка: транспорт по направленности
Схема в двух фразах (по каждому варианту). Короткий polling — клиент сам опрашивает раз в N секунд: тупо и надёжно, но холостые запросы и задержка до N. Long polling — сервер держит HTTP-запрос открытым, пока не появится событие, клиент тут же переоткрывает: реальное время поверх обычного HTTP, работает через старые прокси. SSE — открытый HTTP-поток «сервер → клиент» с автопереподключением из коробки: односторонний, но самый дешёвый в реализации. WebSocket — после HTTP-upgrade дуплексный TCP-канал: оба направления, один коннект, минимальный overhead на сообщение.
Выбор — таблица KB §13.3, критерий один: направленность + совместимость инфраструктуры:
| Задача | Транспорт | Почему |
|---|---|---|
| Мессенджер, коллаб-редактор, live-обновления | WebSocket | Дуплекс: и входящие, и «прочитано/печатает»; LB — least connections/sticky, ping/pong keepalive |
| Нотификации, обновления ленты, цены | SSE | Только сервер → клиент; поверх HTTP, авто-reconnect — в 5 раз проще WS |
| Старые клиенты/корпоративные прокси | Long polling | WS не проходит — имитируем push обычными запросами |
| Простые статусы («готово/не готово») | Short polling | Цена холостых запросов известна и мала |
| Push наружу (интеграции) | Webhook | Сервер → сервер, HTTP POST на зарегистрированный URL |
| Звонки/видео | WebRTC → HLS/CDN | P2P-медиа; ingest → RTMP, раздача → HLS/DASH + CDN |
Трейдофф, вокруг которого крутится раздел: WebSocket — эффективность ценой stateful-инфраструктуры. Коннект живёт на конкретном шлюзе → шлюз stateful: реплицировать нечего, но routing сообщений между шлюзами нужен (пользователь подключён к шлюзу A, отправитель — к шлюзу B: сводим через pub/sub в Redis или очередь — так устроен мессенджер, ../classic-designs.md §3); LB — least connections, не round robin; keepalive ping/pong, иначе LB и NAT рвут «тихие» коннекты; отказ шлюза = обрыв всех его коннектов → клиенты переподключаются (reconnect storm — выдерживаем джиттером и backoff, 08 §5), недоставленное — из офлайн-очереди при переподключении. SSE и long polling — stateless-ish по коннектам и потому дёшевы в эксплуатации.
Формула для секции: «транспорт выбираю по направленности: дуплекс — WebSocket, только сервер→клиент — SSE, зоопарк клиентов — long polling; WebSocket означает stateful-шлюзы, и у меня для них есть routing и reconnect-история».
5. Поисковый краулер: обход графа с вежливостью
Схема в двух фразах. Обход ориентированного графа страниц: seed URL → frontier (очередь «что качать») → загрузчик (с DNS-кэшем — резолв 10–200 мс, без кэша это узкое место) → дедуп контента по хешу (~29 % страниц — дубликаты; «видели ли URL?» — фильтр Блума) → извлечение ссылок → фильтр → обратно в frontier. Цикл живёт, пока не исчерпан бюджет; состояние (frontier, «виденное») — в хранилище, чтобы пережить падение воркера.
Вторая фраза раскрывается в главную идею — frontier не FIFO, а две группы очередей (Сюй гл. 9): лицевые (front queues) — приоритет по ценности страницы (PageRank, популярность, частота обновления): BFS «в лоб» качает мусор и бьёт веером по ссылкам одной страницы в один домен; тыльные (back queues) — вежливость: хеш по домену → своя FIFO на домен → выделенный воркер с гарантированной паузой между запросами к одному серверу. Плюс robots.txt: кэшируем и уважаем Disallow — иначе «краулер, который DDoS'ит чужие сайты», это провал на секции и в проде.
Числа для калибровки (KB §14.2): 1 млрд страниц/мес ≈ 400 QPS среднее (пик 800), при 500 КБ/страница — 500 ТБ/мес, ~30 ПБ за 5 лет → хранение и дедуп — не мелочь, а половина дизайна.
Трейдоффы: приоритизация (качаем ценное первым) против полноты обхода; фильтр Блума — память в разы меньше, но ложные срабатывания (редкие URL сочтём виденными — приемлемо для краулера, фатально для биллинга); один глобальный frontier — просто, но далеко от источников; гео-распределённый краулинг — быстрее и вежливее локально, дороже в консистентности «виденного». Спайдертрапы (календарь-бесконечность): лимит длины URL и глубины, чёрные списки; таймауты на медленные серверы; server-side рендеринг JS-страниц — отдельная дорога (headless-браузер), назвать как расширение.
6. Автозаполнение (typeahead): префиксное дерево с готовыми ответами
Схема в двух фразах. Ядро — trie (префиксное дерево) с частотами, и главная оптимизация: в каждом узле храним готовый топ-k популярных дописываний — ответ на запрос это спуск по префиксу (по буквам, O(длины)) + чтение готового списка, без обхода поддерева и сортировки на лету. Топ-списки строятся офлайн: логи запросов → агрегаторы частот (для Google — неделя, для Twitter — реальное время) → воркеры пересобирают trie → снапшот в БД/KV → сервисы грузят trie в память → API отвечает из памяти.
Числа для калибровки (KB §14.4, Сюй гл. 13): 10M DAU × 10 запросов × 20 символов ≈ 24k QPS (пик 48k) — запрос на каждый введённый символ; требование отклика <100 мс, топ-5 подсказок. Наивный алгоритм (обход поддерева + сортировка на каждый запрос) на таком QPS не живёт — поэтому топ-k в узлах.
Трейдоффы: предвычисленный топ-k в каждом узле — платим памятью (каждый узел хранит список), забираем латентность (O(1) на выдаче); свежесть подсказок — офлайн-пересборка даёт лаг минут/часов (для трендов — инкрементальные обновления или подмешивание realtime-списка поверх снапшота); шардирование trie — по диапазонам префиксов (a–f → нода 1), горячие префиксы — репликами; ограничение максимальной длины префикса — вторая оптимизация памяти. Вариант вопроса «топ-k самых частых запросов за период» — это тот же дизайн: агрегатор частот + топ-k структуры (heap/Count-Min), переиспользуем схему.
7. Сводная таблица: компонент → две фразы → трейдофф
Ядро раздела — критерий брифа. Таблица — шаблон «двух фраз» для репетиции: закрыть таблицу и проговорить каждую строку вслух за 60 секунд на компонент.
| Компонент | Фраза 1: схема | Фраза 2: трейдофф | Где звучит |
|---|---|---|---|
| API (REST/gRPC) | REST наружу (ресурсы, HTTP-семантика, курсорная пагинация, idempotency key), gRPC между сервисами (protobuf, HTTP/2) | REST — читаемость и HTTP-кэш ценой многословности; gRPC — скорость и строгий контракт ценой отладки и LB для HTTP/2 | Этап 2 любого билета (01 §3) |
| Rate limiter | По ключу считаем запросы в окне/корзине (token/leaky bucket), сверх лимита — 429 + Retry-After; распределённо — Redis + Lua на входе шлюза |
Точность против памяти (sliding log точен и дорог; window counter дёшев и врёт на границе ×2); на edge — eventual между ДЦ | 08 §7 (перегрузка), шлюз в любой схеме |
| ID (snowflake) | 64 бита: 41 время + 10 нода + 12 секвенция — каждая нода генерирует сама, старшие биты дают сортировку по времени | Без координации и без единой точки, но порядок приблизительный (часы нод) и нужна дисциплина NTP; UUID — нулевая координация, но раздутые индексы и нет порядка | 09 §7 (уникальность), 11 §3–4 (посты, сообщения) |
| Real-time транспорт | Дуплекс — WebSocket (upgrade, один коннект), однонаправленно — SSE поверх HTTP, совместимость — long polling; routing между WS-шлюзами через pub/sub | WS — эффективность ценой stateful-шлюзов (sticky/least-conn LB, reconnect storm, офлайн-очередь); SSE — дёшев, но только сервер→клиент | 11 §3 (мессенджер), 11 §2 (лента) |
| Краулер | Графовый обход: frontier (front-очереди по ценности + back-очереди по доменам с паузами) → загрузчик с DNS-кэшем → дедуп по хешу/Блуму → извлечённые ссылки в frontier | Приоритет против полноты; Блум — память против ложных срабатываний; вежливость (паузы, robots.txt) обязательна — иначе DDoS источников | Отдельный вопрос; общий паттерн «очередь задач + воркеры» (07) |
| Typeahead | Trie с частотами, в каждом узле — готовый топ-k; топ-списки пересобираются офлайн из логов, снапшоты раздаются сервисам | Память (топ-k в каждом узле) против латентности (O(1) выдача); свежесть — лаг офлайн-пересборки | Поиск в ленте/маркете; «топ-k запросов» |
8. Ошибки этого раздела
| Ошибка | Как выглядит | Противоядие |
|---|---|---|
| Название вместо схемы | «Возьмём Redis» / «будет WebSocket» — без стрелки данных и цены | Формула «2 фразы: схема + трейдофф»; сначала механизм, потом технология (§7) |
| Rate limiter «в каждом сервисе» | Лимитируют на 5 ярусах, клиент получает 429 непонятно от кого | Один источник правды на шлюзе; вложенные лимитеры — только с осознанной иерархией (§2) |
| Fixed window по умолчанию | «Счётчик на минуту» — а burst ×2 на границе окон проходит | Назвать границу окон вслух; sliding window counter как компромисс (§2) |
| Забыли контракт отказа | 429 без Retry-After; клиент долбится ретраями |
429 + Retry-After + X-RateLimit-* — часть API-контракта (§1, §2) |
| Snowflake «просто генератор» | Не названы биты, NTP и приблизительность порядка | 41+10+12, откат часов — буфером, строгий порядок — централизованный источник (§3) |
| Короткая ссылка из snowflake | 64-битный ID → 11+ символов ключа — «короткая» ссылка не короткая | Ключ — base62 последовательности/случайный с проверкой уникальности (§3, classic-designs §1) |
| WebSocket везде | Push-нотификации через WS, а это SSE за час работы | Выбор по направленности (§4); WS — только дуплекс |
| WS без истории эксплуатации | Не сказано про stateful-шлюзы, LB и обрывы коннектов | Routing через pub/sub, least connections, ping/pong, reconnect с джиттером (§4) |
| Краулер-BFS в лоб | FIFO по ссылкам → штурм одного домена и мусор в приоритете | Front/back очереди, пауза на домен, robots.txt (§5) |
| Typeahead на лету | Обход поддерева и сортировка на каждый запрос при 48k QPS | Топ-k в узлах, офлайн-пересборка, память против латентности (§6) |
| Offset-пагинация в ленте | offset 100000 — дрейф страниц и O(offset) в БД |
Курсор для бесконечных списков, offset — только для «страница №N» (§1) |
9. Что назвать на секции (чек-лист раздела)
Обязательные фразы и действия. Полный чек-лист — ../methodology.md §7; канон — ../knowledge-base.md §13–14.
На этапе 1 (требования): вытащить из вводной, какие компоненты нужны: «real-time?» → транспорт; «абуз/публичный API?» → rate limiter; «поиск/подсказки?» → typeahead. Не рисовать компонент, которого нет в требованиях, — но знать, что он появится, если интервьюер спросит.
На этапе 2 (API): контракт из 3–5 сигнатур с идемпотентностью записей (idempotency key) и курсорной пагинацией для списков; в контракте ошибок — 429 + Retry-After; control plane отделён от data plane (01 §3).
На этапе 3–4 (схема и детали): - Rate limiter — на входе, до дорогих операций: «token bucket на шлюзе, Redis + Lua, ключ — user_id; высокие 429 мониторим» (§2). - ID сущностей — snowflake одной фразой: «64 бита: время + нода + секвенция, без координации, сортировка по времени бесплатно» (§3). - Real-time — транспорт по направленности + одна фраза про stateful-шлюзы: «WS-шлюзы stateful, routing через pub/sub, reconnect с backoff» (§4). - Краулер/typeahead по запросу — front/back очереди и топ-k в узлах, каждый со своим числом-калибровкой (§5–6).
На этапе 6 (эксплуатация): у каждого компонента своя метрика: доля заблокированных (rate limiter), число живых коннектов и reconnect-шторма (WS), лаг пересборки топ-списков (typeahead), глубина frontier (краулер) — «метрика привязана к узлу, а не списком в конце» (KB §13.8; глубина — 12-operations-observability.md).
Сквозные формулы раздела:
- «Любой компонент — две фразы: схема по стрелке данных + чем плачу; детали — по запросу».
- «Лимитирую на входе, отвечаю 429 + Retry-After, мониторю долю отказов».
- «Snowflake: уникальность без координации, время в старших битах; строгий порядок — отдельно».
- «Транспорт — по направленности: дуплекс WS, сервер→клиент SSE, совместимость long polling».
- «Краулер — это вежливость: очереди на домен, паузы, robots.txt».
10. Самопроверка «умею, если»
Критерий раздела из брифа: для любого из компонентов могу в 2 фразах описать схему и трейдофф. Протокол проверки (закрытые материалы, вслух, 60 секунд на компонент): для каждого из шести — фраза-схема, фраза-трейдофф, затем «где этот компонент встал бы в билете инстаграм/твиттер» (rate limiter — шлюз, snowflake — ID постов, WS — мессенджер, typeahead — поиск). Затем четыре зонда: (1) «чем leaky bucket отличается от token bucket?» (ровный выход vs допустимый burst), (2) «почему для ленты не offset-пагинация?» (дрейф и O(offset) — курсор), (3) «как масштабируете WS-шлюзы?» (stateful: least-conn LB, pub/sub routing, reconnect backoff), (4) «как краулер не DDoS'ит сайты?» (back-очереди на домен + пауза + robots.txt). Маркеры готовности: (1) шесть строк таблицы §7 проговариваются без запинки и без подглядывания; (2) у каждого компонента всплывает число-калибровка (4 млрд ID/с, 48k QPS typeahead, 400 QPS краулер); (3) на «а что если упадёт…» у WS-шлюза и лимитера есть готовая история с метрикой. Протокол тренировок и рубрика 0–3 — ../methodology.md §6; встроенные примеры — ../classic-designs.md §1–4. Если «две фразы» звучат раньше названия технологии — раздел готов.
Связанные документы
00-overview.md— обзорная статья по всем 12 разделам брифа (TASK-45.40); §10 — карта раздела01-interview-framework.md— этап 2: сигнатуры API, control vs data plane, идемпотентность в контракте02-estimations.md— числа-калибровки компонентов (QPS, storage)04-data-models-storage.md— где живут данные компонентов (KV для лимитера, снапшоты trie, хранилище frontier)07-queues-streams.md— rate limiter как защита входа в очередь; frontier краулера как очередь задач; идемпотентность at-least-once08-fault-tolerance.md— rate limiting/load shedding в картине перегрузки; backoff + jitter для reconnect09-consistency.md— snowflake обходит консенсус; уникальность без порядка vs со строгим порядком11-reference-designs.md— эталоны, где компоненты встроены: snowflake и rate limiter в shortener/ленте, WebSocket в мессенджере-
12-operations-observability.md— метрики компонентов на этапе 6 (доля 429, коннекты, лаг пересборки) -
../brief.md— бриф: раздел 10 с критерием «умею, если» ../knowledge-base.md— §13 (инфографика: LB-алгоритмы 13.2, транспорт 13.3, ID 13.4, локи 13.5) и §14 (rate limiting 14.1, краулер 14.2, уведомления 14.3, typeahead 14.4)../methodology.md— §4 топ-10 ошибок, §6 протокол тренировок, §7 карточка на секцию../classic-designs.md— §1 shortener (генерация короткого ключа), §3 мессенджер (WS-шлюзы и routing)../materials/alex-xu-vol1/ch-04-rate-limiter.md— источник: алгоритмы rate limiting, распределённый limiter, заголовки 429 (TASK-45.38)../materials/alex-xu-vol1/ch-07-unique-id-generator.md— источник: разбор snowflake по битам, ticket service, хеш по порядку (TASK-45.38)../materials/alex-xu-vol1/ch-09-web-crawler.md— источник: frontier, вежливость, фильтр Блума, числа краулинга (TASK-45.38)../materials/alex-xu-vol1/ch-13-typeahead.md— источник: trie, топ-k в узлах, офлайн-конвейер, шардирование по префиксам (TASK-45.38)../materials/lsd-ultimate-guide-image.md— расшифровка инфографики: real-time транспорт, API-шлюз, генерация ID (TASK-45.24)- Соседние статьи:
09-consistency.md(уникальность и порядок ID — отсылки в §7 таблице) ·11-reference-designs.md(компоненты встроены в эталоны) ·12-operations-observability.md(метрики компонентов)