Job 2026 md

title: Системный дизайн — база знаний (числа, back-of-envelope, хранилища, паттерны, карта DDIA) source: подготовлено 2026-09-17, основание — TASK-45.1; источники — System Design Primer (Appendix + гайд), статья Яндекса habr 564132, DDIA Клеппмана, interactive latency (colin-scott, TASK-45.25), производственный опыт


База знаний по системному дизайну

Плотный справочник для архитектурной секции. Задача документа — чтобы на секции числа, названия паттернов и трейдоффы звучали из головы, без пауз. Формат: таблицы + короткие правила проговаривания. Как пользоваться: §1–2 выучить до автоматизма (это скелет back-of-envelope), §3–4 — критерии выбора, §5–11 — словарь паттернов с формулировками для доски, §12 — карта DDIA, §13 — карта инфографики «System Design Blueprint» с мини-блоками, которых не было в §1–12.


1. Числа

1.1 Степени двойки

Степень Точное значение Приближение Название
2^10 1 024 ~1 тысяча 1 KiB
2^20 1 048 576 ~1 миллион 1 MiB
2^30 ~1.07 × 10^9 ~1 миллиард 1 GiB
2^40 ~1.1 × 10^12 ~1 триллион 1 TiB
2^50 ~1.13 × 10^15 ~1 квадриллион 1 PiB
2^60 ~1.15 × 10^18 ~1 квинтиллион 1 EiB

Размеры типовых сущностей (нужно для оценки storage):

Сущность Размер
Символ ASCII / байт UTF-8 (латиница) 1 байт
Int / float 4 байта
Long / double / Unix-timestamp (int64) 8 байт
IPv4-адрес 4 байта
UUID 16 байт
IPv6-адрес 16 байт
SHA-256 (хеш) 32 байта
Короткая строка / URL ~50–500 байт
Запись в БД среднего размера ~100 байт – 1 КиБ
Страница HTML (сжатая) ~50–100 КиБ
Фото (сжатое) ~0.1–5 МиБ
Минута видео 480p / 1080p ~10 МиБ / ~50–100 МиБ

1.2 Latency numbers every programmer should know

Действие Время В человекообразных единицах (1 нс = 1 с)
L1 cache reference 0.5 нс 1 с
Branch mispredict 5 нс 5 с
L2 cache reference 7 нс 7 с
Захват/освобождение mutex 25 нс 25 с
Обращение в RAM 100 нс 2 мин
Сжатие 1 КиБ (snappy/zstd) ~3–10 мкс ~час
Отправить 2 КиБ по 1 Gbps 20 мкс 5.5 ч
Чтение 1 МиБ последовательно из RAM 250 мкс ~3 суток
Round-trip внутри дата-центра 0.5 мс ~6 суток
Чтение 1 МиБ последовательно с SSD (NVMe) 50 мкс – 1 мс до 12 суток
Random read 4 КиБ с SSD ~150 мкс ~2 суток
Disk seek (HDD) 10 мс 4 месяца
Чтение 1 МиБ последовательно с HDD 30 мс 1 год
Round-trip через океан (CA ↔ Европа) ~150 мс 4.8 года

Производные метрики (та же таблица «на поток»):

Канал Скорость последовательного чтения
RAM ~4 ГиБ/с
SSD ~1 ГиБ/с (NVMe — до 3–7 ГиБ/с)
HDD ~30–100 МиБ/с
Сеть 1 Gbps ~100–125 МиБ/с
Сеть 10 Gbps ~1 ГиБ/с
Round-trip в ДЦ ~2 000 RT/с — сетевые хопы «не бесплатные»
Свет вокруг Земли ~5–7 оборотов/с — «скорость света» в распределённых системах это ~150 мс до другого континента

Мнемоники: - Лестница ×1000: нс → мкс → мс → с. RAM — нс, SSD — мкс, HDD и сеть до другого ДЦ — мс, океан — сотни мс. - Каждый следующий уровень быстрее предыдущего в ~10–1000 раз; RAM быстрее SSD в ~100–1000 раз, SSD быстрее HDD-seek в ~100 раз. - «Память — это бесплатно, диск — это дорого, сеть через океан — это вечность»: отсюда все паттерны (кэш, батчинг, locality). - Избегать: random reads на HDD (10 мс seek), синхронных вызовов через океан в горячем пути, «одна лишняя цифра в латентности = другой дизайн». - Порог человеческого восприятия — ~100 мс: лаг меньше ~100 мс человек не замечает (источник 4, habr 516604). Отсюда SLO 150 мс у эталона Яндекса — «на грани комфорта», а round-trip через океан (~150 мс) сам съедает весь бюджет ответа.

Происхождение чисел: тренды железа (материал 21, colin-scott — интерактивная таблица «числа по годам»):

  • CPU-числа — это такты: L1 = 3, branch mispredict = 10, L2 = 13, mutex = 50, сжатие 1 КиБ = 6 000 тактов; при 3 ГГц (0.33 нс/такт) → 1 / 3 / 4 / 17 нс / 2 мкс. На секции называть порядок, не десятые доли (CPU-числа плавают в разы от машины к машине, порядок — стабилен).
  • Стена частот — 2005 год: герцы удваивались каждые 2 года до ~2005, дальше замерли на ~3 ГГц; расти продолжили ядра, не герцы. Однопоточная производительность исчерпана — горизонтальное масштабирование и конкурентность не выбор, а следствие.
  • Полы латентности не двигаются 20+ лет: RAM 100 нс (шина даже деградирует с 2000), RT в ДЦ 0.5 мс, океан 150 мс — скорость света. Полосы при этом удваиваются: NIC ×2/2 года, DRAM и SSD ×2/3 года, HDD ×2/5 лет, HDD-seek — всего /2 за 10 лет (самый медленный тренд).
  • Фраза для доски: «полоса дешевеет, латентность — нет» → батчить, пайплайнить, параллелить, меньше round trip'ов. Одним предложением объясняет пайплайны в протоколах, батчи в Kafka, мультиплексирование HTTP/2.
  • Цветовая лестница ×100 (визуальная мнемоника источника): такты/нс → RAM/100 нс → сеть и SSD в ДЦ/10 мкс → диски и океан/мс — перепутать классы сложно: мьютекс не бывает «зелёным», RT в ДЦ — «синим».
  • Ориентир свежести: обновление таблицы 2018 (Jeff Dean) — сжатие 1 КиБ 10 → 2 мкс, SSD random read 150 → 16 мкс; на секции безопаснее консервативные значения канона. Конспект и пересчёт тренд-модели → materials/lsd-interactive-latency.md.

