Дедлоки (1/2)
Всех с наступившим! 🐌
Решил я тут почитать, что умные дядьки пишут про дедлоки. Как обычно оказалось, что поверхностное понимание уровня «пройти интервью» неверно от начала до конца, а в реальности всё супер интересно и конечно известно уже последние лет 50.
Дедлок или deadly embrace, как их называл товарищ Дейкстра (тот самый, который алгоритм) — ситуация бесконечного ожидания, при которой группа процессов не может продолжать работу, пока «внешние силы» не совершат определенные действия. Слово «процесс» здесь не имеет отношения к процессам в операционной системе. Процесс в дедлоке — некий автомат с набором состояний. Такими состояниями могут быть «процесс А не владеет ресурсами» или «процесс B владеет ресурсом alpha и пытается получить ресурс beta». На самом деле определение не совсем корректное в 100% ситуаций, но для обсуждения большинства подходов подойдёт.
Для начала, рассмотрим самый простой случай: дедлок на одной машине, вызванный конкурентным доступом к некоторому ресурсу из нескольких процессов. Ресурсом может быть как примитив синхронизации, например мьютекс, так и устройство, например принтер.
Такой дедлок называется либо ресурсным, либо interleaved дедлоком. Первое более привычное и популярное название, второе я нашёл только в [4]. Для возможности его возникновения необходимо выполнение 3 условий:
1. Exclusive access. Только один процесс может захватить ресурс одновременно.
2. No-preemption. Процесс, захвативший ресурс, не может быть прерван.
3. Hold and wait condition. Должны разрешаться ситуация, при которой процесс захватил сначала один ресурс, а теперь ждёт пока освободится второй. Не отпуская при этом первый.
Наличие этих условий в системе ещё не гарантирует возникновение дедлока! Это только необходимые условия. Их наличие означает существование unsafe region[5] — такого набора состояний процессов, попав в которое, ни один из процессов не может завершить свою работу.
При наличии unsafe region достаточно найти лишь 1 набор состояний, чтобы обеспечить дедлок. Оно же последнее условие:
4. circular wait condition. Процессы и ресурсы должны иметь возможность собираться в «цепочку». К примеру: Процесс A владеет ресурсом a при этом хочет захватить ресурс b, которым владеет процесс B, который хочет завладеть ресурсом a. Если обозначить эти отношения стрелочками:
Схематично получится круг (ну или квадрат):
Всех с наступившим! 🐌
Решил я тут почитать, что умные дядьки пишут про дедлоки. Как обычно оказалось, что поверхностное понимание уровня «пройти интервью» неверно от начала до конца, а в реальности всё супер интересно и конечно известно уже последние лет 50.
Дедлок или deadly embrace, как их называл товарищ Дейкстра (тот самый, который алгоритм) — ситуация бесконечного ожидания, при которой группа процессов не может продолжать работу, пока «внешние силы» не совершат определенные действия. Слово «процесс» здесь не имеет отношения к процессам в операционной системе. Процесс в дедлоке — некий автомат с набором состояний. Такими состояниями могут быть «процесс А не владеет ресурсами» или «процесс B владеет ресурсом alpha и пытается получить ресурс beta». На самом деле определение не совсем корректное в 100% ситуаций, но для обсуждения большинства подходов подойдёт.
Для начала, рассмотрим самый простой случай: дедлок на одной машине, вызванный конкурентным доступом к некоторому ресурсу из нескольких процессов. Ресурсом может быть как примитив синхронизации, например мьютекс, так и устройство, например принтер.
Такой дедлок называется либо ресурсным, либо interleaved дедлоком. Первое более привычное и популярное название, второе я нашёл только в [4]. Для возможности его возникновения необходимо выполнение 3 условий:
1. Exclusive access. Только один процесс может захватить ресурс одновременно.
2. No-preemption. Процесс, захвативший ресурс, не может быть прерван.
3. Hold and wait condition. Должны разрешаться ситуация, при которой процесс захватил сначала один ресурс, а теперь ждёт пока освободится второй. Не отпуская при этом первый.
Наличие этих условий в системе ещё не гарантирует возникновение дедлока! Это только необходимые условия. Их наличие означает существование unsafe region[5] — такого набора состояний процессов, попав в которое, ни один из процессов не может завершить свою работу.
При наличии unsafe region достаточно найти лишь 1 набор состояний, чтобы обеспечить дедлок. Оно же последнее условие:
4. circular wait condition. Процессы и ресурсы должны иметь возможность собираться в «цепочку». К примеру: Процесс A владеет ресурсом a при этом хочет захватить ресурс b, которым владеет процесс B, который хочет завладеть ресурсом a. Если обозначить эти отношения стрелочками:
a -> A значит процесс A владеет ресурсом a. A -> b значит процесс A хочет завладеть ресурсом b.Схематично получится круг (ну или квадрат):
a -> A
^ |
| v
B <- b
🔥19👍1
Дедлоки (2/2)
Первый инсайт: дедлоки не возникают просто так, для них необходимо 3 условия: exclusive access, no-preemption, hold-and-wait + одно достаточное: circular wait.
Второй инсайт: чтобы побороть дедлок, достаточно избавить от одного из условий!
Пункт 2 особенно интересен, и позволяет другими глазами взглянуть на работу с ресурсами и привычные «программистские трюки». Например: почему часто разделяют локи на read-only и write-only локи? Для перформанса, конечно. Но ещё и потому что в read-only локе не может возникнуть дедлок, так как он не эксклюзивный (первое условие)!
Или вот я недавно сделал нечаянно не concurrent индекс в postgres на табличку с парой сотен миллионов записей и всё встало колом. Решение проблемы — preemption! Руками грохаем транзакцию и дедлок перестаёт существовать. И это магистры компьютерных наук знали ещё в 70-ых. Подобные же механизмы можно и автоматизировать. Правда не так просто. С одной стороны, обнаружение дедлока — в сущности задача на графах, где просто нужно правильно пройтись по ребрам от процессов до ресурсов. С другой, граф постоянно меняется буквально пока по нему проходишься. Кажется решений проблемы в общем случае нет, но в частных, например только в БД, они есть и работают.
С hold-and-wait тоже интересно. А что если бы процесс сразу же запрашивал все ресурсы, которые ему требуются? Или по крайней мере захватывал бы их постепенно, но при не успехе отпускал бы все уже захваченные. Такой подход, оказывается, тоже убирает проблему дедлоков. К сожалению, не каждый процесс знает наперёд ни все нужные ему ресурсы, ни порядок, в котором они будут захватываться. Опять в общем случае решить тяжело, в частном вполне. Например обычно заранее известно, сколько памяти и CPU нужной новой виртуалке/контейнеру, а это тоже ресурсы, за которые тоже может быть конкуренция. Там правда ситуация не обязательно завершиться дедлоком, а скорее перемещением одного из процессов, но это детали. Раскидывать задачи по компьютерам — очень сложная задача, поэтому в как-нибудь в следующий раз.
Наконец, circular-wait. А пусть все ресурсы должны захватывать только в определенном порядке. К примеру, перечислим все ресурсы в системе от
Все эти подходы можно широкими мазками разделить на 3 категории:
- Detection. Подходы, которые по графу ресурсов и их зависимостей позволяют обнаружить дедлок. Для борьбы используется preemption.
- Prevention. Цель: задизайнить систему таким образом, чтобы дедлок был невозможен. Например: пронумеровать ресурсы и ввести условие их захвата. Или запретить захватить ресурс, а затем ждать второй.
- Avoidance. Подходы, при которых система переходит только из одного «безопасного» состояния в другое. Решение о выдаче ресурса принимается динамически, к примеру во время запроса. Самый популярный алгоритм: алгоритм банкира. К сожалению для него необходимо, чтобы процессы сразу знали, какие ресурсы им нужны.
- Алгоритм страуса. Или ничего не делать. Стратегия при которой программист сажается на on-call и ему выдаётся кнопка «отмена».
Ресурсы:
1. E.G. Coffman, M. Elphick, A. Shoshani. System Deadlocks.
2. Richard C. Holt. Some deadlock properties of computer systems.
3. S.S. Isloor, T.A. Marsland. The Deadlock Problem: An Overview.
4. Gertrude Neuman Levine. Defining deadlocks.
5. Charles M. Shub. A Unified Treatment of Deadlock.
Первый инсайт: дедлоки не возникают просто так, для них необходимо 3 условия: exclusive access, no-preemption, hold-and-wait + одно достаточное: circular wait.
Второй инсайт: чтобы побороть дедлок, достаточно избавить от одного из условий!
Пункт 2 особенно интересен, и позволяет другими глазами взглянуть на работу с ресурсами и привычные «программистские трюки». Например: почему часто разделяют локи на read-only и write-only локи? Для перформанса, конечно. Но ещё и потому что в read-only локе не может возникнуть дедлок, так как он не эксклюзивный (первое условие)!
Или вот я недавно сделал нечаянно не concurrent индекс в postgres на табличку с парой сотен миллионов записей и всё встало колом. Решение проблемы — preemption! Руками грохаем транзакцию и дедлок перестаёт существовать. И это магистры компьютерных наук знали ещё в 70-ых. Подобные же механизмы можно и автоматизировать. Правда не так просто. С одной стороны, обнаружение дедлока — в сущности задача на графах, где просто нужно правильно пройтись по ребрам от процессов до ресурсов. С другой, граф постоянно меняется буквально пока по нему проходишься. Кажется решений проблемы в общем случае нет, но в частных, например только в БД, они есть и работают.
С hold-and-wait тоже интересно. А что если бы процесс сразу же запрашивал все ресурсы, которые ему требуются? Или по крайней мере захватывал бы их постепенно, но при не успехе отпускал бы все уже захваченные. Такой подход, оказывается, тоже убирает проблему дедлоков. К сожалению, не каждый процесс знает наперёд ни все нужные ему ресурсы, ни порядок, в котором они будут захватываться. Опять в общем случае решить тяжело, в частном вполне. Например обычно заранее известно, сколько памяти и CPU нужной новой виртуалке/контейнеру, а это тоже ресурсы, за которые тоже может быть конкуренция. Там правда ситуация не обязательно завершиться дедлоком, а скорее перемещением одного из процессов, но это детали. Раскидывать задачи по компьютерам — очень сложная задача, поэтому в как-нибудь в следующий раз.
Наконец, circular-wait. А пусть все ресурсы должны захватывать только в определенном порядке. К примеру, перечислим все ресурсы в системе от
1 до n. И добавим условие: если процесс захватил ресурс 1 <= k <= n, он может захватить только любой другой ресурс с номером m, где m < k. Другими словами, процессу сначала нужно захватить самый «высокоуровневый» ресурс, а дальше все ресурсы пониже. Наличие нумерованных ресурсов и одного условия на их захватывание также позволяет избавиться от дедлоков. Полный пруф есть в [5].Все эти подходы можно широкими мазками разделить на 3 категории:
- Detection. Подходы, которые по графу ресурсов и их зависимостей позволяют обнаружить дедлок. Для борьбы используется preemption.
- Prevention. Цель: задизайнить систему таким образом, чтобы дедлок был невозможен. Например: пронумеровать ресурсы и ввести условие их захвата. Или запретить захватить ресурс, а затем ждать второй.
- Avoidance. Подходы, при которых система переходит только из одного «безопасного» состояния в другое. Решение о выдаче ресурса принимается динамически, к примеру во время запроса. Самый популярный алгоритм: алгоритм банкира. К сожалению для него необходимо, чтобы процессы сразу знали, какие ресурсы им нужны.
- Алгоритм страуса. Или ничего не делать. Стратегия при которой программист сажается на on-call и ему выдаётся кнопка «отмена».
Ресурсы:
1. E.G. Coffman, M. Elphick, A. Shoshani. System Deadlocks.
2. Richard C. Holt. Some deadlock properties of computer systems.
3. S.S. Isloor, T.A. Marsland. The Deadlock Problem: An Overview.
4. Gertrude Neuman Levine. Defining deadlocks.
5. Charles M. Shub. A Unified Treatment of Deadlock.
🔥24👍4
Классификация масштабируемости (1/2)
Растёкся я тут мыслями по древу про масштабируемость (scalability), и как по разному она себя проявляет. В разных источниках в основном говорят о горизонтальной и вертикальной масштабируемости, но это мне кажется очень поверхностно. Поэтому собрал целый пост с классификацией, правда сегодня никаких ответов, а только вопросы!
Начнём с самой очевидной ситуации — масштабируемость численных показателей. Этот пункт включает не только вертикальную масштабируемость (серверы пухнут), и горизонтальную (серверов становится больше), но и другие интересные проблемы. Раньше IPv4 хватало всем, а теперь приходится выкручиваться. Раньше все наши 3 nginx'а можно было запихнуть в DNS, а теперь тысячи их, не лезут! Некоторые решения ещё можно запихнуть под гребёнку «горизонтальной масштабируемости», как к примеру использование хранилищ, оптимизированных на запись или разделение читателей и писателей. Но некоторые компании делают совершенно безумные вещи!
Зачем фейсбук пропускает запрос через L4+L7 балансировщик, а затем делает это внутри ДЦ ещё раз? [1] Это не просто «горизонтальная масштабируемость». Это совершенно другой уровень борьбы с физическими ограничениями интернета и старых протоколов.
Немного перпендикулярная, но связанная ситуация — оптимизация затрат, важность которой растёт линейно с ростом, собственно, затрат. На уровне стартапа с 5 людьми со стоимостью всей инфраструктуры в пару тысяч в год, экономия 1% процессорного времени скорее всего копейки. Для какого-нибудь гугла 1% — это десятки миллионов долларов.
Далее идёт географическая масштабируемость, которая приносит за собой а) задержки б) прекрасные возможности для оптимизации. К примеру, так ли сервису такси в Москве важно знать, что точно такой же сервис такси доступен в Нью-Йорке? Явно никто в здравом уме на закажет такси из Нью-Йорка в Москву. Значит ли это, что их можно разделить, тем самым упростив поддержку локальных серверов? Ответы совершенно неочевидны. Для бизнес-анализа данные от разных частей одного приложения собрать всё равно придётся, но для непосредственно оперирования системы вроде как нет.
Гео-распределённость приносит за собой ещё одну проблему — необходимость в административной масштабируемости. «Административный» не самое удачное слово с миллионом смыслов, но я его краду из [2], так что оставим как есть. Речь идёт про особенности роста большого бизнеса, оказывающие влияние на техническую составляющую. Например, как в техническом плане происходит поглощение одной компании другой? Каким образом мега-корпорация интегрирует продукты от сотни других компаний, так сказать "at scale"? Здесь есть большая часть юридических вопросов, но от чисто технических проблем всё равно не убежать.
Похожая, но отличающаяся группа проблем — юридические особенности в разных странах. К примеру, каким образом в каждой из стран нужно хранить персональные данные? И как жить с тем, что кассовый чек везде выглядит по-разному? А можно ли делиться этими данными? Ответ на каждый из вопросов влияет и на скорость роста, и на общий вид технических решений. Особенно трудно здесь финансовым и медицинским компаниям, там иерархия проверяющих органов растёт уже несколько веков и следит за каждым чихом. Быстрый «онбординг» новых стран тоже не очень подходит в понятия вертикальной и горизонтальной масштабируемости.
Часть 2 🔽.
Растёкся я тут мыслями по древу про масштабируемость (scalability), и как по разному она себя проявляет. В разных источниках в основном говорят о горизонтальной и вертикальной масштабируемости, но это мне кажется очень поверхностно. Поэтому собрал целый пост с классификацией, правда сегодня никаких ответов, а только вопросы!
Начнём с самой очевидной ситуации — масштабируемость численных показателей. Этот пункт включает не только вертикальную масштабируемость (серверы пухнут), и горизонтальную (серверов становится больше), но и другие интересные проблемы. Раньше IPv4 хватало всем, а теперь приходится выкручиваться. Раньше все наши 3 nginx'а можно было запихнуть в DNS, а теперь тысячи их, не лезут! Некоторые решения ещё можно запихнуть под гребёнку «горизонтальной масштабируемости», как к примеру использование хранилищ, оптимизированных на запись или разделение читателей и писателей. Но некоторые компании делают совершенно безумные вещи!
Зачем фейсбук пропускает запрос через L4+L7 балансировщик, а затем делает это внутри ДЦ ещё раз? [1] Это не просто «горизонтальная масштабируемость». Это совершенно другой уровень борьбы с физическими ограничениями интернета и старых протоколов.
Немного перпендикулярная, но связанная ситуация — оптимизация затрат, важность которой растёт линейно с ростом, собственно, затрат. На уровне стартапа с 5 людьми со стоимостью всей инфраструктуры в пару тысяч в год, экономия 1% процессорного времени скорее всего копейки. Для какого-нибудь гугла 1% — это десятки миллионов долларов.
Далее идёт географическая масштабируемость, которая приносит за собой а) задержки б) прекрасные возможности для оптимизации. К примеру, так ли сервису такси в Москве важно знать, что точно такой же сервис такси доступен в Нью-Йорке? Явно никто в здравом уме на закажет такси из Нью-Йорка в Москву. Значит ли это, что их можно разделить, тем самым упростив поддержку локальных серверов? Ответы совершенно неочевидны. Для бизнес-анализа данные от разных частей одного приложения собрать всё равно придётся, но для непосредственно оперирования системы вроде как нет.
Гео-распределённость приносит за собой ещё одну проблему — необходимость в административной масштабируемости. «Административный» не самое удачное слово с миллионом смыслов, но я его краду из [2], так что оставим как есть. Речь идёт про особенности роста большого бизнеса, оказывающие влияние на техническую составляющую. Например, как в техническом плане происходит поглощение одной компании другой? Каким образом мега-корпорация интегрирует продукты от сотни других компаний, так сказать "at scale"? Здесь есть большая часть юридических вопросов, но от чисто технических проблем всё равно не убежать.
Похожая, но отличающаяся группа проблем — юридические особенности в разных странах. К примеру, каким образом в каждой из стран нужно хранить персональные данные? И как жить с тем, что кассовый чек везде выглядит по-разному? А можно ли делиться этими данными? Ответ на каждый из вопросов влияет и на скорость роста, и на общий вид технических решений. Особенно трудно здесь финансовым и медицинским компаниям, там иерархия проверяющих органов растёт уже несколько веков и следит за каждым чихом. Быстрый «онбординг» новых стран тоже не очень подходит в понятия вертикальной и горизонтальной масштабируемости.
Часть 2 🔽.
🔥4👍3
Классификация масштабируемости (2/2)
Наконец, моё любимое — масштабируемость кадров [3]. Как сделать так, чтобы 10 разработчиков не перессорились, пока пишут вместе код? А когда их станет 100? А как в такой толпе ещё бы сделать так, чтобы а) все придерживались одних стандартов и б) количество коммуникаций росло медленнее, чем линейно? Линейный рост коммуникаций быстро перестаёт работать. Люди не машины, и постоянно общение даже с десятком коллег — уже тяжело. Куда уж там ещё расти. Связанная вещь: какой должен быть процесс найма, чтобы нанять 15 разработчиков за год? А пять тысяч? И насколько они отличаются.
Ещё более интересная проблема: как безопасно менять код, которые пишут несколько тысяч человек в день? А как его разворачивать? Гугл вот для этого живет в самописном монорепозитории, в который несколько десятков тысяч коммитов в месяц делает робот[3]. Но самое-то интересно: другие компании не сильно меньше не живут в монорепе, не пользуются роботами (или скрывают) и нормально себя чувствуют. Кто более прав? 🤷♂️
Отдельная, но связанная тема: как поддерживать продукты, которым уже по 10+ лет? Разработчики редко столько работают в одной компании, т.е. эти знания каким-то образом нужно передавать. В [4] есть глава про это, и про другую интересную тему: deprecation продуктов, ака как правильно выводить что-то из эксплуатации.
Здесь ещё можно было бы припомнить и безопасность, ведь когда становится больше «всего», становится и больше дыр. И R&D с инновациями в больших компаниях. Когда разработка чего-нибудь новенького переходит из разряда «приятный бонус», в необходимое конкурентное преимущество. И про организацию роста сотрудников и стратегий удержания. Как обычно, чем дальше в лес, тем больше дров.
Источники:
1. Usama Naseer, Luca Niccolini, Udip Pant, Alan Frindell, Ranjeeth Dasineni, Theophilus A. Benson Zero Downtime Release: Disruption-free Load Balancing of a Multi-Billion User Website.
2. B. Clifford Neuman. Scale in Distributed Systems.
3. Rachel Potvin Josh Levenberg. Why Google Stores Billions of Lines of Code in a Single Repository
4. Titus Winters, Tom Manshreck, Hyrum Wright. Software Engineering at Google. Не знаю на что дать ссылку, но официальная бесплатная PDFка точно где-то есть.
Ссылка на часть 1.
Наконец, моё любимое — масштабируемость кадров [3]. Как сделать так, чтобы 10 разработчиков не перессорились, пока пишут вместе код? А когда их станет 100? А как в такой толпе ещё бы сделать так, чтобы а) все придерживались одних стандартов и б) количество коммуникаций росло медленнее, чем линейно? Линейный рост коммуникаций быстро перестаёт работать. Люди не машины, и постоянно общение даже с десятком коллег — уже тяжело. Куда уж там ещё расти. Связанная вещь: какой должен быть процесс найма, чтобы нанять 15 разработчиков за год? А пять тысяч? И насколько они отличаются.
Ещё более интересная проблема: как безопасно менять код, которые пишут несколько тысяч человек в день? А как его разворачивать? Гугл вот для этого живет в самописном монорепозитории, в который несколько десятков тысяч коммитов в месяц делает робот[3]. Но самое-то интересно: другие компании не сильно меньше не живут в монорепе, не пользуются роботами (или скрывают) и нормально себя чувствуют. Кто более прав? 🤷♂️
Отдельная, но связанная тема: как поддерживать продукты, которым уже по 10+ лет? Разработчики редко столько работают в одной компании, т.е. эти знания каким-то образом нужно передавать. В [4] есть глава про это, и про другую интересную тему: deprecation продуктов, ака как правильно выводить что-то из эксплуатации.
Здесь ещё можно было бы припомнить и безопасность, ведь когда становится больше «всего», становится и больше дыр. И R&D с инновациями в больших компаниях. Когда разработка чего-нибудь новенького переходит из разряда «приятный бонус», в необходимое конкурентное преимущество. И про организацию роста сотрудников и стратегий удержания. Как обычно, чем дальше в лес, тем больше дров.
Источники:
1. Usama Naseer, Luca Niccolini, Udip Pant, Alan Frindell, Ranjeeth Dasineni, Theophilus A. Benson Zero Downtime Release: Disruption-free Load Balancing of a Multi-Billion User Website.
2. B. Clifford Neuman. Scale in Distributed Systems.
3. Rachel Potvin Josh Levenberg. Why Google Stores Billions of Lines of Code in a Single Repository
4. Titus Winters, Tom Manshreck, Hyrum Wright. Software Engineering at Google. Не знаю на что дать ссылку, но официальная бесплатная PDFка точно где-то есть.
Ссылка на часть 1.
🔥5👍1
Scaling Memcache at Facebook
За последние 4.5 месяца я успел эмигрировать в Дубай, получить лычку Staff Engineer, попасть под сокращение (потерять лычку :D), пройти 22 собеса за 2 недели и найти ещё одну работу. Если кто-то хочет в Дубае найти финтех или перевезти сюда котов, пишите. Я походу стал экспертом.
Когда-то давно у меня был план сделать цикл с разбором работ по алгоритмам консенсуса, но этот план теперь там же где российская экономика и мои нервишки, поэтому ближайшие пару постов будут более расслабленными. А именно: несколько работ о том, как компания X сделала Y. Такие пейперы легко читать, а состоят они в основном из практичных советов и цифр, нежели зубодробительной математики и фундаментальных доказательств. Сегодня поговорю о Scaling Memcache at Facebook от 2013 года (ПДФ на usenix). Формат тоже попроще: больше повествования и меньше тягомотины, но ничего не обещаю. Поехали!
Итак Фейсбук решил доработать и так неплохо работающий memcached, потому что могут. В работе memcached — это сам кеш, а memcache (без d) — кластер, на котором крутятся memcached. Первая интересная находка — использовать UDP для get запросов вместо TCP. Set и delete всё ещё по TCP. Результат: теряются 0.25% запросов, зато -20% latency в среднем. В работе -20% на графике average of medians. Я не очень понимаю этот оборот, но походу это в районе 50% процентиля, потому что медианы...? В любом случае, в 95-ом процентиле прироста почти нет, т.е. быстрее становятся медленные запросы, что хорошо. Правда 0.25% потерь — это всё-таки много, поэтому имплементация оборачивает потерянные пакеты и out-of-order delivery в клиентские ошибки, а клиент там сам уже ретраит. Фейсбуку норм, потому что некоторые страницы делают тысячи обращений в memcache, и сделать пару ретраев недорого.
Кстати о ретраях и ошибках. Фейсбук использует отдельный пул под названием gutter, размером 1% от общего размера memcache для обработки запросов с ошибкой. Клиент делает запрос, не получает ответа (т.е. не cache miss, а прямо Connection Error), далее делает повторный запрос в gutter. Если в gutter данных тоже нет, они добавляются туда. Данные в gutter устаревают быстро, чтобы часто не инвалидировать. Такой подход мало того что защищает от 99% ошибок при доступе к memcache, так ещё и превращает 10-25% промахов в попадания. К тому же дополнительный слой защиты бекенда от запросов. В идеальном мире данные есть в memcahce, если тот умер, то в gutter, только если и в нём нет, запрос идёт в бекенд. Правда трейдофф тоже есть, данные в gutter могут быть старыми. Потому и время жизни записей в gutter короткое.
Ещё пара фишек с удалением и записью. Во-первых, при cache miss, клиенту выдаётся lease token на ~10 секунд (дефолт) на конкретный ID для записи данных. Другие клиенты не могут обновить запись, пока у кого-то есть токен. Это позволяет бороться с циклами запись-перезапись при конкурентном доступе. Во-вторых, lease token помогает в борьбе с thundering herds — ситуации, когда много клиентов пытаются получить один и те же данные. Если клиент пытается получить запись, на которую выпущен lease token, клиент самостоятельно уйдёт в небольшое ожидание и повторит запрос ещё раз, потому что обычно между захватом lease token и самой записью проходит несколько милисекунд. Так запрос не попадёт в cache miss, а вернётся с более долгой задержкой.
Связанная, но немного отдельная проблема — это конкурентные удаления. При удалении, записи какое-то время ещё хранятся в особой структуре данных (какой — не написано) При промахе, клиент может по своему усмотрению либо получить lease token для дальнейшей записи, либо получить устаревшие данные. Опять cache miss превращается в hit, но со старыми данными. Удобно.
В работе есть ещё пара интересных моментов с разогревом нового инстанса из соседнего, а не из бекенда, батчинг запросов, и как решить read-your-writes (спойлер: помечать записи и читать с мастера), но для первого поста за 4.5 месяцев я думаю достаточно.
За последние 4.5 месяца я успел эмигрировать в Дубай, получить лычку Staff Engineer, попасть под сокращение (потерять лычку :D), пройти 22 собеса за 2 недели и найти ещё одну работу. Если кто-то хочет в Дубае найти финтех или перевезти сюда котов, пишите. Я походу стал экспертом.
Когда-то давно у меня был план сделать цикл с разбором работ по алгоритмам консенсуса, но этот план теперь там же где российская экономика и мои нервишки, поэтому ближайшие пару постов будут более расслабленными. А именно: несколько работ о том, как компания X сделала Y. Такие пейперы легко читать, а состоят они в основном из практичных советов и цифр, нежели зубодробительной математики и фундаментальных доказательств. Сегодня поговорю о Scaling Memcache at Facebook от 2013 года (ПДФ на usenix). Формат тоже попроще: больше повествования и меньше тягомотины, но ничего не обещаю. Поехали!
Итак Фейсбук решил доработать и так неплохо работающий memcached, потому что могут. В работе memcached — это сам кеш, а memcache (без d) — кластер, на котором крутятся memcached. Первая интересная находка — использовать UDP для get запросов вместо TCP. Set и delete всё ещё по TCP. Результат: теряются 0.25% запросов, зато -20% latency в среднем. В работе -20% на графике average of medians. Я не очень понимаю этот оборот, но походу это в районе 50% процентиля, потому что медианы...? В любом случае, в 95-ом процентиле прироста почти нет, т.е. быстрее становятся медленные запросы, что хорошо. Правда 0.25% потерь — это всё-таки много, поэтому имплементация оборачивает потерянные пакеты и out-of-order delivery в клиентские ошибки, а клиент там сам уже ретраит. Фейсбуку норм, потому что некоторые страницы делают тысячи обращений в memcache, и сделать пару ретраев недорого.
Кстати о ретраях и ошибках. Фейсбук использует отдельный пул под названием gutter, размером 1% от общего размера memcache для обработки запросов с ошибкой. Клиент делает запрос, не получает ответа (т.е. не cache miss, а прямо Connection Error), далее делает повторный запрос в gutter. Если в gutter данных тоже нет, они добавляются туда. Данные в gutter устаревают быстро, чтобы часто не инвалидировать. Такой подход мало того что защищает от 99% ошибок при доступе к memcache, так ещё и превращает 10-25% промахов в попадания. К тому же дополнительный слой защиты бекенда от запросов. В идеальном мире данные есть в memcahce, если тот умер, то в gutter, только если и в нём нет, запрос идёт в бекенд. Правда трейдофф тоже есть, данные в gutter могут быть старыми. Потому и время жизни записей в gutter короткое.
Ещё пара фишек с удалением и записью. Во-первых, при cache miss, клиенту выдаётся lease token на ~10 секунд (дефолт) на конкретный ID для записи данных. Другие клиенты не могут обновить запись, пока у кого-то есть токен. Это позволяет бороться с циклами запись-перезапись при конкурентном доступе. Во-вторых, lease token помогает в борьбе с thundering herds — ситуации, когда много клиентов пытаются получить один и те же данные. Если клиент пытается получить запись, на которую выпущен lease token, клиент самостоятельно уйдёт в небольшое ожидание и повторит запрос ещё раз, потому что обычно между захватом lease token и самой записью проходит несколько милисекунд. Так запрос не попадёт в cache miss, а вернётся с более долгой задержкой.
Связанная, но немного отдельная проблема — это конкурентные удаления. При удалении, записи какое-то время ещё хранятся в особой структуре данных (какой — не написано) При промахе, клиент может по своему усмотрению либо получить lease token для дальнейшей записи, либо получить устаревшие данные. Опять cache miss превращается в hit, но со старыми данными. Удобно.
В работе есть ещё пара интересных моментов с разогревом нового инстанса из соседнего, а не из бекенда, батчинг запросов, и как решить read-your-writes (спойлер: помечать записи и читать с мастера), но для первого поста за 4.5 месяцев я думаю достаточно.
👍14❤7
Dynamo: Amazon’s Highly Available Key-value Store (1/3)
Сегодня две работы. Первая: та самая статья про Dynamo из 2007 года (pdf), когда она ещё не была частью AWS и не называлась DynamoDB. "Та самая" эта работа, поскольку это редкий случай, когда куча разных идей по построению хранилищ были объединены в единое решение, которое не только работало, но и обеспечивало очень большой сайт. И о котором написали. В те далекие времени Amazon был e-commerce в первую очередь, AWS появился всего годом ранее, а DynamoDB так вообще вошла в сервисы AWS только к 2012 году. Вторая — свежий пирожок, опубликованный буквально пару дней назад спустя 10 лет эксплуатации сервиса DynamoDB (usenix pdf).
При создании Dynamo Amazon исходил из нескольких ограничений:
- key-value. Логика большинства сервисов, к примеру корзины товаров, достаточно простая, и реляционная модель не принесёт значительного успеха.
- Heterogeneity & Symmetry. Все узлы выполняют одну и ту же работу. Но узлы разные, значит они могут выдерживать разную нагрузку.
- Запись всегда должна быть успешной. Пусть и с конфликтами.
- Variable consistency. Клиент сам может выбрать соотношение читателей и писателей в кворуме
Теперь как это всё добро работало.
Поскольку хранилище key-value, этот key надо как-то соотнести с сервером. Нагрузка большая, поэтому кластер периодически придётся расширять. Выход — надёжный как швейцарские часы алгоритм консистентного хэширования (если первый раз — лекция Стэнфорда с подробным объяснением). Но не так всё просто. Во-первых, наивное разделение кольца хешей на кусочка приводит к неравномерной нагрузкой на разные узлы. Вернее не приводит, а не позволяет что-то с ней сделать. Во-вторых, узлы гетерогенные, значит некоторые могут принять больше нагрузки, а некоторые меньше. В-третьих, при падении узла вся нагрузка перейдёт на следующий узел, что может его прикончить.
Решение: виртуальные узлы. Кольцо делится на некоторое количество виртуальных узлов, а каждому физическому узлу присваивается несколько виртуальных. Нодам помощнее — больше, послабее — меньше. При этом виртуальные узлы могут находиться в произвольных точках кольца. Теперь при падении физической ноды, её виртуальные узлы начнут обрабатываться не "следующим" в цепочке, а сразу всеми или почти всеми другими узлами кольца.
Следующая проблема — репликация. При факторе репликации
Сегодня две работы. Первая: та самая статья про Dynamo из 2007 года (pdf), когда она ещё не была частью AWS и не называлась DynamoDB. "Та самая" эта работа, поскольку это редкий случай, когда куча разных идей по построению хранилищ были объединены в единое решение, которое не только работало, но и обеспечивало очень большой сайт. И о котором написали. В те далекие времени Amazon был e-commerce в первую очередь, AWS появился всего годом ранее, а DynamoDB так вообще вошла в сервисы AWS только к 2012 году. Вторая — свежий пирожок, опубликованный буквально пару дней назад спустя 10 лет эксплуатации сервиса DynamoDB (usenix pdf).
При создании Dynamo Amazon исходил из нескольких ограничений:
- key-value. Логика большинства сервисов, к примеру корзины товаров, достаточно простая, и реляционная модель не принесёт значительного успеха.
- Heterogeneity & Symmetry. Все узлы выполняют одну и ту же работу. Но узлы разные, значит они могут выдерживать разную нагрузку.
- Запись всегда должна быть успешной. Пусть и с конфликтами.
- Variable consistency. Клиент сам может выбрать соотношение читателей и писателей в кворуме
R+W > N.Теперь как это всё добро работало.
Поскольку хранилище key-value, этот key надо как-то соотнести с сервером. Нагрузка большая, поэтому кластер периодически придётся расширять. Выход — надёжный как швейцарские часы алгоритм консистентного хэширования (если первый раз — лекция Стэнфорда с подробным объяснением). Но не так всё просто. Во-первых, наивное разделение кольца хешей на кусочка приводит к неравномерной нагрузкой на разные узлы. Вернее не приводит, а не позволяет что-то с ней сделать. Во-вторых, узлы гетерогенные, значит некоторые могут принять больше нагрузки, а некоторые меньше. В-третьих, при падении узла вся нагрузка перейдёт на следующий узел, что может его прикончить.
Решение: виртуальные узлы. Кольцо делится на некоторое количество виртуальных узлов, а каждому физическому узлу присваивается несколько виртуальных. Нодам помощнее — больше, послабее — меньше. При этом виртуальные узлы могут находиться в произвольных точках кольца. Теперь при падении физической ноды, её виртуальные узлы начнут обрабатываться не "следующим" в цепочке, а сразу всеми или почти всеми другими узлами кольца.
Следующая проблема — репликация. При факторе репликации
N, данные нужно забросить ещё на N - 1 ноду. Как их выбрать? Решение гениальное: просто пихать данные на следующие N - 1 нод в кольце 🤷♂️. Если кольцо состоит из A -> B -> C -> D -> A, и фактор репликации 3, то данные на узле A также будут на узлах B и C. С виртуальными узлами чуть сложнее, потому что нет гарантии что следующий виртуальный узел находится на другой физической машине. Но это решается чуть более умным программированием.👍11
Dynamo: Amazon’s Highly Available Key-value Store (2/3)
Теперь к главное проблеме: как сделать так, чтобы запись всегда была успешной? Несколько трюков. Во-первых, при факторе репликации
Во-вторых, для записи используется vector clock. Каждая запись содержит не только данные, но также номер сервера и значение монотонно растущего счётчика. Сервер, который записывает данные, обновит значение счётчика или добавит в список новый счётчик. По двум таким записям очевидно, можно ли автоматически разрешить конфликт. К примеру, если есть две записи
И здесь ребята из Amazon поднимают довольно важную проблему. База данных редко знает семантику данных. Для базы это всё просто наборы бит, поэтому на уровне хранилищ редко можно получить хорошую тактику разрешения конфликтов. Можно использовать к примеру
Решение: отдавать конфликты клиенту! Уж программист то точно знает, что две конфликтные записи семантически — это две версии корзины товаров, с вполне понятным алгоритмом их слияния. Немного странно из
Теперь про failure detection и добавление новых нод в кластер. На момент 2007 года у Amazon каждый отдельный сервис сам поднимает и менеджерит кластер Dynamo. Это значит, что общее количество узлов обычно измеряется в сотнях, а не тысячах. В такой ситуации при добавлении новых узлов, хорошо себя показывают gossip алгоритмы (Про gossip писал здесь). Каждый узел самостоятельно строит картину кластера как раз основываясь на сообщениях, полученных по gossip. Интересный момент: тот же gossip не используется для передачи информации о неработающих узлах. Т.е. у Dynamo фактически отсутствовал global failure detection механизм, ноды содержали информацию о неработающих узлах локально (не можешь достучаться — не работает), но не делились ей. Одна из причин: операции по добавлению и удалению узлов делаются в ручном режиме, такой алгоритм просто не нужен.
Ещё немного про антиэнтропию для поддержания одинакового состояния реплик. Чтобы не передавать кучу данных по репликам, используются Merkle Trees. Это такой хитрое дерево у которого хеш родителя состоит из хешей детей. Если хеш в корне двух деревьев одинаковый — оба дерева одинаковые. Если нет — можно сравнить хеши детей, чтобы быстрее обнаружить, в какой из частей поддерева есть расхождение.
И последний интересный кусочек про перформанс. Задержка при записи в кворум из
Теперь к главное проблеме: как сделать так, чтобы запись всегда была успешной? Несколько трюков. Во-первых, при факторе репликации
N, каждый узел содержит preference list, состоящий из более чем N узлов. В идеальном мире, запись будет идти в один из N узлов, но если какая-то часть из них недоступна, запись пойдёт через hinted handoff в другой узел. Hinted Handoff — это такая особая запись на "чужую" ноду с хинтом. Допустим, если обычно запись идёт в A, B, C, но запись нужно сделать в D, можно сообщить узлу "вот данные, но они вообще были для узла A`". Тогда `D сохранит данные, но также будет периодически проверять, не поднялся ли A. Как только это произойдёт, D перельёт свои данные в A, где им и место, и удалит их у себя.Во-вторых, для записи используется vector clock. Каждая запись содержит не только данные, но также номер сервера и значение монотонно растущего счётчика. Сервер, который записывает данные, обновит значение счётчика или добавит в список новый счётчик. По двум таким записям очевидно, можно ли автоматически разрешить конфликт. К примеру, если есть две записи
(node: 1, version: 5) и (node: 1, version: 2), очевидно, что вторую можно отбросить, так как она старая, по сравнению с новой записью того же сервера. Именно таким образом работает read repair в Dynamo. Когда при чтении какой-то из узлов возвращает устарелые данные, они обновляются. Но если запись будет (node: 1, version: 4) и (node: 2, version: 5), просто разрешение конфликтов уже не сработает. И здесь ребята из Amazon поднимают довольно важную проблему. База данных редко знает семантику данных. Для базы это всё просто наборы бит, поэтому на уровне хранилищ редко можно получить хорошую тактику разрешения конфликтов. Можно использовать к примеру
last-write-wins. Для некоторых видов данных можно даже наворотить CRDT, которые не факт что разрешат конфликт семантически правильно, но точно разрешат его одинаковым образом на всех нодах. Решение: отдавать конфликты клиенту! Уж программист то точно знает, что две конфликтные записи семантически — это две версии корзины товаров, с вполне понятным алгоритмом их слияния. Немного странно из
get запроса получить гипотетически 2 версии данных, зато конфликты будут разрешаться правильно.Теперь про failure detection и добавление новых нод в кластер. На момент 2007 года у Amazon каждый отдельный сервис сам поднимает и менеджерит кластер Dynamo. Это значит, что общее количество узлов обычно измеряется в сотнях, а не тысячах. В такой ситуации при добавлении новых узлов, хорошо себя показывают gossip алгоритмы (Про gossip писал здесь). Каждый узел самостоятельно строит картину кластера как раз основываясь на сообщениях, полученных по gossip. Интересный момент: тот же gossip не используется для передачи информации о неработающих узлах. Т.е. у Dynamo фактически отсутствовал global failure detection механизм, ноды содержали информацию о неработающих узлах локально (не можешь достучаться — не работает), но не делились ей. Одна из причин: операции по добавлению и удалению узлов делаются в ручном режиме, такой алгоритм просто не нужен.
Ещё немного про антиэнтропию для поддержания одинакового состояния реплик. Чтобы не передавать кучу данных по репликам, используются Merkle Trees. Это такой хитрое дерево у которого хеш родителя состоит из хешей детей. Если хеш в корне двух деревьев одинаковый — оба дерева одинаковые. Если нет — можно сравнить хеши детей, чтобы быстрее обнаружить, в какой из частей поддерева есть расхождение.
И последний интересный кусочек про перформанс. Задержка при записи в кворум из
W узлов определяется задержкой самого медленного узла. А что если при записи в W узлом писать только в буфер в памяти, а в остальные N-W узлов писать в диск? Тогда задержки меньше, при этом запись скорее всего будет в других узлах. Этот хак позволяет Dynamo в 5 раз уменьшить задержки в 99.9 процентиле в пиковые загрузки.👍8
Dynamo: Amazon’s Highly Available Key-value Store (3/3)
Совсем чуть-чуть про работу 2022 года (usenix pdf).
Что не сработало: Symetry подход с preference list. В Dynamo запись может быть через любую из
Интересная особенность произошла с hinted handoff. Не написано что он НЕ используется, но появился новый хак. Обычно узел хранит данные в виде B-Tree + WAL с лидера. Хак: Лидер может создать специальную Log replica — ноду, в которой только WAL. Такой узел создать быстрее, так как с ним не нужно делиться всем B-Tree, при этом он помогает со сбором кворума.
Что всё ещё работает: ноды и раньше не поддерживали global failure state. Но теперь есть Paxos, в котором ноды сами могут начинать голосование за нового лидера, если посчитают, что старый умер. Иногда ноде только кажется, что лидер умер, что редко, но приводит к ошибкам с двумя лидерами. Такое поведение называют gray failure. Причины: лагающая сеть, медленные машины, да куча всего. Чтобы этого избежать, узел сначала опрашивает соседей, чтобы узнать у них, видят ли они мастера. Если соседи мастера видят, то нода отказывается от идеи попытаться начать раунд голосования за нового лидера.
Что совершенно отвал башки: для проверки алгоритма репликации, как и для S3, в DynamoDB Amazon использует TLA+ модели. Говорят, что парочку неочевидных багов они уже нашли. Кстати, если хотите познакомиться в TLA+ поближе, тут недавно запустился https://learntla.com!
Совсем чуть-чуть про работу 2022 года (usenix pdf).
Что не сработало: Symetry подход с preference list. В Dynamo запись может быть через любую из
N нод. Выбираться скорее всего будет первая, но не обязательно. В DynamoDB реплики выбирают лидера через Multi-Paxos и только он отвечает за запись и за "консистентные" чтения. Лидер же записывает WAL и реплицирует его follower-узлам.Интересная особенность произошла с hinted handoff. Не написано что он НЕ используется, но появился новый хак. Обычно узел хранит данные в виде B-Tree + WAL с лидера. Хак: Лидер может создать специальную Log replica — ноду, в которой только WAL. Такой узел создать быстрее, так как с ним не нужно делиться всем B-Tree, при этом он помогает со сбором кворума.
Что всё ещё работает: ноды и раньше не поддерживали global failure state. Но теперь есть Paxos, в котором ноды сами могут начинать голосование за нового лидера, если посчитают, что старый умер. Иногда ноде только кажется, что лидер умер, что редко, но приводит к ошибкам с двумя лидерами. Такое поведение называют gray failure. Причины: лагающая сеть, медленные машины, да куча всего. Чтобы этого избежать, узел сначала опрашивает соседей, чтобы узнать у них, видят ли они мастера. Если соседи мастера видят, то нода отказывается от идеи попытаться начать раунд голосования за нового лидера.
Что совершенно отвал башки: для проверки алгоритма репликации, как и для S3, в DynamoDB Amazon использует TLA+ модели. Говорят, что парочку неочевидных багов они уже нашли. Кстати, если хотите познакомиться в TLA+ поближе, тут недавно запустился https://learntla.com!
👍15🔥2
No updates update
Я одинаково плохо пишут и вступления, и введения, тем более спустя полгода молчания, поэтому краткая выжимка из моей жизни за последний год:
- Нашел работу Staff инженером и переехал в Дубай в маленький стартап,
- Попал под сокращение 30% сотрудников через 2.5 месяца. Fun fact: за два дня до сокращения согласовал с CTO + CPO "стратегию" на следующий год. Плохая была стратегия 🤔
- Нашел новую работу за 10 дней, но снова Senior, потому что стремно в панике запускать ещё один поиск на Staff позицию,
- За следующие полгода прочитал 0 "технических" книг, 0 пейперов, сбил режим сна, прошёл десятка два игр, начал носить очки, восстановил режим сна...
И вот только сейчас почувствовал силы что-то снова писать. Всем привет! План такой:
Во-первых, никаких "публичных обещаний" и графиков постов, которые я ни разу и не соблюдал. Я понял что физически не могу повторять за principle инженерами из Google и AWS с двумя лонгридами в неделю.
Во-вторых, разборы статей. Мне очень нравится как получились разборы целых тем, как в случае с CAP теоремой, дедлоками или Gossip'ом. Сам даже перечитываю. Но понять одну статью — это квест на 6-20 часов, а писать такие посты даже тяжелее, чем читать. Они будут, но скорее всего поменьше.
В-третьих, хочу начать ковырять базы данных руками. В этой печальной экономике звучит как очень плохой план, но я не закончил даже с четвертью своей карьеры (10/10 пост), поэтому времени ещё вагон. Готов убить на потенциальную ошибку пару лет. Плюсы: будет интересно. Минусы: может быть не будет.
В-четвертых, у меня скопился беклог из десятка постов на отвлечённые темы от отзывов на нетехнические книжки до баек про внедрение Observability. Так что легкое чтение снова вернётся. Ура.
Я одинаково плохо пишут и вступления, и введения, тем более спустя полгода молчания, поэтому краткая выжимка из моей жизни за последний год:
- Нашел работу Staff инженером и переехал в Дубай в маленький стартап,
- Попал под сокращение 30% сотрудников через 2.5 месяца. Fun fact: за два дня до сокращения согласовал с CTO + CPO "стратегию" на следующий год. Плохая была стратегия 🤔
- Нашел новую работу за 10 дней, но снова Senior, потому что стремно в панике запускать ещё один поиск на Staff позицию,
- За следующие полгода прочитал 0 "технических" книг, 0 пейперов, сбил режим сна, прошёл десятка два игр, начал носить очки, восстановил режим сна...
И вот только сейчас почувствовал силы что-то снова писать. Всем привет! План такой:
Во-первых, никаких "публичных обещаний" и графиков постов, которые я ни разу и не соблюдал. Я понял что физически не могу повторять за principle инженерами из Google и AWS с двумя лонгридами в неделю.
Во-вторых, разборы статей. Мне очень нравится как получились разборы целых тем, как в случае с CAP теоремой, дедлоками или Gossip'ом. Сам даже перечитываю. Но понять одну статью — это квест на 6-20 часов, а писать такие посты даже тяжелее, чем читать. Они будут, но скорее всего поменьше.
В-третьих, хочу начать ковырять базы данных руками. В этой печальной экономике звучит как очень плохой план, но я не закончил даже с четвертью своей карьеры (10/10 пост), поэтому времени ещё вагон. Готов убить на потенциальную ошибку пару лет. Плюсы: будет интересно. Минусы: может быть не будет.
В-четвертых, у меня скопился беклог из десятка постов на отвлечённые темы от отзывов на нетехнические книжки до баек про внедрение Observability. Так что легкое чтение снова вернётся. Ура.
👍39❤19🐳2👏1
Observability (1/2)
Последний месяц я внедряю в компании Observability с DataDog взамен старых добрых Prometheus & Grafana. Пока команды в восторге, MTTD (mean time to detect) упал с часов до минут, operations теперь могут заниматься планами на будущее, а не тушением пожаров. Сплошной успех, поэтому расскажу что это такое, и почему классические дашборды должны умереть.
Observability — это способность понимать поведение системы при взгляде снаружи, без знания того, как система работает изнутри. Система с observability позволяет разобраться в причинах неизвестных ранее проблем (unknown unknown). Она предоставляет достаточно данных, чтобы ответить на вопрос "что происходит?" на любом уровне: от единичного запроса до региона или системы в целом. И самое главное — она уже хранит все необходимые данные. Если вам нужно сделать ещё 1 дашборд или дописать кусочек кода, чтобы собрать ещё одну метрику — это не observability.
Основной инструмент observability это распределённые трейсы , но с несколькими особенностями, в основном приходящимися на хранилище для этих трейсов.
Поддержка high-cardinality. Cardinality — это мощность. Как мощность множества. High-cardinality — данные, у которых может быть много возможных значений. Например уникальный ID пользователя имеет максимальную cardinality, потому что он никогда не повториться для другого пользователя. Год заказа — низкую cardinality, потому что он всегда одинаковый в течение года.
Трейс и хранилище трейсов должны содержать не только базовую информацию, такую как ID сервера, регион, время исполнения, тип запроса, но также и специфичные для конкретного запроса и конкретной команды (разработки) параметры: ID пользователя, номер заказа, дата регистрации, идентификатор сессии и т.д. Именно high-cardinality данные позволяют отвечать на специфичные запросы, например "какая средняя latency на запросы в Германии для серверов, развернутых на AMI amzn2-ami-hvm-x86_64-gp2 для пользователей, делающих первый заказ?". Ответ на такие вопросы без изменения системы — вся суть observability.
Поддержка high-dimensionality. Каждый трейс может быть уникальным, поскольку он содержит набор параметров специфичный только для него. Для одной команды важен один набор параметров, для второй — другой. High-dimensionality это способность хранить и эффективно анализировать произвольный, меняющийся набор данных. Система должна одинаково хорошо справляться и с трейсом в 10 полей, и с трейсом в 100 полей. Более того, каждое из этих полей может ещё иметь и high-cardinality.
Быстрый поиск. Observability нужна чтобы отвечать на вопросы здесь и сейчас. Это значит: 1) данные должны быстро попадать в систему, 2) данные можно быстро вытащить из системы, что сложно, из-за high-dimensionality и high-cardinality, но 3) данные довольно быстро теряют полезность, а значит их довольно скоро можно удалить. Главная полезность в том, чтобы решить текущую проблему, а не анализировать трейсы спустя год или два, хотя технически такое тоже возможно.
Последний месяц я внедряю в компании Observability с DataDog взамен старых добрых Prometheus & Grafana. Пока команды в восторге, MTTD (mean time to detect) упал с часов до минут, operations теперь могут заниматься планами на будущее, а не тушением пожаров. Сплошной успех, поэтому расскажу что это такое, и почему классические дашборды должны умереть.
Observability — это способность понимать поведение системы при взгляде снаружи, без знания того, как система работает изнутри. Система с observability позволяет разобраться в причинах неизвестных ранее проблем (unknown unknown). Она предоставляет достаточно данных, чтобы ответить на вопрос "что происходит?" на любом уровне: от единичного запроса до региона или системы в целом. И самое главное — она уже хранит все необходимые данные. Если вам нужно сделать ещё 1 дашборд или дописать кусочек кода, чтобы собрать ещё одну метрику — это не observability.
Основной инструмент observability это распределённые трейсы , но с несколькими особенностями, в основном приходящимися на хранилище для этих трейсов.
Поддержка high-cardinality. Cardinality — это мощность. Как мощность множества. High-cardinality — данные, у которых может быть много возможных значений. Например уникальный ID пользователя имеет максимальную cardinality, потому что он никогда не повториться для другого пользователя. Год заказа — низкую cardinality, потому что он всегда одинаковый в течение года.
Трейс и хранилище трейсов должны содержать не только базовую информацию, такую как ID сервера, регион, время исполнения, тип запроса, но также и специфичные для конкретного запроса и конкретной команды (разработки) параметры: ID пользователя, номер заказа, дата регистрации, идентификатор сессии и т.д. Именно high-cardinality данные позволяют отвечать на специфичные запросы, например "какая средняя latency на запросы в Германии для серверов, развернутых на AMI amzn2-ami-hvm-x86_64-gp2 для пользователей, делающих первый заказ?". Ответ на такие вопросы без изменения системы — вся суть observability.
Поддержка high-dimensionality. Каждый трейс может быть уникальным, поскольку он содержит набор параметров специфичный только для него. Для одной команды важен один набор параметров, для второй — другой. High-dimensionality это способность хранить и эффективно анализировать произвольный, меняющийся набор данных. Система должна одинаково хорошо справляться и с трейсом в 10 полей, и с трейсом в 100 полей. Более того, каждое из этих полей может ещё иметь и high-cardinality.
Быстрый поиск. Observability нужна чтобы отвечать на вопросы здесь и сейчас. Это значит: 1) данные должны быстро попадать в систему, 2) данные можно быстро вытащить из системы, что сложно, из-за high-dimensionality и high-cardinality, но 3) данные довольно быстро теряют полезность, а значит их довольно скоро можно удалить. Главная полезность в том, чтобы решить текущую проблему, а не анализировать трейсы спустя год или два, хотя технически такое тоже возможно.
❤8🔥3👍2
Observability (2/2)
Цель быстрого поиска в поддержке core analysis loop — подхода к решению "любой" проблемы:
0. Получить алерт или другое оповещение о том, что что-то идёт не так
1. Посмотреть визуализацию трейсов: есть ли явные outliners? Прослеживается ли корреляция между разными данными?
2. Просмотреть данные по разным dimensions, к примеру сгруппировать по статусу запроса, региону, номеру дата центра. Цель: убрать максимум данных, чтобы outliners всё ещё были видны, другими словами — сузить область в которой скорее всего находится проблема.
3. Применить фильтр из 2 и вернуться к пункту 1. Повторять до того, как проблема не станет очевидной. Для примера: на первом шаге смотреть на весь регион, на второй на одну availability zone, на один ДЦ, на одну стойку в ДЦ и т.д.
Чтобы получить мощный инструмент Observability, достаточно просто устроиться в Meta или Google, которые свой шарманки сделали лет эдак 10 назад. Для всех остальных есть вендоры: Honeycomb, Datadog, Lightstep и пр. Но можно начать играться и бесплатно через OpenTelemetry — это стандарт, которому пытаются соответствовать все вендоры с разной степенью успеха. Первый шаг — инструментация кода, чтобы начать собирать трейсы + отправлять их куда-то. Второй — добавление кастомных business-specific тегов или целых трейсов. Технически всё достаточно просто, а про "социальную" сторону я расскажу в следующий раз.
Источники:
- Observability Primer от OpenTelemetry
- Charity Majors, Liz Fong-Jones, George Miranda Observability Engineering
Цель быстрого поиска в поддержке core analysis loop — подхода к решению "любой" проблемы:
0. Получить алерт или другое оповещение о том, что что-то идёт не так
1. Посмотреть визуализацию трейсов: есть ли явные outliners? Прослеживается ли корреляция между разными данными?
2. Просмотреть данные по разным dimensions, к примеру сгруппировать по статусу запроса, региону, номеру дата центра. Цель: убрать максимум данных, чтобы outliners всё ещё были видны, другими словами — сузить область в которой скорее всего находится проблема.
3. Применить фильтр из 2 и вернуться к пункту 1. Повторять до того, как проблема не станет очевидной. Для примера: на первом шаге смотреть на весь регион, на второй на одну availability zone, на один ДЦ, на одну стойку в ДЦ и т.д.
Чтобы получить мощный инструмент Observability, достаточно просто устроиться в Meta или Google, которые свой шарманки сделали лет эдак 10 назад. Для всех остальных есть вендоры: Honeycomb, Datadog, Lightstep и пр. Но можно начать играться и бесплатно через OpenTelemetry — это стандарт, которому пытаются соответствовать все вендоры с разной степенью успеха. Первый шаг — инструментация кода, чтобы начать собирать трейсы + отправлять их куда-то. Второй — добавление кастомных business-specific тегов или целых трейсов. Технически всё достаточно просто, а про "социальную" сторону я расскажу в следующий раз.
Источники:
- Observability Primer от OpenTelemetry
- Charity Majors, Liz Fong-Jones, George Miranda Observability Engineering
👍7❤6
Вспомнил, что у меня завалялся substack и начал потихоньку переводить в него свои посты из внутреннего блога компании, скопившиеся за последний год. Первый просто — что вообще такое ваши эти Service Level Indicators.
https://open.substack.com/pub/sharkinit/p/service-level-what-part-1-defining?r=36tm5&utm_campaign=post&utm_medium=web
Параллельно, я кажется стал немного понимать, почему сложно что-либо менять одновременно в 5-6 инженерных командах, но пока не совсем понимаю, как это переложить в слова. Что-нибудь придумаю :D
https://open.substack.com/pub/sharkinit/p/service-level-what-part-1-defining?r=36tm5&utm_campaign=post&utm_medium=web
Параллельно, я кажется стал немного понимать, почему сложно что-либо менять одновременно в 5-6 инженерных командах, но пока не совсем понимаю, как это переложить в слова. Что-нибудь придумаю :D
👍8🔥3