Комментарии 24
источник не прошел проверку отказостойчивости Internal Server Error
возможно, источник не был готов к хабрэффекту
Во-первых, в статье неправильная ссылка на источник. Правильная: https://volodymyrpotiichuk.com/blog/articles/the-architecture-behind-99%25-uptime
А в статье указана https://volodymyrpotiichuk.com/blog/articles/the-architecture-behind-99%2525-uptime
А, во-вторых, крайне сомнительно, что кто-то личный блог запускает на Erlang. Такой тип сайтов традиционно разруливает нагрузку через кеширование статических страниц.
Эрланг и Эликсир это самое прекрасное что я видел в области разработки по.
Строго говоря, автор не ответил на вопрос, который сам поставил: почему Erlang до сих пор король отказоустойчивых систем. Видимо, потому, что изначально ставилась задача - обеспечить постоянно работающие системы (тогда было с прицелом на телекоммуникацию) и потому, что задача была хорошо продумана теоретически. Можно посмотреть диссертацию Джо Армстронга, где всё по полочкам: почему изолированные процессы и обмен сообщениями, почему нужна виртуальная машина, почему "Let it crash", почему язык функциональный...
Так же потому, что то, что должно упасть/подняться (основной концепт OTP), должно сделать это максимально быстро, с восстановлением стейта и коммуникаций.
А разве в Erlang/OTP есть какой-то универсальный механизм сохранения стейта? Возьмём gen_statem. Чтобы полностью восстановить работу процесса, надо сохранить не только специальный терм (который колбеки состояний получают в последнем аргументе), но ещё и собственно состояние машины состояний, а также состояние таймаутов. Ещё надо учесть, что могут быть отложенные (postpone) события. Как это всё сохранить, если мы состояние таймаута даже узнать не можем?
Всё верно - его нет. А нет его потому-что в большинстве случаев это кастомный функционал, необходимый на месте и реализовывать его универсальным просто не нужно. А в OTP же это сделать максимально просто с помощью ETS, cb terminate, cb init и конечно try/catch. OTP даёт тебе все инструменты для этого и каркас (supervisor/gen_server/...). Остальное сам)
ну т.е. ровно то, что делают с помощью микросервисной архитектуры и оркестратора. Только при этом можно пользоваться языком с нормальной статической типизации без всякого позора типа is_integer/1 и который не будет в 6-10 раз проигрывать по производительности CPU-bound и требовать х3 памяти.
Если вы только из-за того, что бы реализовать let it crash, будете притягивать микросервисную архитектуру, оркестратор и тд, то это очевидно попахивает архитектурной астронавтикой. Есть инструмент - он хорош для своих задач. Сравнивать готовый инструмент с какой-то там архитектурой, подходящей только для проектов с дикими нагрузками - я бы точно не стал.
По факту OTP готовый инструмент только для исчезающе малого класса задач типа телекома, буквально шаг в сторону и оказывается что для асинхроного мессаджинга нужны гарантии доставки, так что мейлбокс "процесса" из коробки не подходит, для базы нужна транзационность без глобального лока, так что ETS/DETS/Mnesia заменяются на полноценные базы типа redis/postgres/cockroach/cassandra, RPC требует схемы и версионированости, а оркестрация кластера разных стратегий деплоймента, сайдкаров и стореджей. В результате OTP теряет всю свою привлекательность как готового инструмента. А сам по себе эрланг ничего интересного не может предложить кроме иммутабильности, в жертву которой он приносит производительность настолько, что реальный проекты на нем забиты NIF'ами, которые в любой момент могут уронить весь BEAM целиком по сегфолту(это к слову о надежности). Да, 25 лет назад, когда ничего из перечисленных продуктов не было, OTP выглядел хорошо, по-этому на нем написали тот же ejabberd(из-за чего WhatsApp и сидит до сих с эрлангом), но сейчас erlang/elixir/OTP это не более чем анахронизм.
Вас послушать, так можно подумать, что все живут в SAAS-ах, вебе и других продуктах, "варящихся" в своих инфраструктурах. Не забывайте про "коробку" или embedded например - туда вы не втащите всё это барахло, а программирование - далеко не всегда полная свобода действий. В чем-то я с вами соглашусь - та же гарантия доставки должна быть реализована отдельно, если нужно, да и то в случае кластерных многонодовых решений. В остальном - это инструмент для своих задач и называть его анахронизмом я бы не стал.
В такой ситуации, например, дойдя до числа 5000, мы сохраняем текущий стек вызовов и позицию, на которой остановились, а также промежуточные данные в куче процесса — и переключаемся на следующий процесс!
И, таким образом, делаем то же самое, что делает реальный процессор со своими потоками и процессами, когда переключается с одного на другой.
Тут встает вопрос, почему механизм, встроенный в язык, получается эффективнее, чем тот, что встроен в OS.
Потому что BEAM использует потоки ОС и работает над ними, используя асинхронный подход в каждом потоке и довольно сложный шедулер, реализуя аналог корутин + недавно, вроде как, стал более стабилен JIT BEAM
BEAM реализует вытесняющую многозадачность для кода на Эрланге (и кооперативную внутри функций на Си). Общение между процессами Эрланга (не путать с процессами ОС) асинхронное.
Переключение контекста внутри происходит быстрее, нагрузка, благодаря вытесняющей многозадачности и алгоритмам шедулеров (их три типа), распределяется равномернее.
BEAM умеет перераспределять нагрузку с более загруженных шедулеров на менее загруженные и т.д. и т. п.
В Erlang настройки вроде того, сколько вызовов функций процесс может выполнить до того, как планировщик переключится на другую задачу, а также прочая конфигурация, хранятся в том самом блоке управления процессом (Process Control Block), о котором я говорил выше.
Как заранее просчитать это количество вызовов?
На нём ещё что-то пишут?
Спасибо за публикацию, читал на эту тему доклад, но там был Elixir — Эволюция файловых систем: от Web2 к Web3 — построение отказоустойчивых децентрализованных хранилищ данных
Просто оставлю это тут, вдруг, кто-то хочет погрузиться и посмотреть еще чуть шире
Почему Erlang до сих пор король отказоустойчивых систем