1.3 Календарные константы

Константа Значение Округление для доски
1 час 3 600 с 3.6 × 10^3
1 сутки 86 400 с ~100 тыс. с (10^5)
1 месяц (30 дней) 2 592 000 с ~2.5 млн с
1 год 31 536 000 с ~30 млн с (3 × 10^7)
100 лет ~3.15 млрд с ~3 × 10^9

Availability в минутах простоя: 99 % = 3.65 дня/год; 99.9 % («три девятки») = 8.7 ч/год; 99.99 % = 52 мин/год; 99.999 % = 5 мин/год (доп. Alex Xu, гл. 2: простой = (100 − SLA)% × год).


2. Back-of-envelope estimation

2.1 Метод (порядок вопросов себе)

  1. Масштаб пользователей: DAU / MAU, пик одновременных.
  2. RPS: QPS_avg = DAU × запросов/сутки / 86 400; QPS_peak ≈ 2–3 × QPS_avg (утро/вечер, релизы). Reads обычно ≥ writes в 10–100 раз; запись чаще всего и есть узкое место по консистентности.
  3. Storage: записей/с × размер × секунд/год × retention × фактор репликации + индексы (×2 от сырых данных — хорошая прикидка).
  4. Bandwidth: чтение = read QPS × размер объекта, запись = write QPS × размер. Сравнить с 1/10 Gbps NIC и решить: нужен ли CDN / сжатие.
  5. Память кэша: правило 80/20 — кэшируем горячие 20 %; память = 0.2 × DAU × запросов/сутки × размер объекта.
  6. Серверы: типовое приложение-сервис держит ~1–10 тыс. RPS на ноду (зависит от работы: CPU-bound vs IO-bound); БД — лучше не считать «одна нода потянет», а сразу делить роли.

Правило разговора: назвать допущение → формулу → число → вывод («это терабайты, влезает в одну ноду, значит узкое место не storage, а RPS на чтение → кэш/KV-зеркало»).

2.2 Пример 1: storage (URL shortener)

Дано: 100 млн новых ссылок/месяц, retention 10 лет, запись ~500 байт.

  • Записи: 100M × 12 мес × 10 лет = 12 млрд записей.
  • Сырые данные: 12G × 500 Б = 6 ТБ; с индексами и оверхедом ×2 → ~12 ТБ.
  • Вывод: 12 ТБ — это одна-две ноды с дисками; storage не узкое место. Значит дизайн крутится вокруг чтения (см. classic-designs.md: 500k RPS → KV-зеркало + кэш).

2.3 Пример 2: RPS (лента новостей)

Дано: 100 млн DAU, пользователь открывает ленту 10 раз/сутки, публикует 2 поста/сутки, в среднем 300 подписчиков.

  • Чтение: 100M × 10 / 86 400 ≈ 11.5k QPS avg, пик ×3 → ~35k QPS.
  • Запись постов: 100M × 2 / 86 400 ≈ 2.3k QPS.
  • Fan-out: каждый пост → 300 вставок в ленты подписчиков → 2.3k × 300 ≈ 700k вставок/с в кэш лент. Вот здесь узкое место: не пост, а доставка. Отсюда выбор fan-out on write vs read и микс для селебрити (см. classic-designs.md §2).

2.4 Пример 3: bandwidth (видео-хостинг)

Дано: 10 тыс. загрузок/сутки × ~1 ГБ исходник; 100 тыс. просмотров/сутки, средний просмотр 5 мин при битрейте 3 Mbps ≈ 110 МиБ.

  • Storage-рост: 10k × 1 ГБ = 10 ТБ/сутки исходников + транскоды ×3–5 версий → 30–50 ТБ/сутки. Через год — десятки ПБ: только объектное хранилище (S3-класс), никакой NAS.
  • Egress: 100k × 110 МиБ ≈ 10 ТБ/сутки → средний поток 10 ТБ / 86 400 с ≈ 120 МБ/с ≈ 1 Gbps, пик ×3–5 → 3–5 Gbps. Вывод: отдавать клиентам напрямую из кластера нельзя (дорого и медленно глобально) → CDN с offload 90 %+.
  • Транскодинг: 10k видео/сутки, минуты CPU на минуту видео → асинхронный пайплайн через очередь (обработка может отставать от загрузки на минуты — это приемлемо и проговаривается).

2.5 Гигиена оценок и пример Twitter (Alex Xu, гл. 2)

Правила из книги — как подавать прикидку на секции:

  • Округлять до круглых чисел: не 11.64 МБ, а «примерно 10 МБ» — интервью про порядок, а не про точность.
  • Записывать допущения (письменно/на доске): «предположим 100 млн DAU, 2 твита/день» — интервьюер сможет поправить, а не оспорить результат.
  • Подписывать единицы: KB — килобайт, Kb — килобит (×8 разницы); латентность в мс, bandwidth в МБ/с.
  • Пример книги (Twitter): 100M DAU × 2 твита ≈ 2300–3500 QPS запись; медиа ~30 ТБ/день; 5 лет ≈ 55 ПБ — порядок величин важнее цифр.

3. Классы хранилищ и критерии выбора

