>Если сервисы идемпотенты, то одинаковые данные приведут к одинаковым ошибкам. Если этот так, то зачем вообще повторять?
это чаще всего задержки in-memory очередей где-то в системе, те самые "флапы". Их норм ретраить. Происходят либо из-за случайностей (не повезло и на данном поде параллельно выполнялся очень тяжелый запрос, съевший CPU), либо когда одному поду плохонько.
>Ретраи нужнытолько для сетевых ошибок и больше ни для чего другого.
даже если такое правило принять, то его нереально выполнять: для этого нужно чтобы каждый сервис или СУБД при получении сетевой ошибки от зависимостей, наружу тоже выдавал сетевую ошибку (например, тайм-аутил или разрывал коннект). Так как ретраить могут на уровень выше. А если не выдать сетевую ошибку - не будет ретрая. Но так код не пишут обычно.
>сомнение в пользе увеличивающих таймаутов в небатчевых сервисах. Если клиентский сервис может отвалиться по таймауту, то экспотенциальные повторы только увеличат вероятность таймаута
не расшифруете мысль? тайм-ауты в exponential backoff не увеличиваются, растут лишь паузы между попытками.
1 - да, и вот это довольно дорого. Интересно насколько дешево у вас удалось похожее сделать? Мы сначала смотрели на envoy adaptive concurrency чтобы эту проблему решить, там свои сложности всплыли после тестов. В итоге решили вместо envoy запилить самим адаптивные пулы коннектов к СУБД в userver (то что выше@antoshkka называет congestion control). И это довольно дорогая разработка вышла на месяцы. А retry budget для нового go фреймворка внутри мы например за пару дней сделали.
2 - у нас такие мультиплексоры были в Такси, мы от них из-за сложностей отказались, но снова думаем о них :) по опыту факапов с mongos и самописными мультиплексорами - сложно, рискованно, легко набажить. У вас все гладко тут было?
у нас по дефолту в инфре микросервисов (в userver) выставлено 10%. Во всех новых сервисах оно автоматом начинает работать так. При этом можно оверрайдить и отключать, и есть такие единичные случаи, но это отдельная проблема.
спасибо за идею, и по правде мы не использовали автоскейлинг в Такси. Но боюсь он намного сложнее чем retry budget:
когда скейлишь stateless сервис до скейлинга его базы, то базу прикладывает коннектами или запросами. Наступали на граблю когда руками скейлили на факапах. Вместо базы может быть и другая незащищенная зависимость
чтобы скейлить сервис выше какого-то числа подов порой нужно напилить мультиплексер коннектов к базе
автоскейлинг надо дружить с load shedding и рейт-лимитерами если юзается только сигнал про cpu usage
механика circuit breaker через readiness пробу уязвима к тем же проблемам что описаны в статье: либо порог будет слишком большой (50+%), либо будет на выпадании шарда базы отрубать все. С большим порогом есть шанс войти в состояние когда переходы слишком резкие, и система не может работать при полном возврате трафика, и работает по синусоиде. Мы такое тоже ловили.
не отрицаю что автоскейлинг делать нужно, и мы планируем, челленджу лишь его огромную сложность и риски относительно retry budget
есть сходство, да. Только в отличие от криптографии, довольно много популярных либ коннекторов/клиентов делает небезопасные ретраи по умолчанию (порой можно сконфигурить безопасно, но это надо явно указывать).
Хорошее замечание. Пока у нас это реализовано внутри языковых фреймворков (userver, go, java), со временем хотим унести все в service mesh. Экспериментируем с envoy, у него есть в тч retry budget.
При этом на практике довольно важно понимать что это за техники вокруг ретраев и почему они существуют, а не просто использовать готовое.
В статье это скомкано, но имелся ввиду кейс, когда определенная часть заказов или пользователей сбоит. Но скорее не по причине плохого хоста, а из-за выпадания одного шарда СУБД или поломанной логики для части заказов (например, только для Доставки или только за кэш).
у подхода "отдельный инстанс СУБД под микросервис" есть не только преимущества - например, в изоляции отказов, но и недостатки. Самый жирный недостаток в контексте аптайма - потеря эластичности. Когда базы общие/коммунальные - при всплеске нагрузки у таблицы/базы только одного сервиса - оно нормально прожевывается за счет запаса остальных баз/таблиц. С выделенными СУБД под микросервисы это теряется и мы быстро упираемся в cpu инстанса.
>Не хватает поэтапного заведения трафика на приложения (1, 10, 50, 100%), чтобы кеши всех нижележащих сервисов успели прогрузиться."
Пара комментов про этот action item из опыта починки аналогичных инцидентов:
лучше иметь рубильник для возврата нагрузки произвольной плавности от 0 до 100%. Переход с 50% до 100% легко может свалить систему заново.
в зависимости от специфики бизнеса возможно рубильник нужен не только по проценту, но и как-то более умно
выглядит, что этот action item важнее всех остальных вместе взятых, тк он самый обобщаемый на произвольные инциденты. В инцидент-менеджменте важна фокусировка. На практике, когда много action items, то они как-то паралеллятся, и в итоге может оказаться, что из-за этого самый важный action item сделан только через месяц.
довольно много тонкостей как делать этот рубильник надежно: по запросам / пользователям / заказам; делать ли его в форме rps лимита или concurrency лимита (link); если concurrency то на каком уровне - все in progress заказы или еще как-то; если по запросам то как решать проблемы этого подхода; и др.
кэши это лишь частный случай. Могут быть очереди запросов.
советую изучить теорию про metastable failure state (там внутри также крутая pdf-ка), она довольно хорошо структурирует произошедшее и объясняет почему этот action item самый ключевой. Например, из теории становится понятным почему ребут системы после отката версии помог, а следующее при починке только ухудшило ситуацию:
увеличение размера очереди запросов (BulkheadQueue), лучше наоборот уменьшать
увеличение лимита на concurrency (BulkheadConcurrency) в 7 раз, лучше наоборот уменьшать
апскейл сервисов перед базой без апскейла базы перед этим. Лучше опять таки наоборот - даунскейлить сервис, чтобы базе полегчало скорее
обычно на такое юнит-тесты пишут, особенно если работа с деньгами
передавая число число (json стандарт это позволяет
с этим связаны свои риски, что дальше на таком double (вместо decimal) по ошибке пойдет арифметика и получим проблемы с точностью.
Либо стандартизировать сериализацию
мы в Яндекс Go передаем суммы как int в API - умножаем decimal на 10000 (знаем что больше 4-х знаков не поддерживаем) - там где нужна обработка значения. Там где не нужна, например, просто отрендерить на клиенте текст, там строкой.
мы быстро привыкли, приточка так же шумит примерно. Проблема скорее в том, что это первая скорость. Поэтому влажность итоговая неидеальная, точных процентов не помню. А чтобы была хорошая влажность, надо скорость ставить выше и оно сильно громче. Поэтому днем мы включаем на высокие скорости, а ночью на минимальную.
мы включаем на первую скорость, и по шуму это примерно как наша приточка на 3-ей скорости, ~37дб. Привыкли за пару дней. Специально брал с 4-мя вентиляторами увлажнитель, вроде как они тише чем с 1-2 вентиляторами
Не рассматривали ли вариант не делать вообще интеграционные тесты, а ограничиться функциональными/юнит тестами на отказы внешних сервисов? Как много встречается на практике таких интеграционных багов, которые такие функциональные тесты бы не нашли?
Не изучал, я с установкой из статьи живу, ничего не менял. Насчет сопротивления воздуха — Minibox Home 350 продавливает HEPA, только для поддержания воздухообмена пришлось поднять с 3 на 4 скорость. То есть продавливать то почти все будет, но шумнее.
HEPA фильтры меняю (мне Minibox обещал звонить и напоминать, но пока ни одного звонка за 8 месяцев не было), когда уровень CO2 становится выше порога, значит сопротивление фильтра стало слишком высоким, значит забился.
Перевели на английский: medium
на 1 под мог только что выехать канареечный релиз, который кидает 500 на 1% специфичных запросов. Хорошо бы его отретраить в другой под.
по сути ретраи в таких кейсах работают как естественная балансировка нагрузки между подами
>Если сервисы идемпотенты, то одинаковые данные приведут к одинаковым ошибкам. Если этот так, то зачем вообще повторять?
это чаще всего задержки in-memory очередей где-то в системе, те самые "флапы". Их норм ретраить. Происходят либо из-за случайностей (не повезло и на данном поде параллельно выполнялся очень тяжелый запрос, съевший CPU), либо когда одному поду плохонько.
>Ретраи нужны только для сетевых ошибок и больше ни для чего другого.
даже если такое правило принять, то его нереально выполнять: для этого нужно чтобы каждый сервис или СУБД при получении сетевой ошибки от зависимостей, наружу тоже выдавал сетевую ошибку (например, тайм-аутил или разрывал коннект). Так как ретраить могут на уровень выше. А если не выдать сетевую ошибку - не будет ретрая. Но так код не пишут обычно.
>сомнение в пользе увеличивающих таймаутов в небатчевых сервисах. Если клиентский сервис может отвалиться по таймауту, то экспотенциальные повторы только увеличат вероятность таймаута
не расшифруете мысль? тайм-ауты в exponential backoff не увеличиваются, растут лишь паузы между попытками.
понятно, спасибо за детали!
Пока нет, планирую. Когда переведу - в ответ на ваш комментарий прикреплю ссылку.
1 - да, и вот это довольно дорого. Интересно насколько дешево у вас удалось похожее сделать? Мы сначала смотрели на envoy adaptive concurrency чтобы эту проблему решить, там свои сложности всплыли после тестов. В итоге решили вместо envoy запилить самим адаптивные пулы коннектов к СУБД в userver (то что выше @antoshkka называет congestion control). И это довольно дорогая разработка вышла на месяцы. А retry budget для нового go фреймворка внутри мы например за пару дней сделали.
2 - у нас такие мультиплексоры были в Такси, мы от них из-за сложностей отказались, но снова думаем о них :) по опыту факапов с mongos и самописными мультиплексорами - сложно, рискованно, легко набажить. У вас все гладко тут было?
у нас по дефолту в инфре микросервисов (в userver) выставлено 10%. Во всех новых сервисах оно автоматом начинает работать так. При этом можно оверрайдить и отключать, и есть такие единичные случаи, но это отдельная проблема.
спасибо за идею, и по правде мы не использовали автоскейлинг в Такси. Но боюсь он намного сложнее чем retry budget:
когда скейлишь stateless сервис до скейлинга его базы, то базу прикладывает коннектами или запросами. Наступали на граблю когда руками скейлили на факапах. Вместо базы может быть и другая незащищенная зависимость
чтобы скейлить сервис выше какого-то числа подов порой нужно напилить мультиплексер коннектов к базе
автоскейлинг надо дружить с load shedding и рейт-лимитерами если юзается только сигнал про cpu usage
механика circuit breaker через readiness пробу уязвима к тем же проблемам что описаны в статье: либо порог будет слишком большой (50+%), либо будет на выпадании шарда базы отрубать все. С большим порогом есть шанс войти в состояние когда переходы слишком резкие, и система не может работать при полном возврате трафика, и работает по синусоиде. Мы такое тоже ловили.
не отрицаю что автоскейлинг делать нужно, и мы планируем, челленджу лишь его огромную сложность и риски относительно retry budget
есть сходство, да. Только в отличие от криптографии, довольно много популярных либ коннекторов/клиентов делает небезопасные ретраи по умолчанию (порой можно сконфигурить безопасно, но это надо явно указывать).
Хорошее замечание. Пока у нас это реализовано внутри языковых фреймворков (userver, go, java), со временем хотим унести все в service mesh. Экспериментируем с envoy, у него есть в тч retry budget.
При этом на практике довольно важно понимать что это за техники вокруг ретраев и почему они существуют, а не просто использовать готовое.
Вася передает благодарность, в статье поправил
В статье это скомкано, но имелся ввиду кейс, когда определенная часть заказов или пользователей сбоит. Но скорее не по причине плохого хоста, а из-за выпадания одного шарда СУБД или поломанной логики для части заказов (например, только для Доставки или только за кэш).
у подхода "отдельный инстанс СУБД под микросервис" есть не только преимущества - например, в изоляции отказов, но и недостатки. Самый жирный недостаток в контексте аптайма - потеря эластичности. Когда базы общие/коммунальные - при всплеске нагрузки у таблицы/базы только одного сервиса - оно нормально прожевывается за счет запаса остальных баз/таблиц. С выделенными СУБД под микросервисы это теряется и мы быстро упираемся в cpu инстанса.
Спасибо за интересный пост-мортем!
>Не хватает поэтапного заведения трафика на приложения (1, 10, 50, 100%), чтобы кеши всех нижележащих сервисов успели прогрузиться."
Пара комментов про этот action item из опыта починки аналогичных инцидентов:
лучше иметь рубильник для возврата нагрузки произвольной плавности от 0 до 100%. Переход с 50% до 100% легко может свалить систему заново.
в зависимости от специфики бизнеса возможно рубильник нужен не только по проценту, но и как-то более умно
выглядит, что этот action item важнее всех остальных вместе взятых, тк он самый обобщаемый на произвольные инциденты. В инцидент-менеджменте важна фокусировка. На практике, когда много action items, то они как-то паралеллятся, и в итоге может оказаться, что из-за этого самый важный action item сделан только через месяц.
довольно много тонкостей как делать этот рубильник надежно: по запросам / пользователям / заказам; делать ли его в форме rps лимита или concurrency лимита (link); если concurrency то на каком уровне - все in progress заказы или еще как-то; если по запросам то как решать проблемы этого подхода; и др.
кэши это лишь частный случай. Могут быть очереди запросов.
советую изучить теорию про metastable failure state (там внутри также крутая pdf-ка), она довольно хорошо структурирует произошедшее и объясняет почему этот action item самый ключевой. Например, из теории становится понятным почему ребут системы после отката версии помог, а следующее при починке только ухудшило ситуацию:
увеличение размера очереди запросов (BulkheadQueue), лучше наоборот уменьшать
увеличение лимита на concurrency (BulkheadConcurrency) в 7 раз, лучше наоборот уменьшать
апскейл сервисов перед базой без апскейла базы перед этим. Лучше опять таки наоборот - даунскейлить сервис, чтобы базе полегчало скорее
спасибо, в целом валидно, что тут много тонкостей
обычно на такое юнит-тесты пишут, особенно если работа с деньгами
с этим связаны свои риски, что дальше на таком double (вместо decimal) по ошибке пойдет арифметика и получим проблемы с точностью.
мы в Яндекс Go передаем суммы как int в API - умножаем decimal на 10000 (знаем что больше 4-х знаков не поддерживаем) - там где нужна обработка значения. Там где не нужна, например, просто отрендерить на клиенте текст, там строкой.
HEPA фильтры меняю (мне Minibox обещал звонить и напоминать, но пока ни одного звонка за 8 месяцев не было), когда уровень CO2 становится выше порога, значит сопротивление фильтра стало слишком высоким, значит забился.