-
Notifications
You must be signed in to change notification settings - Fork 0
Distributed System Model
Создать модель (уже существующей) распределенной системы, которая дает возможность точного (пусть неэффективного) моделирования поведения, опуская несущественные детали.
Система состоит из управляемых и неуправляемых акторов, которые изображают элементы системы, состояние и внешний мир. Те акторы, состоянием/устройством которых мы можем управлять, расписываются на лог сообщений/эффектов (дуальный состоянию), и правила обработки сообщений (возможно, черные ящики). Абсолютного времени нету, моделиируются причинно-следственные связи между сообщениями, которые иногда привязываются к внешним датчикам реального времени.
Сущность, с которой можно взаимодействовать, посылая ей сообщения. Физически может быть устроена произвольным образом, логически состоит из состояния (которое включает в себя правила обработки сообщений). Акторы - номинальны (т.е. различимы исключительно по идентификатору)
Актор, находящийся внутри Окружения, состоянием которого мы можем управлять (сохранять, восстанавливать, копировать, изменять). Копия состояния Актора не является актором. Идентичность управляемого актора контролируется его окружением.
Значение, передаваемое между акторами. Все сообщения в данной модели асинхронны, но могут быть отдельно связаны причинно-следственными связями.
Момент времени в жизни отдельного актора. Мы считаем, что акторы детерминистичны и локальны, поэтому в пределах одного актора моменты строго упорядочены, а в пределах нескольких - только если есть соответствующие причинно-следственные связи.
Мир, в котором существует конкретный Актор. Акторы взаимодействуют с Окружением как с любым другим Актором, Окружение взаимодействует с ними произвольным образом. Окружение определяет семантику доставки сообщений и активации управляемых Акторов.
Протяженное событие, когда Актор получил сообщение и его обрабатывает. При этом Актор может изменить своё состояние, произвести побочные эффекты и пр.
Протяженное событие Актора, происходящее в известном окружении, которое либо
- Только изменяет состояние Управляемого актора
- Только порождает контролируемые эффекты
В этих случаях мы можем восстановить состояние Актора как до, так и после активации (повторив входящее сообщение или повторив побочные эффекты соответственно).
Любое изменение состояния Окружения, других Акторов и т.д., которое нельзя описать чистой функцией от состояния к состоянию Актора, у которого произошла активация.
Эффект, который можно зарегистрировать, повторить или отменить (например, отправка сообщения другому Актору того же окружения)
Управляемый актор окружения, который передает сообщения к/от актора вне этого окружения. Для других акторов, по идее, неотличим от удаленного актора.
Каждый актор имеет 3 представления, оплог, логическая модель и имплементация. Самое сложное с оплогом - однозначно закодировать сайдэффекты.
Активация актора A сообщением (командой, запросом) K при стейте S вызывает исходящий сайдэффектинг меседж E' и стейт-мутатор S'. Они все пишутся в оплог. Далее при реплее если есть исходящие сайдэффекты то входящие сообщения пропускаются, а исполняются только мутаторы.
Актор без сайдэффектов он может менять себя как угодно под капотом так как достаточно лога входящих сообщений.
А вот реакция на действие с сайдэффектом обязана никак не трогать внутренний стэйт актора за исключением того, что можно энкоднуть в стейт-мутирующий меседж сам себе.
Сообщения оплога ссылаются на то, что их вызвало. Не-сайдэффектящие ссылаются на себя и предыдущий стейт. Сайдэффектящие содержат ссылку на энвайронмент (например других акторов), точнее на local view of environment at point in time (просто GUID). Он в рамках системы является causality reference point, к которому возможно прилепливаются старты новых акторов или сообщения от энвайронмента.
Если сообщения от энвайронмента не прилеплены к refpoint, то они могут быть arbitrarily ordered, точнее, ордеринг им имеет право устанавливать актор которому они прилетели.
Таким образом у нас в систему вводится понятие эмбиента и хм независимого от нас окружения, и cross-thread causality
Таким образом мы
а) избавляемся от дурацкой необходимости писать двусторонние линзы для персиста/восстановления
б) получаем возможность абстрагировать мутаторы то есть рассматривать их как блекбоксы не перегружая этим систему, но
в) получаем что сайдэффекты должны быть строго упорядочены относительно стейт-мутаторов. Это усложняет некоторые аспекты рассуждений, но стоит того.
Актор описывается чистыми функциями ((состояние, сообщение) -> состояние) и one-shot функциями ((состояние, сообщение) -> (мутатор состояния, сообщения о эффектах)). В терминологии Хаскеля это будут эффекты и коэффекты соответственно.
Чтобы трансформировать в такую форму произвольный код, не обращая внимания на его реализацию нам надо либо
- способ захватывать/восстанавливать состояние актора как черный ящик (виртуальная машина), либо
- ограничить список сайд-эффектов до порождения сообщения-мутатора (которое мы не можем инспектировать, зато можем применить к черному ящику) и исходящих сообщений к окружению
В логической модели это все не нужно, но на практике упрощает описание до “модель виртуалки и хэш ее состояния”. Или актор “виртуалка” отвечающий на сообщения “сохранить-восстановить”, или “применить сообщение-мутатор”.
Поскольку это черный ящик, его нельзя формально проверить, но иногда можно вхолостую сохранить-восстановить и выяснить, что персист поломался.
То есть в общем случае сохраненный актор выглядит как ссылка на состояние, ссылка на восстановлятор VM и causality-точка. Из этого мы можем создать VM, засунуть в нее стейт, подключиться к ее портам и поехать дальше. Так же выглядит клонирование и порождение нового актора, только ссылка на состояние выдается окружением.
Если нам надо описать актор, стэйтом которого мы не управляем, то все что нам остается - ссылка на актор (способ доставки сообщений, URL и т.д.) и causality-точки. К примеру, актор “системные часы” отвечает на сообщение “getCurrentTime”, и мы можем запомнить, что мы послали сообщения А и Б одним и тем же часам, а также что мы послали сообщение Б после сообщения А, и обнаружить так, к примеру, что часы отвели назад. Но мы не можем никак предсказать, что нам ответит актор снова и ответит ли. Если актор не умеет сохранять causality, то порядок сообщений можно трекать только в пределах одной прокси.
Активации у нас считаются атомарными и не-инспектируемыми. Если мы хотим рассуждать о таймаутах и не нарушать этим чистоту/тотальность модели, нам следует явным образом порождать актор “процесс” (Task, Future и т.д.), который уже детерминированным образом обрабатывает сообщения getValue, cancel и т.д. Аналогично, окружение может порождать активации зеркал обьектов, отладчиков и прочих сущностей, нарушающих инкапсуляцию актора, в том числе в том же окружении, но они всегда отдельные.
См. Composable Output-Valid Resilient Messaging (HP Labs 2014) http://www.labs.hp.com/techreports/2014/HPL-2014-14.pdf
Для быстрой оценки causality оптимально использовать Hybrid Logical Clock (2014) http://www.cse.buffalo.edu/tech-reports/2014-04.pdf, а для репортинга - при возможности для каждого executor’a (ядра) поддерживать набор счетчиков “последнее и максимальное известное время” для каждого источника (Local time, NTP time, Market time и т.д.), из которых делать выводы непосредственно в момент генерации репорта.
Помимо этого, для строгой корреляции довольно важно для каждого отдельного вычислителя поддерживать набор последних известных causality points (GUIDов) от разных других агентов и выдавать его вместе с исходящими сообщениями.
Все описываемые агенты должны иметь возможность узнать, какой код/конфиги и конфигурация окружения были использованы при их запуске, иначе коррелировать становится намного сложнее.