Класс Представители Модель / движок Сильные стороны Слабые стороны Когда выбирать
RDBMS PostgreSQL, MySQL B-tree, строгая схема, SQL/JOIN ACID, транзакции, сложные запросы, зрелость, ограничения целостности Вертикальный масштаб, шардирование вручную, дорогие high-write-нагрузки на большие объёмы Деньги, заказы, связи сущностей; данные до единиц ТБ; консистентность важнее доступности
KV Redis, DynamoDB, Cassandra hash / LSM; O(1) по ключу Гигантский RPS на ноду, горизонтальный масштаб, TTL Нет JOIN и сложных запросов, ограниченная модель, у Redis — память Сессии, счётчики, фичи-флаги, кэш, hot path по известному ключу
Document MongoDB, Couchbase JSON-документы Гибкая схема, агрегаты «документ = объект домена», атомарность на документ JOIN слабые, транзакции между документами — боль Контент с вариативной структурой, каталоги, профили
Column / analytics ClickHouse, HBase, Bigtable columnar / LSM-семейство Агрегации по миллиардам строк, сжатие, потоковая вставка Нет точечных UPDATE, слабые транзакции Логи, метрики, аналитика, отчёты, event store для чтения
Search Elasticsearch, Solr, OpenSearch инвертированный индекс Полнотекстовый поиск, фасеты, морфология, fuzzy Не источник правды (в конечном счёте eventual), дорогая запись Поиск по тексту, автодополнение, логи-поиск
Time-series Prometheus, InfluxDB, VictoriaMetrics time-partitioned columnar Даунсемплинг, retention, быстрые оконные агрегаты Специализированная модель Метрики, мониторинг, телеметрия
Graph Neo4j граф, index-free adjacency Траверсы на глубину, связи N-го порядка Редко нужно; операции на массовом графе дороги Соцграф, рекомендации, fraud-цепочки
Object storage S3, GCS, MinIO blob по ключу Почти бесконечный масштаб, дешевизна, 11 девяток durability Высокая латентность, нет списков/запросов в реальном времени Медиа, бэкапы, стейджинг для batch
Event log Kafka append-only partitioned log Реплей, ретеншн, десятки МБ/с на партицию, порядок в партиции Не queryable, порядок только в партиции Интеграция сервисов, event sourcing, батчи-конвейеры

Как отвечать на секции (по одному предложению на выбор): «Данные реляционные и за деньги → PostgreSQL; hot path по ключу с RPS, которые не тянет SQL → KV-зеркало/Redis; поиск → инвертированный индекс; аналитика → колоночная БД; медиа → S3 + CDN; событийная интеграция → Kafka. Источник правды один (RDBMS), остальные — производные проекции с известным лагом».

3.1 Движки хранения: LSM vs B-tree (DDIA 2-е изд., гл. 4)

Два семейства OLTP-движков — классический вопрос этапа 4 «а как внутри вашей БД»:

  • LSM-tree (RocksDB, Cassandra, HBase, LevelDB): запись → memtable (отсортированная структура в памяти) → flush в SSTable (иммутабельные отсортированные файлы на диске, компрессия, sparse index); фоновая компактация сливает сегменты как mergesort; удаление — tombstone; Bloom filter отсекает чтение отсутствующих ключей; компактация size-tiered (сначала мелкие равного размера) vs leveled (уровни по размеру) — компромисс write/пространство.
  • B-tree (PostgreSQL, MySQL, почти все RDBMS): страницы фиксированного размера, обновление на месте, глубина O(log n) — на практике 3–4 уровня (≈250 TB при branching factor ~500); page split при заполнении; надёжность через WAL (журнал до модификации страниц); copy-on-write варианты (LMDB) дают снапшоты.
  • LSM vs B-tree: LSM — выше write throughput (только последовательные записи, ниже write amplification, лучше компрессия), плата — чтение старых ключей дороже (несколько SSTable), range слабее, компактация конкурирует за IO → латентность-спайки; B-tree — предсказуемое точечное чтение и range, плата — write amplification (WAL + запись страницы целиком). SSD: sequential > random даже на flash (erase block ~512 KiB).

4. Кэш

Стратегии: - Cache-aside (базовая): приложение читает кэш, при промахе — БД и запись в кэш; при записи — БД, кэш инвалидируется. Просто, консистентность через TTL. - Read-through / write-through: кэш-слой сам ходит в БД; запись идёт сквозь кэш. Строже, но дороже на запись. - Write-behind (write-back): запись в кэш, в БД — асинхронно батчами. Быстро, но риск потери при падении — проговаривать явно. - Refresh-ahead: предиктивное обновление горячего до истечения TTL (профилактика stampede).

Инвалидация: TTL (универсальная страховка), инвалидация по записи (delete, не update), версионированные ключи (key:v2), событийная инвалидация через шину (Kafka → удалить/прогреть). Классическая гонка «читатель успел положить старое после записи» → лечится коротким TTL; радикально — delayed double delete.

Cache stampede (thundering herd): истечение тысячи горячих ключей разом роняет БД. Защиты: (1) single-flight / distributed lock — один запрос пересчитывает, остальные ждут; (2) probabilistic early expiration (XFetch) — обновлять заранее с вероятностью, растущей от близости TTL; (3) разогрев и разнесённые TTL (jitter).

Прочее, что надо проговаривать: hit ratio цель 90 %+ для hot path; LRU как дефолт, LFU для неравномерных; hot key (селебрити) → локальный L1 + реплики ключа; метрики кэша обязательны (hit ratio, evictions, latency).


5. Репликация

  • Leader–follower (базовая): запись на лидера, реплики тянут WAL/binlog. Sync — не теряем данные, платим латентностью; async — быстро, лаг и потери при failover; semi-sync — компромисс (ждём ACK одной реплики).
  • Replication lag: read-your-writes ломается. Средства: читать свои записи с лидера (sticky), монотонное чтение с одной реплики, версионные токены (LSN), наблюдение за lag как SLO.
  • Multi-leader: несколько пишущих узлов (мульти-ДЦ, оффлайн-клиенты). Плата — конфликты writes; решение: LWW (теряет данные), CRDT, ручное разрешение. На секции: «избегаю без необходимости».
  • Leaderless (Dynamo-стиль): пишем в W реплик, читаем из R, при W + R > N читаем свежее; hinted handoff, read repair, anti-entropy (Merkle-деревья). Tunable consistency. Членство/детект отказов — gossip-протокол (без центрального детектора = без SPOF); при недоступности «своих» узлов — sloppy quorum: пишем на ближайшие доступные с hint, отдаём владельцу позже (Alex Xu, гл. 6).
  • Failover: детект (heartbeat + кворум), выбор нового лидера, fencing старого (split brain = два лидера пишут — катастрофа), автоматический vs ручной (проговаривать: автоматика быстрее, но ложные failover дороже простоя).
  • Что реплицируем (DDIA 2-е изд., гл. 6): statement-based (нестабильно: now()/rand()), WAL (привязан к версии движка), logical/row-based лог — переносимый компромисс, фундамент CDC; сессионные гарантии формулируются по позиции в логе (read-your-writes = «читай с реплики, дотянувшей позицию моей записи»; monotonic reads = приклеивание к реплике; consistent prefix reads = порядок причинных записей). Кворумы W+R>N дают свежесть, но не linearizability — см. §7.

6. Шардирование

