Отказоустойчивость

Отказоустойчивость — свойство Системы сохранять свою работоспособность в любых ситуациях: в случае тех или иных программных ошибок, отказа одной или нескольких составных частей, необходимости обновиться и др. То же: fault tolerance.

С самого начала Erlang/OTP проектировался для создания безупречно отказоустойчивых систем. Эти системы могут быть очень сложными: включать десятки, сотни, тысячи узлов (нод), выполняющих код, измеряющийся десятками и сотнями тысяч, миллионами строк. Это может быть, например, какой-нибудь телекоммуникационный сервис или “умная фабрика”.

Чем сложнее Система, тем чаще будут случаться разного рода неурядицы. Если увеличение числа неурядиц в единицу времени будет приводить к учащению критических ситуаций, рано или поздно мы упрёмся в потолок, когда дальнейшее развитие Системы перестанет быть рентабельным. В этом и заключается ключевая ценность Эрланга — возможность бесконечного развития Системы.

Трудно, на самом деле, классифицировать все возможные неурядицы, которые могут повлиять на работоспособность Системы. Попробуем привести более-менее типичные примеры.

Как видно, проблем, больших и маленьких, может быть много. Наш долг, конечно, заранее побеспокоиться, продумать профилактику неприятностей. Но стоит признать, мы никогда не сможем построить идеальную Систему, которая никогда не ломается и на все неурядицы у неё готово штатное решение. Стоит признать, что ошибки и прочие неурядицы были, есть и будут.

Какой из этого следует вывод? Очевидно, надо минимизировать ущерб от каждой неурядицы. Проблема должна быть локализована во времени и пространстве, так сказать. Она должна быть исправлена в кратчайшие сроки, а вред должен коснуться лишь избранных акторов. Большинство других акторов, насколько это возможно, должны остаться на плаву и выполнять свои задания, как будто ничего не случилось.

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

Эрланг, именно как язык, обладает некоторыми приятными возможностями, повышающими отказоустойчивость. Так, в нём используется целочисленная арифметика, и можно не беспокоиться о переполнении разряда числа. Используя хвостовую рекурсию, можно не переживать о переполнении стека. Но главное всё же то, как в философии Erlang/OTP сформулированы основные принципы отказоустойчивости.

Гениальный Джо Армстронг сформулировал их с помощью двух фраз, которые легко запомнить:

Процессы (акторы) имеют две роли: работники и надзиратели. Первые — “рабочие лошадки”, они создают для нас какой-либо полезный продукт (например, опрашивают датчик и пересылают данные тем акторам, кому эти сведения нужны). Их код элегантен и изящен — и без проверок на ошибки. Если работник — увы! — погибает, об этом станет известно надзирателю. Включая то, что будет известна и причина смерти работника.

Специально обученный актор-надзиратель попробует самостоятельно исправить проблему. Он может, например, заспаунить новое задание с новыми аргументами. Если проблема никак не решается, дело доходит до человека, который анализирует проблему, меняет код актора-работника, компилирует его и загружает в Систему (“горячая замена”). В общих чертах так выглядит универсальный рецепт отказоустойчивости в Erlang/OTP.


Copyright © 2025 Алексей Карманов