>Если сервисы идемпотенты, то одинаковые данные приведут к одинаковым ошибкам. Если этот так, то зачем вообще повторять?
это чаще всего задержки 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 становится выше порога, значит сопротивление фильтра стало слишком высоким, значит забился.
спасибо,
желания менять систему точно нет, уже несколько месяцев как все хорошо работает. Вероятно, были способы как добиться всего того же сильно дешевле и быстрее, и без танцев с бубном. Например, поставить бризер, или попробовать другие приточки.
на 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 становится выше порога, значит сопротивление фильтра стало слишком высоким, значит забился.
желания менять систему точно нет, уже несколько месяцев как все хорошо работает. Вероятно, были способы как добиться всего того же сильно дешевле и быстрее, и без танцев с бубном. Например, поставить бризер, или попробовать другие приточки.