Сначала — а надо ли? Лестница «до шардирования»: индексы → вертикаль (железо) → реплики на чтение → кэш → денормализация/проекции → и только потом шарды. Шардирование — последняя ступень, потому что оно ломает транзакции, JOIN и операции.

Стратегии: - Range: по диапазону ключа (даты, ID-отрезки). Плюс — range-запросы, минус — hot spot на «свежем» диапазоне. - Hash: равномерно, но нет range-запросов; перешардирование болезненное. - Consistent hashing: кольцо + виртуальные ноды (100–200 на ноду) → при добавлении/уходе узла переезжает ~1/N ключей, а не почти всё. Стандартный ответ на «как перекладывать шарды без даунтайма». - Directory: lookup-сервис хранит карту; гибко (неровные данные), но сама карта — точка отказа/кэширования.

Hot partition: селебрити/перегретый ключ перекашивает шард. Средства: salting (ключ + суффикс с последующей агрегацией), выделение горячего в отдельный шард/сервис, локальный кэш на ноде.

Шард-ключ — главное решение: выбирается по access pattern, менять после — переливка всех данных. Кросс-шардные транзакции и JOIN → избегать (денормализация, 2PC — осторожно, см. §7). Ребалансировка: удвоение числа шардов заранее, миграция в фоне с dual-read.

Поверх шардов (DDIA 2-е изд., гл. 7): - Вторичные индексы: local (каждый шард ищет сам → scatter-gather по всем шардам) vs global (сам распределён по ключу индекса — быстрее поиск, но отстаёт от записи и hot-spot-уязвим). - Сколько шардов: фиксированное число маленьких шардов заранее — миграции дешевле, но больше метаданных; динамическое расщепление — автоматика, но движущиеся границы. Не mod N (двигается почти всё). - Request routing: «где мои данные?» — слой маршрутизации + метаданные в координационном сервисе + кэш на клиенте; изменение членства — gossip/notifications. - Мультиарендность: tenant_id в ключе шарда; шумные соседи (noisy neighbors) изолируются выделенными шардами.


7. Консистентность и распределённые транзакции

  • CAP: при сетевом разделении (P — не опция, оно случается) выбираем C или A. Формулировка для доски: «в одном ДЦ почти всегда C; между ДЦ — вынужденно AP с eventual».
  • PACELC: даже без разделения (Else) платим Latency за Consistency или наоборот. Честнее CAP в обычной жизни: строгая консистентность = дополнительный round-trip.
  • Модели: linearizability (выглядит как одна копия; дорого) → sequential → causal → eventual (дёшево, но аномалии: read-your-writes, monotonic reads нужно обеспечивать отдельно).
  • Quorum: W + R > N гарантирует пересечение; настройка W=N/R=1 (строгая запись), W=1/R=N (строгое чтение), промежуточные — по чтению/записи. Конфликты параллельных версий отслеживаем векторными часами (счётчики по узлам в каждой записи): определяют причинность; параллельные ветки → отдаём клиенту обе версии, слияние вручную (Alex Xu, гл. 6, Dynamo).
  • 2PC (prepare/commit): строгая атомарность, но координатор — точка блокировки и отказа; на секции говорить: «в высоконагруженных системах избегаю, применяю саги».
  • Saga: последовательность локальных транзакций + компенсирующие действия при откате (order → reserve → charge → ship; откат = cancel/refund). Оркестрация (координатор) vs хореография (события через шину).
  • Transactional outbox: запись в БД и публикация события атомарно: событие пишется в outbox-таблицу в той же транзакции, отдельный воркер/CDC (Debezium) публикует в Kafka. Решает «БД записала, событие потерялось».
  • Идемпотентность: точно-once между системами не существует — есть at-least-once + дедупликация (idempotency key, уникальные constraint, таблица обработанных).

Уровни изоляции и гонки (DDIA 2-е изд., гл. 8): read committed (нет dirty read/write: MVCC — читатели видят закоммиченные версии, row-lock на запись) → snapshot isolation (транзакция читает согласованный снапшот на старте) → serializable (2PL или SSI: MVCC + отслеживание опасных rw-антизависимостей → abort вместо блокировок). Гонки: lost update (read-modify-write; лечится SELECT FOR UPDATE / CAS / автоматическим детектом в MVCC), write skew (две транзакции читают пересечение, пишут разные строки — инвариант сломан; лечится serializable/constraint/материализацией конфликта), phantom (предикатные чтения → predicate locks).

Linearizability vs serializability (DDIA 2-е изд., гл. 10, вопрос-ловушка): linearizability — гарантия свежести одной операции («после завершения записи все читают новое», система выглядит как одна копия); serializability — гарантия изоляции транзакций (порядок любой, если эквивалентен последовательному). Вместе = strict serializability (Spanner, FoundationDB). Нужна для: lock/leader election, uniqueness-ограничений, cross-channel timing. Дают: single-leader (пока лидер настоящий) и консенсус; кворумы W+R>N НЕ дают (гонки concurrent read repair; LWW по wall-clock — тем более теряет данные).

Часы и порядок (DDIA 2-е изд., гл. 9–10): time-of-day (NTP, прыжки, drift до 200 ppm) против monotonic (только интервалы). Lamport clock (counter+nodeID) и hybrid logical clock (HLC) дают total order, согласованный с причинностью — но не linearizability; параллельность детектируют только vector clocks (дороже по размеру). ID: автоинкремент = linearizable fetch-and-add; Snowflake/UUIDv4/ULID теряют порядок.

Консенсус (DDIA 2-е изд., гл. 10): эквивалентные формулировки — single-value consensus, linearizable CAS, shared log (total order broadcast), atomic commit, fetch-and-add; свойства agreement/integrity/validity (safety — держится всегда) + termination (liveness — требует большинства живых). Механика: epoch/term, уникальный лидер эпохи, два голосования (выборы + коммит) с пересекающимися кворумами. Практика — координационные сервисы (ZooKeeper/etcd/Consul): locks/leases + fencing tokens (zxid/cversion в ZooKeeper, revision в etcd, epoch в Kafka; term/ballot в Raft/Paxos) + failure detection (сессии, ephemeral nodes) + change notifications; service discovery — кэшировать (observers).

Распределённые транзакции на практике (DDIA 2-е изд., гл. 8): 2PC/XA — in-doubt транзакции держат локи при падении координатора, «heuristic decisions» ломают атомарность; NewSQL (CockroachDB, Spanner, TiDB, FoundationDB, транзакции Kafka) — реплицированный координатор на консенсусе, без XA-болей. Exactly-once без распределённых транзакций: таблица обработанных message-ID в локальной транзакции = идемпотентный консьюмер (так делает Kafka Streams).


