Отказоустойчивость и масштабируемость
Паттерны отказоустойчивости и масштаба, которые аналитик закладывает в НФТ — балансировка нагрузки, circuit breaker, повторы с backoff, bulkhead, плавная деградация, ограничение частоты, кэширование и миграция без простоя.
14 вопросов
JuniorТеорияОчень частоЧто такое отказоустойчивость и зачем распределённой системе отдельные паттерны устойчивости?
Что такое отказоустойчивость и зачем распределённой системе отдельные паттерны устойчивости?
Отказоустойчивость — это способность продолжать выдавать корректные результаты, пока часть системы отказала. Паттерны устойчивости — timeout, retry, circuit breaker, bulkhead, fallback — локализуют отдельный отказ, чтобы деградировала одна функция, а не каскадно упала вся распределённая система.
Типичные ошибки
- ✗Приравнивать отказоустойчивость к покупке более мощного железа вместо локализации отказов
- ✗Считать распределённую систему устойчивой по умолчанию без явных паттернов
- ✗Думать, что устойчивость предотвращает отказы, а не ограничивает радиус поражения
Уточняющие вопросы
- →К какому паттерну устойчивости вы обратитесь первым и почему?
- →Чем timeout отличается от retry по тому, что он защищает?
MiddleТеорияОчень частоЧто такое балансировка нагрузки, какие есть алгоритмы (round-robin, least-connections, hash) и что такое sticky session?
Что такое балансировка нагрузки, какие есть алгоритмы (round-robin, least-connections, hash) и что такое sticky session?
Балансировщик распределяет запросы по одинаковым инстансам, чтобы никто не перегружался, а мёртвые пропускались. Round-robin чередует поровну, least-connections берёт наименее занятого, hash маршрутизирует по ключу клиента. Sticky session закрепляет клиента за инстансом — удобно для in-memory состояния, но плохо для баланса и failover.
Типичные ошибки
- ✗Путать round-robin (чередование) с least-connections (по нагрузке)
- ✗Думать, что sticky session улучшает баланс, а не вредит ему
- ✗Считать, что балансировщик реплицирует каждый запрос на все инстансы
Уточняющие вопросы
- →Почему sticky session усложняет масштабирование и failover?
- →Когда hash-маршрутизация предпочтительнее round-robin?
MiddleТеорияОчень частоВ чём разница между масштабированием сервиса вширь и ввысь, и что делает сервис горизонтально масштабируемым?
В чём разница между масштабированием сервиса вширь и ввысь, и что делает сервис горизонтально масштабируемым?
Масштабирование ввысь (вертикальное) — машина побольше для одного инстанса: просто, но с потолком и единой точкой отказа. Масштабирование вширь (горизонтальное) — больше инстансов за балансировщиком, ёмкость растёт почти линейно и переживает потерю инстанса. Сервис масштабируется вширь, только если он stateless — сессия и данные в общем хранилище, а не в памяти инстанса.
Типичные ошибки
- ✗Менять местами определения масштабирования ввысь и вширь
- ✗Называть горизонтально масштабируемым сервис с сессией в памяти инстанса
- ✗Игнорировать требование общего хранилища, делающего инстансы взаимозаменяемыми
Уточняющие вопросы
- →Почему stateful-сервис трудно масштабировать вширь даже за балансировщиком?
- →Когда масштабирование ввысь остаётся прагматичным первым выбором?
JuniorТеорияЧастоГде реализуется кэширование — клиент, CDN, шлюз или сервер — и что кэширует каждый слой?
Где реализуется кэширование — клиент, CDN, шлюз или сервер — и что кэширует каждый слой?
Кэш стоит на нескольких слоях. Клиент кэширует ресурсы по Cache-Control; CDN кэширует контент на edge-узлах рядом с пользователем; API-шлюз кэширует частые ответы, разгружая бэкенд; сервер кэширует результаты запросов в хранилище вроде Redis. Слой ближе к пользователю снижает задержку; ближе к данным — пересчёт.
Типичные ошибки
- ✗Думать, что кэширование происходит в одном месте, а не на нескольких слоях
- ✗Позволять CDN кэшировать приватные ответы пользователя на общем edge
- ✗Игнорировать
Cache-Controlкак контракт, управляющий каждым слоем
Уточняющие вопросы
- →На каком слое вы бы кэшировали персональный дашборд и почему?
- →Как
Cache-Controlсообщает каждому слою, что он может хранить?
MiddleДизайнЧастоЧёрная пятница утраивает ожидаемую нагрузку на интернет-магазин. Оформление заказа обязано работать, даже если часть функций пострадает. Какие рычаги вы задействуете и в каком порядке, чтобы защитить путь оформления? Охватите преднастройку ёмкости, сброс необязательной нагрузки (рекомендации, аналитика, подсказки поиска), защиту общих зависимостей (платежи, склад) через timeout и circuit breaker, и что вы намеренно отключаете первым, чтобы денежный путь работал, пока менее ценные функции деградируют.
Чёрная пятница утраивает ожидаемую нагрузку на интернет-магазин. Оформление заказа обязано работать, даже если часть функций пострадает. Какие рычаги вы задействуете и в каком порядке, чтобы защитить путь оформления? Охватите преднастройку ёмкости, сброс необязательной нагрузки (рекомендации, аналитика, подсказки поиска), защиту общих зависимостей (платежи, склад) через timeout и circuit breaker, и что вы намеренно отключаете первым, чтобы денежный путь работал, пока менее ценные функции деградируют.
Сначала преднастройте ёмкость заранее — добавьте инстансы и реплики чтения, пока нагрузка низкая. Второе — защитите денежный путь: оберните платежи и склад в timeout и circuit breaker, чтобы медленная зависимость падала быстро, а не тормозила оформление. Третье — сбрасывайте необязательную нагрузку по ценности: рекомендации, подсказки, тяжёлую аналитику первыми.
Типичные ошибки
- ✗Масштабироваться реактивно во время всплеска вместо преднастройки заранее
- ✗Сбрасывать путь оформления, а не необязательные функции первыми
- ✗Опускать timeout на платежах и складе, из-за чего медленный вызов тормозит оформление
Уточняющие вопросы
- →Как вы определяете порядок отключения функций?
- →Почему преднастройка лучше, чем автоскейлинг во время всплеска?
MiddleТеорияЧастоЧто такое circuit breaker, каковы его три состояния и от чего он защищает?
Что такое circuit breaker, каковы его три состояния и от чего он защищает?
Circuit breaker оборачивает вызовы зависимости и перестаёт их слать, когда она выглядит нездоровой. Closed считает отказы; Open роняет вызовы быстро за порогом доли отказов; Half-open после паузы пропускает несколько пробных вызовов, закрываясь при успехе или снова открываясь при отказе. Он бережёт ресурсы на мёртвом сервисе и гасит каскадную перегрузку.
Типичные ошибки
- ✗Называть состояния скоростью повторов или статусом кэша вместо Closed/Open/Half-open
- ✗Думать, что breaker повторяет сильнее, а не падает быстро в Open
- ✗Путать circuit breaker с балансировщиком по репликам
Уточняющие вопросы
- →Как Half-open избегает удара по только что восстановившемуся сервису?
- →Как circuit breaker дополняет retry с backoff?
MiddleДизайнЧастоСтраница товара в e-commerce показывает персональные рекомендации из отдельного сервиса рекомендаций. Этот сервис необязателен для покупки, но иногда медленный или недоступен. Спроектируйте плавную деградацию для этой зависимости: укажите, что страница обязана делать при отказе рекомендаций, опишите fallback-ответ, который вы вернёте, и объясните, как fallback срабатывает (timeout, circuit breaker), чтобы медленный сервис рекомендаций никогда не блокировал отрисовку страницы товара или покупку.
Страница товара в e-commerce показывает персональные рекомендации из отдельного сервиса рекомендаций. Этот сервис необязателен для покупки, но иногда медленный или недоступен. Спроектируйте плавную деградацию для этой зависимости: укажите, что страница обязана делать при отказе рекомендаций, опишите fallback-ответ, который вы вернёте, и объясните, как fallback срабатывает (timeout, circuit breaker), чтобы медленный сервис рекомендаций никогда не блокировал отрисовку страницы товара или покупку.
Основное — цену, описание, добавление в корзину — страница рисует независимо от рекомендаций. Вызов рекомендаций оберните в короткий timeout и circuit breaker; при срабатывании верните fallback вместо ошибки: кэш, бестселлеры или скрытый блок. Оформление работает, пока деградирует один необязательный виджет, незаметно покупателю.
Типичные ошибки
- ✗Позволять необязательной зависимости блокировать основную страницу или оформление
- ✗Отдавать страницу ошибки вместо тихого fallback для виджета
- ✗Повторять медленную зависимость по месту вместо быстрого timeout
Уточняющие вопросы
- →Как не дать кэшированному fallback-списку сильно устареть?
- →Почему при отказе показать бестселлеры, а не пустую область?
MiddleДизайнЧастоПартнёрская интеграция долбит ваш публичный API 10 000 запросов в секунду — сильно выше согласованной квоты — и вытесняет других клиентов. Спроектируйте политику rate limiting: выберите, где применяется лимит, какой алгоритм ограничивает частоту, как ограничить именно этого партнёра, и укажите точно, что API возвращает вызывающему при превышении — код статуса, заголовки и тело — чтобы легитимный трафик продолжал идти, пока злоупотребляющий партнёр троттлится.
Партнёрская интеграция долбит ваш публичный API 10 000 запросов в секунду — сильно выше согласованной квоты — и вытесняет других клиентов. Спроектируйте политику rate limiting: выберите, где применяется лимит, какой алгоритм ограничивает частоту, как ограничить именно этого партнёра, и укажите точно, что API возвращает вызывающему при превышении — код статуса, заголовки и тело — чтобы легитимный трафик продолжал идти, пока злоупотребляющий партнёр троттлится.
Применяйте лимит на API-шлюзе, до бэкенда, на ключ партнёра, а не глобально. Используйте token bucket или sliding window, ограничивая запросы в секунду и допуская короткие всплески. При превышении верните HTTP 429 Too Many Requests с заголовком Retry-After — не тихий сброс и не 500. У каждого клиента своя корзина, поэтому троттлинг партнёра щадит остальных.
Типичные ошибки
- ✗Наложить один глобальный лимит вместо области на партнёра (на ключ)
- ✗Возвращать
200или тихий сброс вместо429сRetry-After - ✗Применять лимит в бэкенде после сделанной работы, а не на границе
Уточняющие вопросы
- →Почему token bucket допускает всплески, которые fixed window отвергает?
- →Как
Retry-Afterпомогает добросовестному клиенту отступить?
MiddleТеорияЧастоЧто такое retry с экспоненциальным backoff и jitter, и когда повтор опасен?
Что такое retry с экспоненциальным backoff и jitter, и когда повтор опасен?
При временном сбое клиент повторяет, но ждёт дольше на каждой попытке — экспоненциальный backoff (1с, 2с, 4с…), чтобы не долбить перегруженный сервис. Jitter рандомизирует каждую задержку, чтобы упавшие вместе клиенты не повторяли синхронно. Повтор опасен, когда операция не идемпотентна (повторный платёж спишет дважды) или когда усиливает перегрузку, что вызвали повторы.
Типичные ошибки
- ✗Разворачивать backoff во всё меньшие задержки вместо всё больших
- ✗Опускать jitter, позволяя синхронным клиентам вызвать thundering herd
- ✗Повторять неидемпотентную операцию и дублировать её эффект
Уточняющие вопросы
- →Как ключ идемпотентности делает повтор безопасным для платежа?
- →Почему повторы без лимита углубляют сбой, который начали не они?
SeniorДизайнЧастоПлатёжная платформа обязана пережить потерю целого дата-центра. Как аналитик, вы отвечаете за нефункциональные требования. Определите две цели восстановления — RTO (recovery time objective) и RPO (recovery point objective), укажите реалистичные значения для платёжной системы и во что каждое обходится, и назовите паттерны устойчивости и топологию, которые вы заложите — репликацию, мульти-регион, failover, бэкапы — чтобы бизнес утвердил, точно зная, сколько простоя и потери данных допустимо.
Платёжная платформа обязана пережить потерю целого дата-центра. Как аналитик, вы отвечаете за нефункциональные требования. Определите две цели восстановления — RTO (recovery time objective) и RPO (recovery point objective), укажите реалистичные значения для платёжной системы и во что каждое обходится, и назовите паттерны устойчивости и топологию, которые вы заложите — репликацию, мульти-регион, failover, бэкапы — чтобы бизнес утвердил, точно зная, сколько простоя и потери данных допустимо.
RTO — сколько вы можете быть недоступны после отказа; RPO — сколько свежих данных допустимо потерять. Для платежей обе жёсткие — RTO в минуты, RPO около нуля — ведь потерянные транзакции это деньги и доверие. RPO около нуля требует синхронной репликации во второй регион; низкий RTO — тёплого/горячего резерва с авто-failover, а не ручного восстановления из ночных бэкапов.
Типичные ошибки
- ✗Менять местами определения RTO (простой) и RPO (потеря данных)
- ✗Принимать ночные бэкапы и ручное восстановление для системы с RPO около нуля
- ✗Оставаться в одном регионе, полагая, что одна машина не откажет
Уточняющие вопросы
- →Почему RPO около нуля вынуждает синхронную, а не асинхронную репликацию?
- →Как горячий резерв снижает RTO по сравнению с восстановлением из бэкапов?
MiddleТеорияИногдаЧто такое паттерн bulkhead и что именно он изолирует?
Что такое паттерн bulkhead и что именно он изолирует?
Названный по водонепроницаемым отсекам корабля, паттерн bulkhead разделяет общий ресурс, чтобы один перегруженный потребитель не осушил его для всех. Каждая зависимость получает свой пул — отдельные пулы потоков и соединений или очереди — с лимитами, поэтому зависший сервис насыщает лишь свой пул, а другие сохраняют ёмкость. Он изолирует конкуренцию за ресурс, а не сам отказ.
Типичные ошибки
- ✗Думать, что bulkhead делит один пул, а не разбивает его по зависимостям
- ✗Путать bulkhead (изоляция ресурсов) со скрытием ошибки от пользователя
- ✗Считать, что он изолирует сам отказ, а не конкуренцию за ресурс
Уточняющие вопросы
- →Как отдельный пул потоков на зависимость останавливает распространение зависания?
- →Как bulkhead и circuit breaker дополняют друг друга?
SeniorДизайнИногдаДля корзины вы решаете предпочесть доступность строгой консистентности (AP вместо CP): корзина обязана всегда принимать добавление и удаление, даже при разделении, а реплики могут кратко расходиться. Объясните конкретно, что пользователь может из-за этого наблюдать, почему такой компромисс приемлем именно для корзины, и где в процессе покупки вы обязаны вернуться к строгой консистентности, чтобы AP-корзина не нанесла реального вреда.
Для корзины вы решаете предпочесть доступность строгой консистентности (AP вместо CP): корзина обязана всегда принимать добавление и удаление, даже при разделении, а реплики могут кратко расходиться. Объясните конкретно, что пользователь может из-за этого наблюдать, почему такой компромисс приемлем именно для корзины, и где в процессе покупки вы обязаны вернуться к строгой консистентности, чтобы AP-корзина не нанесла реального вреда.
Выбор AP держит корзину доступной для записи при разделении, поэтому реплики могут расходиться — удалённый товар может кратко вернуться или счётчик устареть, пока они не согласуются. Это нормально: корзина — приватный черновик, который легко изменить, а блокировка добавления теряет продажи. На оформлении вы переходите к CP — цена, остаток и платёж строго консистентны, — чтобы не купить отсутствующее или по устаревшей цене.
Типичные ошибки
- ✗Думать, что AP гарантирует идентичные реплики, а не терпит расхождение
- ✗Держать расслабленную AP-модель через платёж и склад
- ✗Считать, что AP-корзина делает корзину только для чтения при разделении
Уточняющие вопросы
- →Как вы согласуете две разошедшиеся реплики корзины после разделения?
- →Почему перепродажа недопустима на оформлении, а устаревший счётчик в корзине — нормально?
SeniorДизайнИногдаСтейкхолдер просит спроектировать систему заказов, одновременно консистентную, доступную и устойчивую к разделению — все три гарантии CAP сразу — между двумя дата-центрами. Во время сетевого разделения запросы на запись всё ещё приходят с обеих сторон. Объясните, где этот идеал на самом деле ломается, какой одной гарантией придётся пожертвовать при разделении и почему нельзя сохранить все три, и как вы обоснуете выбор CP против AP для оформления заказа этому стейкхолдеру на языке бизнеса.
Стейкхолдер просит спроектировать систему заказов, одновременно консистентную, доступную и устойчивую к разделению — все три гарантии CAP сразу — между двумя дата-центрами. Во время сетевого разделения запросы на запись всё ещё приходят с обеих сторон. Объясните, где этот идеал на самом деле ломается, какой одной гарантией придётся пожертвовать при разделении и почему нельзя сохранить все три, и как вы обоснуете выбор CP против AP для оформления заказа этому стейкхолдеру на языке бизнеса.
CAP говорит, что разделение вынуждает выбор между консистентностью и доступностью; при здоровой сети есть обе, поэтому все три держатся только без разделения. Когда оба дата-центра пишут, но не синхронизируются, они либо отвергают записи (CP), либо принимают расходящиеся (AP). Для оформления заказа я выбираю CP — отклонять или ставить в очередь записи меньшинства, — ведь конфликтный заказ дороже краткой недоступности с повтором.
Типичные ошибки
- ✗Утверждать, что современные базы дают все три гарантии одновременно
- ✗Считать выбор C против A постоянным, а не только на время разделения
- ✗Отказываться от устойчивости к разделению вместо распределения вовсе
Уточняющие вопросы
- →Как AP-система заказов согласует конфликтующие записи потом?
- →Какие операции заказа могут быть AP, пока оформление остаётся CP?
SeniorДизайнИногдаНужно изменить схему таблицы, в которую пишут около 1 000 раз в секунду — скажем, переименовать колонку или разбить её надвое — без простоя и без потери записей. Одиночный блокирующий ALTER TABLE застопорит всех писателей. Опишите поэтапный подход: как сосуществуют новая и старая схемы, как приложение пишет во время перехода, как заполняются существующие строки, и как вы переключаетесь и убираете лишнее, чтобы ни один писатель не блокировался.
Нужно изменить схему таблицы, в которую пишут около 1 000 раз в секунду — скажем, переименовать колонку или разбить её надвое — без простоя и без потери записей. Одиночный блокирующий ALTER TABLE застопорит всех писателей. Опишите поэтапный подход: как сосуществуют новая и старая схемы, как приложение пишет во время перехода, как заполняются существующие строки, и как вы переключаетесь и убираете лишнее, чтобы ни один писатель не блокировался.
Используйте expand-migrate-contract, а не один блокирующий ALTER. Expand: добавьте колонку аддитивно, чтобы старое и новое сосуществовали. Migrate: пишите сразу в обе, затем заполните строки малыми пачками, чтобы не держать долгий лок. Когда новое заполнено и чтение переключено, contract: прекратите писать старую колонку и удалите позже. Глобальный лок не берётся, писатели не блокируются.
Типичные ошибки
- ✗Запускать один блокирующий
ALTERвместо аддитивного поэтапного изменения - ✗Удалять старую колонку в том же релизе, а не в позднем
- ✗Заполнять всю таблицу одним оператором вместо малых пачек
Уточняющие вопросы
- →Почему удаление старой колонки должно ждать позднего релиза?
- →Как dual-write держит две колонки согласованными во время миграции?