company_banner

Graceful degradation. Доклад Яндекс.Такси

    Сервисы необходимо писать так, чтобы минимальная функциональность сохранялась всегда — даже если откажут критически важные компоненты. Илья Сидоров, руководитель одной из команд продуктовой разработки бэкенда Яндекс.Такси, объяснил в своем докладе, как мы даем пользователю заказать машину, когда отдельные части системы не работают, и по какой логике мы активируем упрощенные версии сервиса.


    Важно писать не только сервисы, которые хорошо работают, но и сервисы, которые хорошо ломаются.

    — Я очень рад вас всех видеть. Сегодня я буду рассказывать о graceful degradation. Если вы поищете это в Яндексе, скорее всего, вы узнаете, как сделать так, чтобы ваш сайт работал без JS. Я расскажу немного про другое. Про graceful degradation применительно к бэкенду.



    Начнем с определения. Как это выглядит в реальности?



    Тут представлено наше приложение Яндекс.Такси в том случае, если не работает один из сервисов — сервис выбора точки назначения, куда водитель должен вас отвезти. Как вы видите, на этом экране нет большой кнопки «Заказать такси», а значит, пользователь не сможет воспользоваться сервисом. Но можно попробовать деградировать и позволить пользователю не выбирать точку Б.

    Тогда он не сможет узнать точную цену поездки, мы не сможем построить маршрут, но зато у пользователя будет кнопка «Заказать такси», и он сможет воспользоваться нашим сервисом. Основная функция нашего приложения будет доступна. Именно об этом я и хочу сегодня рассказать. О том, как правильно деградировать и что можно делать с сервисом, который сломался.

    План выступления. Расскажу, как деградировать, что делать с сервисом. Можно его отключать, а еще — применять другое поведение. Потом расскажу, как понять, когда настало время выключать наш сервис. И в конце расскажу о нескольких нюансах, с которыми нам пришлось столкнуться, когда мы делали систему автоматической деградации для Яндекс.Такси.

    Что можно сделать с сервисом, который сломался? Можно выключить функциональность. Если у вас не работает сервис предсказания индивидуальных точек назначения, то вы выключаете этот сервис. Если не работает чат между водителем и пассажиром, то вы выключаете чат. Если нельзя заказать машину, то вы выключаете кнопку «Заказать машину» — ой, нет, так не работает. Не всю функциональность можно выключить. И если что-то выключить нельзя, то нужно применить другой подход. Например, можно попробовать сделать макет или упрощенную функциональность. Такое упрощенное поведение мы в Яндексе называем тыквой — говорим, что сервис превратился в тыкву.

    Рассмотрим эти решения подробнее.



    Как отключать сервисы? Наверное, можно сделать правильную архитектуру. Предположим, у нас есть один монолитный сервис. Если выходит из строя одна из его частей, то ломается весь сервис. А вот если мы разделим сервис на части так, что клиенты будут использовать разные сервисы для разных запросов, то станет значительно лучше.

    Как это будет работать на примере? Есть сервис Яндекс.Такси, в котором две основные функции: заказать такси и пообщаться с водителем. Пока у нас есть один монолитный бэкенд, при отказе чата с водителем будет затронут и основная функциональность заказа такси.





    Что можно попробовать сделать? Разделить монолитный сервис на две части. Одна часть будет отвечать за заказ такси, а другая — за общение с водителем.

    Теперь все выглядит гораздо лучше. Если ломается чат с водителем, то все остальное продолжает работать правильно.



    Как видите, клиент использует разные API, разные запросы, чтобы совершать заказ и осуществлять связь с водителем.

    Но на самом деле кажется, что сейчас не все так хорошо, потому что есть паразитная связь между сервисом чатов и сервисом заказов. И может получиться, что сервис заказа использует неработающий сервис чатов. В таком случае основная функциональность не будет работать.



    А в таком случае все гораздо лучше. Паразитная связь пропала, и теперь наши сервисы действительно независимы друг от друга. А значит, при поломке сервиса чатов вы все еще сможете воспользоваться такси.

    Вывод из этого следующий: если вы хотите деградировать с использованием разделения на сервисы, то очень важно сделать так, чтобы сервисы были независимы друг от друга. Это означает, что у них должны быть разные точки входа, разные энд-поинты. У них должны быть разные рантаймы. И конечно, они должны использовать разные БД. Иначе один сломавшийся сервис может сломать за собой все остальные сервисы по цепочке.



    Хорошо, мы разобрали, как можно отключать функциональность. Давайте теперь посмотрим, как делать функциональность по умолчанию, как делать тыкву. На этом экране наш сервис предсказания точек назначения. Сервис использует умный ИИ, чтобы предсказать пользователю наилучшие для него в текущий момент точки назначения. А если ИИ устал, то используем поведение по умолчанию и предлагаем пользователю уехать из Москвы.

    Давайте разберем, как это работает на практике.



    У нас есть клиент, он обращается в сервис точек назначения и получает ошибку.



    Теперь возможны две ситуации. Первая ситуация, если сбой был единичный, — это просто один непрошедший запрос. В таком случае мы просто прокинем ошибку клиенту, он сделает перезапрос и получит свои любимые точки назначения.

    А вот если сбой массовый, мы включаем тыкву и пользователь получает поведение по умолчанию.



    Но такое захардкоженное поведение реализовать гораздо проще, и эта тыква очень надежная, поэтому позволяет нам работать даже в том случае, когда ИИ выходит из строя. Если мы знаем, что пользователи часто ездят в аэропорты, то мы не заметим сильного ухудшения жизни пользователей.



    Даже если у включен режим деградации, включена тыква, но пользователь обращается в сервис и получает успешный ответ, то мы используем этот ответ, а не тыкву. И такое поведение — когда в случае получения ответа мы его и используем, а в случае ошибки используем тыкву — мы называем режимом fallback.



    Нет ошибки — успешный ответ. Есть ошибка — тыква. Мы говорим, что включился fallback.

    Я разобрал то, что можно сделать с сервисом, который сломался. Можно отключить, а можно включить тыкву. Давайте теперь перейдем ко второй части и разберемся, как диагностировать.

    У нас есть два больших вопроса, на которые нужно ответить. Первый — когда нужно выключить сервис и включить тыкву. Второй — когда нужно выключить тыкву и включить назад сервис. Перед тем, как мы сможем ответить на эти вопросы, нужно прояснить один момент.



    В любой сложной системе, которая взаимодействует с большим числом агентов, обязательно есть некоторый фон ошибок. На этом слайде мы видим реальный график обращений к одному из наших сервисов. На него приходит несколько тысяч RPS, ошибок мы получаем чуть меньше 1%. Здесь логарифмический масштаб.

    Ошибки могут быть вызваны разными вещами. Может, это какой-то внутренний процесс, обновление каких-то БД или просто фоновые процессы. Может, клиенты ходят с неправильными запросами, но факт остается фактом: у нас всегда будет фон ошибок. Давайте его примем и будем двигаться дальше.



    Итак, мы используем решение на основе статистики. У нас есть специальная БД, в которую мы сохраняем статистику, сохраняем количество успешных запросов, количество запросов с ошибками и запросов, для которых был включен fallback. Мы берем и накапливаем скользящим окном в некотором промежутке времени статистику по нашему сервису. Когда в этом скользящем окне доля запросов с ошибками превышает некоторый порог, мы включаем fallback. А когда число ошибок становится меньше порога, то мы его выключаем.

    Обратите внимание на выделенные области. В 19:01 начали появляться первые ошибки, но пока что их доля достаточно мала, и до 19:02 мы не включаем fallback. В 19:02 порог превышен, мы включили fallback. В 19:08 обратный процесс: ошибки закончились, но еще некоторое время у нас fallback включен, потому что в нашем скользящем окне порог все еще превышен. В 19:09 мы выключили fallback.

    Мы разобрали, когда выключать сервис. Надо ответить на второй вопрос: когда его включить. Все просто: мы используем то же самое решение на основе статистики.



    Тут важно то, что мы не снимаем нагрузку с сервиса, даже если мы включили режим деградации. Именно это позволяет нам продолжать получать статистику даже в том случае, если мы показываем пользователю тыкву. Таким образом мы можем определить, что ошибки закончились, сервис починился. Значит, можно снова включить его на полную.



    Когда мы говорим про деградацию, нельзя не сказать про мониторинги. Хороший мониторинг — половина успеха, половина пути к автоматическому отключению или автоматической деградации. Нам важно понимать, какие вообще с нашим сервисом случаются проблемы, какой может быть характер ошибок и насколько часто они происходят. И возможно, на первом этапе нам даже не нужен автоматический выключатель. Просто если лампочка мониторинга загорелась, мы можем взять и отключить сервис вручную. Когда лампочка мониторинга потухла, мы включаем сервис.

    Если мы делаем автоматическую деградацию, автоматический выключатель, то важно сделать мониторинг на сам fallback. Если система деградации работает достаточно хорошо, то пользователи, на самом деле, могут вообще не замечать, что у нас что-то сломалось. Мы сами можем, если не будет мониторингов, это не замечать. Важно следить за fallback, важно понять, когда он включен, когда выключен, чтобы была статистика и мы могли понимать, сколько времени функциональность не работает, становится ли наш бэкенд хуже или лучше со временем, в зависимости от того, сколько времени мы считаем проводить fallback.

    С основной частью всё.

    Под конец хотел бы рассказать несколько нюансов, с которыми нам пришлось столкнуться, когда мы разрабатывали систему автоматической деградации в Яндекс.Такси.



    Первое, на что стоит обратить внимание, это консистентность. Если вы делаете автоматическую деградацию для некоторого сервиса, важно, чтобы сервис отвечал консистентно для всех его клиентов. Если у вас есть два клиента, которые используют сервис, важно, чтобы ответы для этих двух клиентов в случае деградации были консистентны. И если у вас есть сервис, который участвует в некотором длительном процессе, нужно понимать: возможно, в начале и конце процесса сервис будет работать правильно, а где-то посередине будет включен fallback.

    Это звучит сложно, но давайте попробую объяснить на примере. Возможно, станет понятнее.



    Вот наш чат между водителем и пассажиром. Самый простой способ его деградировать — отключить. Давайте представим, что сломался чат для водителя. Что получится? Клиент будет писать в чат, а водитель не увидит сообщений. Наверное, они будут очень недовольны, будут ругаться на наше приложение, когда встретятся друг с другом. В данном случае важно, чтобы чат был или одновременно включен, или одновременно выключен для всех участников этого чата. Вот что я называю консистентностью.



    Второй нюанс касается того, что наше приложение Яндекс.Такси — геораспределенное: такси можно заказать в Москве, Красноярске или Хельсинки. Это приходится учитывать даже при разработке систем деградации. Представьте, что у нас есть очень много успешных запросов и совсем чуть-чуть запросов с ошибками. Казалось бы, это нормальная ситуация, фон ошибок всегда присутствует. Но на ту же самую картину можно взглянуть по-другому.

    Можно увидеть, что сервис не работает в Мытищах и нужно включать fallback для этих пользователей. Вывод следующий: нужно строить правильную статистику. Для нас как для геораспределенного сервиса это в том числе означает, что нужно строить статистику в разрезе по городам. Если мы правильно сделаем статистику, мы сразу увидим, что большинство запросов из Мытищ ломается, и включим fallback именно для пользователей из Мытищ. А для всех других пользователей продолжим работать в нормальном режиме, потому что для них сервис работает правильно.



    Возможно, для других сервисов будут другие условия и другие нюансы.

    Наши сервисы становятся все более сложными. Часто они зависят от внешнего мира, который мы не можем предсказывать. Поэтому важно писать не только сервисы, которые хорошо работают, но и сервисы, которые хорошо ломаются. Если вы узнали что-то новое, то рассказывайте коллегам, делитесь. Лайк, шер, репост. Деградируйте правильно.
    Яндекс
    575,00
    Как мы делаем Яндекс
    Поделиться публикацией

    Комментарии 28

      +4
      Глядя на заголовок сперва подумал, что это про отключение приложения Uber в России. (со вторника, если кто не в курсе)
        +2

        Немного не в тему, к сожалению достучаться через адреса техподдержки до людей, которые хоть что-то решают нереально.
        Но очень хочется узнать, чем думал яндекс, когда делал приложение Uber BY (я так думаю, с Uber Russia аналогичная ситуация) доступным только из белорусского AppStore? Теперь, когда ко мне приезжают иностранцы я не могу сказать «вызовите убер». Мне приходится надеятся на то, что человек не полениться заранее сменить свой регион в телефоне, скачать нужное приложение и только потом лететь в Беларусь. Зачем вся эта свистопляска, нужно было сразу сделать как в Германии — мол в РБ убера нет, качайте заранее альтернативы. Тем более что приложение Uber By смотрится как дешёвый скин к Яндекс.Такси (к тому же, с порезанным функционалом).

          +8
          В этом была вся прелесть Убера — одно приложение на весь мир. Куда бы не приехал — можешь вызвать такси. А теперь — деградация сервиса
            –4

            Ну весь мир это громко сказано. Нельзя монополизировать весь мир. Есть многоженство факторов, которые помешают это сделать

              +6
              Цель-то была такая. Мне очень нравилось: хоть Франция, хоть Тайланд — одно приложение.
            –6
            Здравствуйте, Дмитрий!

            Мы понимаем, что это не очень удобно. Увы, поддержание старого приложения Uber в Беларуси, России и Казахстане более невозможно по целому ряду причин.

            Действительно, новое приложение Uber BY скачивается из Google Play или App Store только при условии регистрации стора на территории РБ.

            (На всякий случай, вот инструкции по смене страны регистрации аккаунта в сторах —

            App Store: support.apple.com/ru-ru/HT201389
            Google Play: support.google.com/googleplay/answer/7431675?hl=ru)

            Новое приложение действует в трёх странах — Беларусь, Казахстан и Россия. То есть любая версия из трёх приложений, активированная в стране с соответствующей регистрацией стора, будет работать в остальных двух странах.

            Несмотря на трудности с указанием страны в сторах для пользователей из других государств, новое приложение действительно стабильнее и лучше работает на территории Беларуси, России и Казахстана.

            Если у вас есть предложения по поводу оптимизации приложения — будем благодарны за замечания и пожелания на blogs@support-uber.com.
              +3

              Как это поможет-то человеку, который приехал к друзьям в РБ/Россию? Переключи себе Google Play на белорусский, а потом ГОД жди чтобы переключить его обратно ("Переключаться между странами в Google Play можно не чаще раза в год", цитата из статьи по ссылке).


              Бред же. Вон в Тайланде убер так же продался сервису grab, но он меня не заставляет быть коренным тайцем для установки приложения.

                +2

                Я понимаю, что вы процитировали типовой ответ первого уровня поддержки пользователей. А я, наивный, полагал что мне повезёт услышать честный ответ. Мол так и скажите — не прокатывает по юридическим причинам выложить Uber BY в глобальный стор. Могли бы выложить в глобальный и сделать заглушку для всех стран кроме РБ, к примеру. Так же как это реализовано в основном приложении. Ну реально, вы же сделали заведомо мёртвый usecase для иностранцев.
                Единственный раз когда мне более менее честно ответили в поддержке, это когда я прислал видео с демонстрацией бага с расчётом стоимости поездки.

                  +1

                  И да, главный вопрос: зачем нужно это приложение в принципе? С учётом того, что его полностью заменяет или даже значительно превосходит Яндекс.Такси.

                    0
                    видимо это и есть их «заговор».
                0
                Йоды магистра почерк на слайдах местами наблюдаю я
                  +1
                  Если коротко
                  Вырубаем то что не работает. В этом суть доклада… Воды слишком много…
                    +4
                    Суть в том, чтобы спроектировать свое приложение так, что у каждой фичи был бы отдельный рубильник, который отключает только конкретную неработающую часть, не затрагивая нормальные.
                      +2
                      Ну ещё интересно, что они не совсем вырубают — нагрузка продолжает идти. И как только ошибки от сервиса пропадают, они просто выключают тыкву.
                        +1

                        На самом деле идея действительно простая. Но по опыту выходит, что никто о таких вещах просто не задумывается.


                        Технически же вся сложность в том, чтобы вырубить так, чтобы не сломать для пользователя поведение, потому что не всегда можно просто взять и вырубить. И об этом еще меньше задумываются.

                        –4
                        Иногда водители приезжают на другую сторону дороги. Например, вызываешь на Тверской машину. Водитель приезжает и стоит на другой стороне дороги. До ближайшего пешеходного перехода — далеко. Пока он крутится по пробкам, чтобы развернуться включается счетчик ожидания. Это когда-нибудь победите?

                        И такой еще вопрос уже не как к программистам. Иногда водители предлагают отменить заказ и типа дать ему денег в карман, когда расчет идет за кеш. Я, конечно, отказываюсь. Они дуются всю дорогу. Стучать на них — я не так воспитан. И это не единичный случай, почти постоянно, когда сумма поездки большая.
                          +5

                          Я всегда на таких читеров жалуюсь, потому что из-за них страдает качество сервиса.

                            0
                            На другую сторону дороги от чего? В своё время поработал водителем такси, в 99% случаев этот вопрос задают люди, нажимающие «Вызвать такси» не глядя, как их телефон определил местоположение, и куда установлен маркер подачи. Исправлять ничего не нужно, приложение позволяет передвигать маркер.
                            P. S. нередко от таких слышал «ничего не знаю, это ваш Яндекс определяет местоположение» :-)
                              +2
                              Я вызываю такси, вбивая точный адрес. У меня четная сторона улицы, водитель приезжает на нечетную сторону и нажимает на кнопку «мол, я уже приехал». Время ожидания пошло.
                              +1
                              Извините, но подход:
                              Стучать на них — я не так воспитан.
                              никак не приведёт к улучшению клиентского сервиса, в улучшении которого, вы в первую очередь и должны быть заинтересованы.
                                0
                                Я думаю так, что если я нажалуюсь сегодня на Васю, а завтра на Равшана, то толку не будет, после завтра приедет такой же. Ошибка в системе.

                                И я люблю яндекс-такси, и вспоминаю с ужасом такси, которое было в 90-х и нулевых. Спасибо им большое за этот скачек в развитии. И именно поэтому написал это здесь в корпоративном блоге, а так бы просто прошел бы мимо.
                              –1
                              Circuit-breaker(Hystrix) в масштабе сервисов?
                                +3
                                Люблю читать и слушать доклады Яндекса про их Такси. Сразу умный AI, хитрые программерские трюки, летающие автомобили. Потом запускаешь кривобокенькую программку в городе Мухосранске в 5 часов утра с повышенным спросом и едешь на разбитом корыте по разбитым дворам куда водителя таджика за каким-то хреном повёл Яндекс Навигатор.
                                  +1
                                  Яндекс.Такси при недоступном местоположении (загород, подвал и т.д.) даёт возможность указать место подачи вручную. Uber Russia — тыква, причём в «большом» Uber'е такой проблемы не было.
                                    +1

                                    Спасибо за комментарий. Обязательно передадим разработчикам. Если не сложно – пришлите, пожалуйста, на blogs@support-uber.com подробности и модель устройства, на котором этот момент заметили.

                                    +1
                                    Прочитав статью, сразу вспоминается диалог:
                                    — Отдам даром микроволновку: крутится, но не греет, выглядит хорошо.
                                    — То есть курица не разогреется, но накатается вдоволь?
                                      +2
                                      Интересно, будет ли статья от Яндекса на Хабре об участии их беспилотного автомобиля в CES 2019 в Лас-Вегасе?)
                                        0
                                        Первый раз такая весёлая и в то же время нечестная раздача призов

                                        Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                                        Самое читаемое