8. Очереди и потоковая обработка

Зачем: развязка сервисов, буферизация пиков (load leveling), ретраи, broadcast, порядок и реплей. Плата: лаг, дубли, сложность отладки.

Kafka — словарь: topic → партиции (порядок и параллелизм внутри партиции; ключ маршрутизирует), consumer group (каждая партиция — одному консьюмеру группы), offsets (позиция, коммитится; реплей с любого offset), retention по времени/размеру (лог можно перечитать), ISR для надёжности, acks=all для данных, которые нельзя терять.

Delivery semantics: - At-most-once — может потерять, не дублирует (fire-and-forget). - At-least-once — дефолт надёжных систем; дубли неизбежны → потребитель обязан быть идемпотентным. - Effectively-once — at-least-once + дедуп (Kafka transactions / idempotent producer / ключи дедупа на стороне потребителя).

Паттерны: DLQ для ядовитых сообщений, backpressure (не тащим быстрее, чем можем обработать), competing consumers для масштаба, приоритетные очереди (отдельные топики), dead man's switch на затишье.

Группы потребителей (DDIA 2-е изд., гл. 12): одна consumer group — load balancing (партиция — одному консьюмеру группы), разные группы — fan-out (каждая получает всё); acknowledgments + redelivery. Обычные брокеры (AMQP/RabbitMQ) — delete-after-delivery; log-based (Kafka) — append-only партиции, consumer offsets, retention и реплей.

CDC и компоновка (DDIA 2-е изд., гл. 12): CDC — репликационный лог БД → стрим (Debezium/Kafka Connect): БД не знает об этом, исторические данные — через initial snapshot + offset; log compaction — по ключу хранится только последнее значение (навсегда), а не окно по времени. CDC (факты изменений) ≠ event sourcing (намерения домена). Окна: tumbling/sliding/session; event time vs processing time (часы клиентов плывут — watermarks для закрытия окна).


9. Отказоустойчивость

Изоляция и деградация: - Timeout на каждый внешний вызов — без него один зависший dependency съедает потоки. - Retries: экспоненциальный backoff + jitter; только идемпотентные операции; ограничение числа попыток. - Circuit breaker: closed → (ошибки > порога) → open (фейлим сразу, дёшево) → half-open (пробный запрос). Защищает от каскадного падения и даёт зависимости время восстановиться. - Bulkhead: отдельные пулы соединений/потоков на dependency; падение одного не топит остальные. - Rate limiting: token bucket / leaky bucket / sliding window; на клиенте (борьба с абузой) и на сервере (self-protection); 429 + Retry-After; в распределёнке — Redis + Lua или лимитер на шлюзе. - Load shedding: при перегрузке отказываем «дешёвым» запросам, спасаем критичные; graceful degradation — отдаём кэш/неполные данные вместо ошибки.

Резервирование: N+1 на каждом ярусе; active–active (мульти-ДЦ, дороже, выше утилизация) vs active–passive (проще, тёплый простой). Health checks (liveness/readiness), автоматический failover с кворумом.

Арифметика надёжности: доступность цепочки = произведение; параллель = 1 − ∏(1−ai). «Три девятки» = 8.7 ч/год — уже требует резервирования всего пути.

Слабые места распределённой среды (DDIA 2-е изд., гл. 9): - Partial failure — норма: «работает или нет» больше нет; сеть асинхронна — потерянный запрос, очередь, упавшая нода, GC-пауза, потерянный ответ не отличить; таймаут — догадка; «нет ответа» ≠ «не выполнилось» → ретраи только для идемпотентного. - Часы: time-of-day (NTP: прыжки, drift) для wall-clock опасен (LWW теряет данные), интервалы — только monotonic; между машинами время несравнимо. - Process pauses / зомби-лидер: GC, VM suspend, swap — нода «засыпает» на секунды, не замечая истёкшего lease; защита — fencing token на каждом grant, хранилище отвергает старые (см. §7). - Кворум объявляет мёртвым — нода обязана подчиниться: узел знает только полученные сообщения; majority-кворум — единственный с гарантированным пересечением. - System models: реалистичная — partially synchronous + crash-recovery; safety (не нарушается никогда) vs liveness («eventually», при живом большинстве); Byzantine (враньё узлов) — вне охвата датацентров, от слабого вранья — checksums/валидация входа; gray failure (limping node) — хуже чистого отказа.


10. Эксплуатация (говорим на секции всегда)

  • Golden signals (Google SRE): latency (отдельно ошибки и успехи!), traffic, errors, saturation. Метрики каждого яруса: QPS, p50/p95/p99, error rate, очередь/лаг, утилизация (CPU, память, соединения).
  • SLI/SLO/SLA + error budget: SLO 99.9 % даёт бюджет 43 мин/мес; бюджет кончился — фризим фичи, чиним надёжность. На секции это маркер production-зрелости.
  • Релизы: rolling, canary (1 % → 10 % → 50 % → 100 % с авто-откатом по метрикам), blue-green (мгновенный откат переключением), feature flags (расходим деплой и релиз фичи).
  • Миграции без даунтайма — expand/contract: 1) расширяем схему (новая колонка/таблица, nullable, backfill в фоне), 2) двойная запись/чтение-сравнение, 3) переключаем чтение, 4) контракт: убираем старое. Каждый шаг — рабочий релиз. Antipattern: большие миграции ALTER на живых больших таблицах и dual writes без сверки.
  • Наблюдаемость: metrics (Prometheus) + logs (структурированные) + traces (OpenTelemetry, сквозной trace id). Алерты на симптомы (SLO burn), а не на причины.
  • Ёмкость: план на пики (реклама, праздники), headroom ≥ 2×, регулярный load-тест.
  • Хвостовая латентность и metastable failure (DDIA 2-е изд., гл. 2): считать p50/p95/p99, среднее врёт; tail amplification — один медленный вызов в fan-out-цепочке тянется в общую p99 (N бэкендов → почти каждая страница ловит хвост). Metastable failure: перегруз → таймауты → retry storm → система «застревает» в перегрузе даже после спада нагрузки; лечится exponential backoff + jitter, circuit breaker, load shedding, backpressure. Словарь: response time ≠ service time ≠ latency (очередь — главный источник задержки).

