![](https://habrastorage.org/getpro/habr/upload_files/3ea/a73/8be/3eaa738be83ed92ec104462e7e3add3f.jpg)
Привет, меня зовут Евгений. Я разрабатываю инфраструктуру поиска Яндекс.Маркета. Хочу рассказать, как graceful degradation помогает нам обрабатывать больше запросов, чем физически могут выдержать наши сервера, и что происходит с поиском в Маркете, если один из дата-центров отключается.
Проблема
Все критичные сервисы в Яндексе должны переживать отключение одного из дата-центров (ДЦ), поэтому обычно они размещаются минимум в трёх ДЦ.
![Обычное состояние Обычное состояние](https://habrastorage.org/getpro/habr/upload_files/a17/d9a/9c5/a17d9a9c589e1d610e6fb53dbd7b32ab.jpg)
Но когда все ДЦ работают, каждый из них оказывается задействован лишь на 60%. Ещё 10% резервируются для экспериментов и непредвиденного роста, а оставшиеся 30% используются только в случае, если один из ДЦ отключается, и нужно перераспределить запросы.
![ДЦ 2 отключён ДЦ 2 отключён](https://habrastorage.org/getpro/habr/upload_files/6a7/a89/c26/6a7a89c26397b05b283ea5242f701772.jpg)
Если сервис работает на сотнях серверов, 30% — это огромное количество железа. А поскольку отключаются ДЦ очень редко, резервные мощности почти всегда простаивают.
Нам хотелось бы меньше греть воздух, ведь если сократить количество железа в каждом ДЦ хотя бы на 10%, речь уже шла бы о внушительных суммах. При этом, если просто вытащить часть железа, а затем при пиковой нагрузке какой-то из ДЦ отключится, оставшиеся мощности могут не справиться. Резко вырастет время ответа пользователю, а некоторые запросы будут вообще оставаться без ответа.
![ДЦ 1 и ДЦ 3 не справляются с нагрузкой ДЦ 1 и ДЦ 3 не справляются с нагрузкой](https://habrastorage.org/getpro/habr/upload_files/160/491/77d/16049177d1d57af6435bda6f9d0f0f52.jpg)
Применяем graceful degradation
Graceful degradation или изящная деградация — это подход к построению систем, при котором система продолжает выполнять свою основную функцию, даже если отдельные её элементы вышли из строя.
Мой коллега из Яндекс.Такси уже рассказывал про graceful degradation в случае, когда ломается часть сервиса. Мы же применили этот подход на бэкендах для ситуации, когда у нас отключается значительное количество серверов.
Нашей целью было сделать так, чтобы сервис продолжал работать полностью, но нагрузка на него уменьшалась бы, а пользователи при этом не замечали бы разницы. Мы хотели научить наш поисковый сервис понижать среднюю нагрузку от одного запроса, что позволило бы обрабатывать большее количество запросов на меньшем количестве железа.
Чтобы запустить graceful degradation, нам надо было решить две задачи:
Разработать механизм уменьшения нагрузки.
Сделать автоматизацию включения механизма.
Механизм уменьшения нагрузки
Уменьшить нагрузку можно по-разному. Один из вариантов — отвечать ошибкой на некоторые запросы. Но тогда надо понять, на что можно так отвечать, а на что нет. То есть нужен какой-то параметр «важность», которого мы не знаем. Если спросить у наших клиентов, что важно, они ответят, что важны все запросы, и ничего нельзя отбрасывать. Поэтому этот путь нам не подошёл.
Другой вариант — использовать облегчённый алгоритм ранжирования. Минус в том, что это долго и сложно — новый алгоритм надо обучить и потом постоянно переобучать. Кроме того, состояние нашей системы может быть только «вкл.» или «выкл.», а нам хотелось бы иметь механизм плавного уменьшения нагрузки.
Таким образом, мы выбрали третий вариант — снижать качество. Основное преимущество этого подхода — возможность уменьшать нагрузку на 5%, 10% и так далее.
Допустим, вы ищете себе новый телефон и заходите в категорию «Смартфоны» на Яндекс.Маркете. Я уже подробно описывал, как работает наш поиск: запрос отправляется во фронтенд, надо показать пользователю 48 лучших предложений из категории с телефонами для конкретного региона. Фронтенд делает свой запрос в поисковый бэкенд.
Бэкенд получает запрос и раcпределяет его на 8 серверов с шардами. В шардах хранятся предложения от магазинов.
![Общая схема обработки поискового запроса Общая схема обработки поискового запроса](https://habrastorage.org/getpro/habr/upload_files/6a4/972/abb/6a4972abb902f63a7f8f2b5aac455496.jpg)
На каждом шарде поиск проходит несколько стадий. На стадии фильтрации ищется примерно 50 000 предложений, это число зависит от категории. На этапе ранжирования для каждого предложения вычисляется релевантность, учитывается цена, рейтинг товара, рейтинг магазина и ещё более 2000 факторов. ML по факторам вычисляет вес каждого предложения. Затем берётся только 48 лучших. Meta Search получает эти 48 предложений с каждого шарда, то есть всего 48*8=384 предложения. Предложения снова ранжируются, опять берётся 48 лучших. Последние 48 уже показываются пользователю. То есть чтобы показать нашим пользователям 48 телефонов, мы обрабатываем 400 000 предложений.
![Количество обрабатываемых документов без graceful degradation Количество обрабатываемых документов без graceful degradation](https://habrastorage.org/getpro/habr/upload_files/ac6/f53/20a/ac6f5320a0fb21532b8ee0c8ab40d4eb.jpg)
В случае с graceful degradation, когда надо уменьшить нагрузку, мы можем скомандовать: теперь обрабатывай 95% документов, а теперь 90% или 80%. Если обрабатывать 95%, то есть 400 000*0.95=380 000 документов, то из них всё равно выбираются 48 лучших предложений для выдачи. И в среднем только 2 предложения будут отличаться от изначальной выдачи без снижения качества. При таком маленьком изменении большинство пользователей даже не заметят разницы.
![Количество обрабатываемых документов при включении механизма уменьшения нагрузки Количество обрабатываемых документов при включении механизма уменьшения нагрузки](https://habrastorage.org/getpro/habr/upload_files/7a2/315/1e5/7a23151e54495debd69b3cfd28c91f24.jpg)
Автоматизация включения механизма
Автоматизация работает за счёт постоянного мониторинга загрузки CPU. Если нагрузка становится выше 90%, автоматика начинает снижать качество. На 90% снижение небольшое, но если нагрузка продолжает расти, процент деградации повышается линейно и доходит до максимума при 100% загрузки CPU. Такой подход позволяет снижать качество минимально.
Общий алгоритм выглядит так:
При выключении ДЦ: балансеры перераспределяют запросы в оставшиеся ДЦ => нагрузка на CPU повышается => при превышении порогового значения происходит снижение качества по заданной формуле.
При включении ДЦ: балансеры перераспределяют запросы на все ДЦ => нагрузка на CPU снижается => понижение качества прекращается.
![Повышение нагрузки при выключении ДЦ. Линии на верхнем графике показывают загрузку CPU в отдельных ДЦ. Нагрузка выросла с 82% до 98%. Нижний график показывает процент срезанных документов. Повышение нагрузки при выключении ДЦ. Линии на верхнем графике показывают загрузку CPU в отдельных ДЦ. Нагрузка выросла с 82% до 98%. Нижний график показывает процент срезанных документов.](https://habrastorage.org/getpro/habr/upload_files/602/c25/b73/602c25b733b9ce88e9107fc0619abd2e.jpg)
Внедрение
Для внедрения мы провели несколько экспериментов и понижали качество на небольшом проценте пользователей. В течение недели мы постоянно отслеживали уменьшение нагрузки и одновременно количество переходов на магазины-партнёры. Имея на руках цифры, мы договорились с бизнесом на максимальное снижение кликов в 1% при снижении качества. Исходя из этого мы посчитали, сколько серверов можем отдать другим проектам или выключить для резерва. У нас получилось 15% всех мощностей поиска.
В конце, когда всё было готово, мы провели эксперимент с отключением дата-центра, чтобы убедиться, что всё работает как надо.
Выводы
Graceful degradation — хороший способ не держать в резерве много железа на случай большой аварии вроде отключения дата-центра, а также для кратковременных повышений нагрузки, например, нашествия поисковых ботов.