Обновить

Почему Erlang до сих пор король отказоустойчивых систем

Уровень сложностиСредний
Время на прочтение12 мин
Охват и читатели11K
Всего голосов 25: ↑23 и ↓2+24
Комментарии24

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

Для эмбеддет можно взять если не раст, то тот же го, где тоже будет gc, зелёные потоки с каналами и preemptive concurrency, но со статической типизацией и без виртуальной машины, что даёт в разы большую производительность.

В такой ситуации, например, дойдя до числа 5000, мы сохраняем текущий стек вызовов и позицию, на которой остановились, а также промежуточные данные в куче процесса — и переключаемся на следующий процесс! 

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

Тут встает вопрос, почему механизм, встроенный в язык, получается эффективнее, чем тот, что встроен в OS.

Потому что BEAM использует потоки ОС и работает над ними, используя асинхронный подход в каждом потоке и довольно сложный шедулер, реализуя аналог корутин + недавно, вроде как, стал более стабилен JIT BEAM

BEAM реализует вытесняющую многозадачность для кода на Эрланге (и кооперативную внутри функций на Си). Общение между процессами Эрланга (не путать с процессами ОС) асинхронное.

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

BEAM умеет перераспределять нагрузку с более загруженных шедулеров на менее загруженные и т.д. и т. п.

В Erlang настройки вроде того, сколько вызовов функций процесс может выполнить до того, как планировщик переключится на другую задачу, а также прочая конфигурация, хранятся в том самом блоке управления процессом (Process Control Block), о котором я говорил выше.

Как заранее просчитать это количество вызовов?

Это известное число, оно равно 4000 редукций, одна редукция это грубо один вызов функции.

А как подобрано это число? Из того что я почитал, раньше оно равнялось 1000 например.

Разработчики BEAM считают его оптимальным, можно поменять, но не рекомендовано.

На нём ещё что-то пишут?

Пишут, но говорят об этом редко.

Раньше много писал на нём, но сейчас будто на других языках проще

Спасибо за публикацию, читал на эту тему доклад, но там был Elixir — Эволюция файловых систем: от Web2 к Web3 — построение отказоустойчивых децентрализованных хранилищ данных

Просто оставлю это тут, вдруг, кто-то хочет погрузиться и посмотреть еще чуть шире

Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Информация

Сайт
otus.ru
Дата регистрации
Дата основания
Численность
101–200 человек
Местоположение
Россия
Представитель
OTUS