11. Мини-шпаргалка «что назвать на секции»

Тема Обязательная лексика
Оценка DAU → QPS avg/peak, storage с retention, bandwidth, 80/20 для кэша
Хранилище источник правды RDBMS, проекции под access pattern, критерии выбора класса
Масштаб кэш → реплики → шардирование; consistent hashing, virtual nodes, hot partition
Консистентность CAP/PACELC, quorum, лаг репликации, saga/outbox вместо 2PC, идемпотентность
Очереди Kafka, партиции, at-least-once + дедуп, DLQ, load leveling
Отказы timeout/backoff/jitter, circuit breaker, bulkhead, rate limit, graceful degradation
Эксплуатация golden signals, SLO/error budget, canary, expand-contract миграции

12. Карта DDIA (Клеппман, «Высоконагруженные приложения»)

⚠️ Карта ниже — по 1-му изданию (= русский перевод). Вышло 2-е издание (Kleppmann & Riccomini, 2025, 673 стр.): гл. 1 переписана, гл. 2 новая (НФТ + кейс лент), старые гл. 5–9 → 6–10, + логические часы и ID-генераторы, + durable execution/workflow engines в гл. 5. Соответствие глав и приоритеты 2-го изд. (6–10 ядро, 1–5 вторично, 11–13 опционально, 14 — этика) — materials/ddia-map.md §0; полный текст 2-го издания с выжимками — materials/ddia-2ed/ch-*.md (TASK-45.39).

Приоритеты: ЯДРО — читать обязательно, именно отсюда вопросы секции; вторично — к интервью после ядра; опционально — после секций / для общего развития.

Часть I. Основы данных (главы 1–4)

Глава Тема Приоритет Что брать на секцию
1 Надёжность, масштабируемость, поддерживаемость ЯДРО (быстро) Словарь: нагрузка → латентность p99 → эластичность; «сначала описать нагрузку, потом решать»
2 Модели данных и языки запросов Вторично Реляционная vs документная: локальность данных vs связи; когда NoSQL оправдан
3 Хранение и извлечение ЯДРО LSM vs B-tree — любимый вопрос: LSM — быстрый write, компактация, read amplification; B-tree — быстрый read, предсказуемость; плюс хеш-индексы, SSTable
4 Кодирование и эволюция Вторично Обратно-совместимые схемы (protobuf/Avro), зачем schema registry; старые и новые читатели

Часть II. Распределённые данные (главы 5–9) — сердце книги и секции

Глава Тема Приоритет Что брать на секцию
5 Репликация ЯДРО Sync/async, лаг, read-your-writes, failover, split brain, multi-leader конфликты
6 Партиционирование ЯДРО Range vs hash, hot skew/celebrity, ребалансировка, запросы по нескольким шардам
7 Транзакции ЯДРО Уровни изоляции, race conditions (dirty/phantom/lost update), serializable snapshot isolation
8 Проблемы распределённых систем ЯДРО Partial failure, неопределённость ответа (timeout — «не знаю»), часы и порядок событий, GC-паузы как «зомби»-процессы
9 Консистентность и консенсус ЯДРО (самая плотная) Linearizability, CAP/PACELC, кворумы, fencing-токены, RAFT/Paxos на уровне идей, зачем ZooKeeper/etcd

Часть III. Производные данные (главы 10–12)

Глава Тема Приоритет Что брать на секцию
10 Пакетная обработка Опционально MapReduce-идея, partition-parallel batch, когда batch вместо stream
11 Потоковая обработка Вторично Delivery semantics, exactly-once иллюзия, окна, переработка (replay) — прямо ложится на вопросы про Kafka
12 Будущее Опционально Общая картина: deriving state from event log

Порядок чтения к слотам (18.09 / 21.09 / 22.09)

  • До слота 18.09: гл. 1, 3, 5, 6 (+ детальный конспект URL shortener из статьи Яндекса). Это скелет: числа, хранение, репликация, партиции.
  • Между 18.09 и 21.09: гл. 7, 8, 9 — транзакции, отказы, консистентность/консенсус. Самое плотное ядро.
  • Перед 22.09: гл. 11 + повтор конспектов по ч. II; гл. 2, 4 — по остатку времени.
  • Гл. 10 и 12 — после секций, для собеседований уровнем выше.
  • После каждой главы — 5-минутный пересказ схемы вслух у доски (протокол тренировки — methodology.md).

Детальная проработка карты чтения и привязка к слотам — materials/ddia-map.md (TASK-45.10).


13. Инфографика System Design Blueprint (learn-system-design) — карта и дополнения

Источник: system-design-guide.jpeg из подборки learn-system-design («System Design Blueprint: The Ultimate Guide», TASK-45.12): весь путь запроса на одном листе — DNS → балансировщик → API-шлюз → бэкенды → кэш/очереди → БД → хранение медиа, плюс сквозные темы. Большая часть уже покрыта §3–§11; ниже — карта «блок → где в KB» и мини-блоки, которых не хватало. Полная послойная расшифровка — ✅ TASK-45.24 (materials/lsd-ultimate-guide-image.md), индекс всей подборки — materials/learn-system-design-index.md.

Блок инфографики Что на листе Где в KB
DNS resolver → root → TLD → authoritative, GeoDNS, кэш/TTL §13.1 (новое)
API-шлюз валидация, auth, rate limit, TLS, idempotency key, логи §9 + §13.7
Load Balancing алгоритмы + метрики выбора ноды §13.2 (новое)
Real-time транспорт WebSocket/SSE/polling/webhook/WebRTC/RTMP §13.3 (новое)
Бэкенды fan-out, конкурентность, распределённые локи, генерация ID §13.4–13.5 (новое), §7 (идемпотентность)
Cache стратегии записи, eviction, инвалидация §4 (уже есть)
Database классы хранилищ, шардирование, кворумы, Merkle §3, §5–§7 + §13.6 (гео)
Upload медиа чанки, signed URL, metadata-таблица, транскодинг classic-designs §4
Очереди/метрики лаг, in-transit, golden signals §8, §10
Платежи charge-сервис, внешний banking, идемпотентность §13.7 (новое), §7
Security OAuth/RBAC, шифрование, audit trail §13.7 (новое)

Послойная расшифровка листа и маппинг «блок → где в KB» — materials/lsd-ultimate-guide-image.md (TASK-45.24); ниже §13.1–13.7 — мини-блоки с формулировками, §13.8 — дополнения по итогам полной расшифровки.

13.1 DNS — одной фразой на секции

Цепочка: клиент → рекурсивный resolver (ISP) → root NS → TLD NS (.com) → authoritative NS → IP; каждый уровень кэшируется с TTL. GeoDNS отдаёт IP ближайшего региона — с этого начинается «мульти-ДЦ». Формулировка: «клиент резолвит домен, GeoDNS ведёт в ближайший регион, дальше весь трафик — в LB». Не углубляться.

13.2 Алгоритмы балансировщика

Round Robin (stateless-дефолт) • Weighted RR (ноды разной мощности) • Least Connections (долгие соединения: websocket/streaming) • Hash/IP-sticky (сессии, кэш-локальность; вместе с consistent hashing — ответ про масштабирование пула LB) • Random (говорим, что не выбираем). Обязательная фраза: «перед LB — health checks, нездоровую ноду выбрасываем из ротации».

13.3 Real-time транспорт — выбор по направленности и совместимости

Механизм Направление Когда выбирать
WebSocket дуплекс мессенджер, коллаб-редактор, live-обновления (см. classic-designs §3); LB — least connections/sticky, ping/pong keepalive
SSE сервер → клиент нотификации, лента: проще WS, поверх HTTP, авто-reconnect
Long polling сервер → клиент старые клиенты/прокси, где WS не проходит
Short polling клиент опрашивает простые статусы; называем цену: холостые запросы
Webhook сервер → сервер push событий наружу (интеграции)
WebRTC / RTMP P2P медиа / ingest звонки; приём видеопотока → дальше HLS/DASH + CDN

13.4 Распределённая генерация ID (URL shortener, лента, заказы)

  • UUID v4: без координации, но 128 бит и нет порядка → плохие индексы и шардирование по времени.
  • Auto-increment × N нод (чёт/нечет или шаг N): просто, но плохо масштабируется и предсказуемо.
  • Snowflake (64 бита: 41 время + 10 нод + 12 секвенция): хронология «бесплатно», дружелюбно индексам и шардированию; семейство — Baidu UID, Sonyflake. Дефолтный ответ для ID сущностей в масштабе.
  • Централизованный генератор (ticket service): просто, но точка отказа; лечится кэшированием диапазонов.

13.5 Распределённые локи и координация

Redis (Redlock) — быстро, компромисс по надёжности; ZooKeeper/etcd/Chubby — кворумные, надёжнее и дороже. Применение: leader election, single-flight пересчёт кэша (stampede, §4), дедуп критичных операций. Оптимистичная блокировка (версия/CAS) — дефолт; пессимистичная — короткие критические секции. При локах проговаривать fencing token и «лок ≠ транзакция» (§5, §7).

13.6 Гео-шардирование и пространственные данные

Гео-шарды (по регионам): локальность данных, latency, GDPR-границы; цена — кросс-региональная консистентность (§7). Запросы «ближайшие N объектов» — geohash/QuadTree + R-деревья; на задачах Uber/карт-класса это ключевой блок выбора БД.

13.7 Платежи и security — сквозные темы секции

Платежи: charge-сервис с idempotency key (повтор запроса ≠ двойное списание), статус-машина платежа, внешний banking-провайдер = медленная зависимость → таймаут/ретрай/очередь + сверка (reconciliation). Ошибки домена (карта просрочена, недостаточно средств, провайдер недоступен) — обрабатываемые ветки, не 500.

Security-чеклист (назвать всегда): OAuth 2.0/JWT, RBAC, TLS in-transit + шифрование at-rest, audit trail, input validation, signed URLs на приватные объекты (медиа), rate limiting (§9).

13.8 Дополнения по полной расшифровке листа (TASK-45.24)

Мини-факты, которых не было в §1–13.7; формулировки — как на листе, добавлена расшифровка «зачем»:

  • API-шлюз, ответная сторона (просим не забыть): gzip/deflate, Request ID (трассировка), pagination, expiry-заголовки (кэш-контроль), mime, cookie, коды/тексты ошибок. Запросная сторона: валидация, auth (OAuth 2.0), rate limit, whitelist/blacklist, flow control, TLS termination, дедуп по idempotency key, metering/диспетчеризация.
  • CDN — push hot / pull rare / hybrid: горячее проталкиваем на край заранее, редкое тянем по промаху, обычно гибрид; метрики края — cache miss outs, active/total resources.
  • Генерация ID — offline generations: генератор выдаёт нодам диапазоны заранее — сервис не стоит на критическом пути записи (дополняет snowflake, §13.4).
  • Конкурентность — serially batch: между «строго последовательно» и пессимистичными локами есть промежуточная ступень — последовательные батчи (дополняет §13.5).
  • Upload медиа — metadata-таблица чанков: key | checksum | timestamp в in-memory key storage, целостность чанка проверяем по checksum при каждом чтении, таблицу синхронизируем централизованно; событие «новый чанк» фан-аутится в MQ → воркеры (транскодинг: compute time, failed count, encoded/compression ratio, storage consumed).
  • Рекомендации — фильтры: content-based (по признакам контента) и collaborative (по поведению похожих пользователей) — назвать обе, если в системе есть лента/каталог.
  • Метки наблюдаемости листа развешаны по компонентам (шлюз: 4XX/5XX/2XX, response time; очередь: count, consumption rate, in-transit waiting-for-ack, queue limit; БД: query time, throughput, active connections) — так и подавать на этапе 6: метрики привязаны к узлу, а не списком в конце.

14. Строительные блоки из Alex Xu Vol. 1 (TASK-45.38)

Источник: «System Design. Подготовка к сложному интервью» (рус. Vol. 1), полный текст глав — materials/alex-xu-vol1/ch-*.md, карта переноса — materials/alex-xu-vol1/_map.md. Здесь — паттерны, которых не было в §1–13: алгоритмы rate limiting (гл. 4), краулер (гл. 9), уведомления (гл. 10), автозаполнение (гл. 13), блочное хранилище (гл. 15). Snowflake — §13.4; транспорт (polling/WS) — §13.3; consistent hashing — §6; Dynamo-механики — §5/§7.

14.1 Алгоритмы rate limiting (гл. 4)

Алгоритм Память Плюсы Минусы
Token bucket O(размер корзины) допускает burst, гибкая настройка скорости два параметра
Leaking bucket O(размер очереди) ровный выходной поток burst режется; два параметра
Fixed window counter O(счетчиков) просто burst ×2 на границе окон
Sliding window log O(запросов) точно дорого по памяти
Sliding window counter O(счетчиков) компромисс (гибрид двух) оценка, не точный лимит

Распределённый лимитер: Redis + Lua (атомарный check-and-set, иначе race между воркерами); multi-DC → лимитер на edge (GeoDNS → ближайший узел), консистентность между узлами — eventual, учесть в бенчмарках. Эксплуатация: заголовки X-RateLimit-*, код 429, мониторинг доли заблокированных (высокая = абьюз или неверный лимит). Клиентские лимитеры (в приложении) — для экономии вызовов, серверные — источник правды.

14.2 Поисковый краулер (гл. 9)

Характеристики: масштабируемость, устойчивость, вежливость (не DDoS чужие серверы), расширяемость (плагины). Компоненты: seed URL → frontier (очередь на загрузку) → загрузчик HTML (+DNS-кэш — узкое место, 10–200 мс) → дедуп контента по хешам (29 % страниц — дубликаты; фильтр Блума для «видели URL?») → извлекатель ссылок → фильтр URL → хранилище URL.

Frontier — сердце: лицевые очереди (приоритет: PageRank, популярность, частота обновления) + тыльные очереди (вежливость: домен → таблица связывания → своя FIFO → выделенный рабочий поток с задержкой). BFS «в лоб» бьёт по одному домену и не учитывает ценность страниц. Ещё: robots.txt (кэшировать, уважать Disallow), гео-распределение, timeout на медленные серверы, состояние в хранилище (рестарт после сбоя), спайдертрапы (лимит длины URL, чёрные списки), server-side рендеринг JS-страниц.

14.3 Система уведомлений (гл. 10)

Каналы: APNs (iOS) / FCM (Android) / Twilio-Nexmo (SMS) / SendGrid-Mailchimp (email) — провайдеры сторонние, расширяемость обязательна (FCM недоступен в Китае → JPush). Паттерн: сервисы → серверы уведомлений (API, валидация, шаблоны) → очередь на каждый канал (отказ одного провайдера не роняет остальные) → воркеры → провайдеры. Надёжность: уведомление не теряется — журнал уведомлений в БД + retry из очереди; доставок может быть больше одной (at-least-once) → дедуп по ID события; rate limiting на получателя (иначе отписки); мониторинг длины очередей (растёт → добавляем воркеров); аналитика (открытия/клики/отписки), настройки opt-in. Это же — типовой ответ «как связать сервис с внешним миром».

14.4 Автозаполнение поиска / typeahead (гл. 13)

Требования: <100 мс, топ-5 по частоте, совпадение с начала строки. Прикидка: 10M DAU × 10 запросов × 20 символов ≈ 24k QPS (пик 48k) — запрос на каждый введённый символ. Ядро — trie (префиксное дерево) с частотами; наивный алгоритм O(p) + O(c) + O(c log c). Две оптимизации до O(1): (1) ограничить максимальную длину префикса; (2) топ-k популярных запросов прямо в каждом узле — память платим, латентность забираем. Данные строятся offline: логи → агрегаторы (частоты за период; для Google хватает недели, для Twitter — реальное время) → воркеры пересобирают trie → БД (документная/снимок или KV «префикс → список») → кэш trie в памяти → API. Шардирование — по диапазонам префиксов. Вариант вопроса «top-k самых частых запросов» — это он.

14.5 Блочное хранилище и синхронизация файлов (гл. 15, Google Drive/Dropbox)

Файл → блоки (~4 МБ) → сжатие по типу → шифрование → S3; метаданные (блоки + хеши, версии) — в реляционной БД (ACID): нужна строгая согласованность между кэшем и БД, инвалидация кэша при записи. Экономия трафика: дельта-синхронизация (передаём только изменённые блоки), дедупликация (одинаковый хеш = один блок), лимит версий, холодное хранилище (Glacier) для неактивного. Конфликты одновременного редактирования: побеждает первая обработанная версия, второму клиенту — обе версии на слияние/выбор. Уведомления об изменениях — long polling (одностороннее, редкие события — WebSocket избыточен); оффлайн-клиент — очередь автономной архивации. Таблицы: user, device (push_id), namespace, file, file_version (immutable), block.


15. Кодирование, эволюция схем и durable execution (DDIA 2-е изд., гл. 5)

Мини-блок под вопросы «как менять формат данных без даунтайма» и «какие alternatives сагам есть":

  • Совместимость: backward (новый код читает старые данные) и forward (старый код — новые): обязательны при поэтапных релизах; потоки данных — через БД («сообщение будущему себе»), через сервисы (REST/RPC), через сообщения (брокер).
  • Форматы: JSON/CSV — читаемые, объёмные, двусмысленность чисел; Protobuf/Avro — компактные, эволюция через схему (protobuf — теги полей, новые поля с новыми номерами; Avro — writer/reader schema, идеален для CDC), проверка совместимости до деплоя (schema registry).
  • RPC не может спрятать сеть: частичные отказы, таймауты, семантика ретраев — те же проблемы, что у распределённых систем (§9).
  • Durable execution / workflow engines (Temporal, Restate) — новое во 2-м изд.: оркестратор персистит состояние workflow в журнал, задачи идемпотентны и ретраятся движком; практическая альтернатива сагам и распределённым транзакциям для долгих бизнес-процессов (заказы, платежи, онбординг). На секции: «сага — дефолт; для долгих многошаговых процессов смотрю в сторону workflow-движков с durable execution».

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

  • ../interview-materials.md — общая карта материалов по вакансии
  • materials/ — конспекты источников (статья Яндекса, System Design Primer, habr 516604, видео Gabbard, DDIA) + learn-system-design-index.md (индекс подборки, TASK-45.12) + lsd-ultimate-guide-image.md (расшифровка инфографики, TASK-45.24) + alex-xu-vol1/ (книга Алекса Сюя, полный текст глав + выжимки + карта переноса _map.md, TASK-45.38) + ddia-2ed/ (DDIA 2-е издание, полный текст глав + выжимки + карта переноса _map.md, TASK-45.39)
  • methodology.md — фреймворк ответа, тайминг, план тренировки (TASK-45.2)
  • classic-designs.md — эталонные разборы сервисов (TASK-45.3)
  • ../algo-exam/complexity-cheat-sheet.md — методичка по алгоритмам (этап 1, закрыт)