avlasov (avlasov) wrote,
avlasov
avlasov

Мини-учебнег по распределенным системам/протоколам. Восстановление после сбоев, ч.1

Рассмотрим теперь подробнее восстановление после сбоев в распределенной системе. Я выделил тут три пункта в одном из предыдущих постов:
— детерминированность обработки событий каждой нодой
— запись входящих сообщений
— output commit

На самом деле, это не совсем точно, скорее это один из возможных подходов. Но если мы слишком сильно от него отступаем, у нас могут быть проблемы. Тем не менее, в зависимости от требований можно и нужно отступать. Рассмотрим все это дело более детально.

Допустим к нам пришло сообщение от клиента — внешнее входящее сообщение. Если мы подтверждаем клиенту его получение, то неплохо бы гарантировать, что если в системе произойдет сбой, то системе сообщение не забудет — т.е. перед отправкой подтверждения, сообщение от клиента будет помещено в персистентное хранилище.

Это может быть важно, к примеру, по такой причине: в процессе восстановления после сбоя соединения, может возникнуть необходимость переслать потерянные сообщения. Т.е. клиент хранит у себя в буфере отправленные сообщения, но если выясняется, что они потерялись, то он может их переслать — мы разбирали эту схему в одном из предыдущих постов. Посылка подтверждения о получении, означает, что клиент может почистить свой буфер, чтобы он не рос до бесконечности. Посему, если мы посылаем подтверждение, но не записали само сообщение в персистентное хранилище, то при сбое мы вообще можем его потерять, ибо оно удалилось и из клиентского буфера и из нашей системы при сбое. Т.е. подтверждение получение по сути означает, что сторона принимает на себя ответственность за дальнейшую судьбу этого сообщения.

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

Конечно же, и в этой ситуации все зависит от протокола восстановления после сбоев (и требований относительно того что нужно помнить, а что нет). Т.е. если противоположная сторона сохраняет входящие сообщения (что многие делают и предоставляет интерфейс, чтобы их можно было получить при восстановлении, то на отправляющей стороне их можно и не хранить.
В принципе, система вообще может не иметь персистентного состояния, и восстанавливать свое состояние после сбоев путем опроса контрагентов. Но иногда имеет смысл держать состояние в персистентном хранилище, даже если есть возможность восстановиться другим способом. Ибо такая система lously-coupled с другими системами: ведь если контрагент не доступен, то с него и не восстановиться.

Подведем некоторые итоги. Прежде всего имеет смысл определиться какое состояние должно быть восстановлено после сбое, а какое — не обязательно, какое можно похерить. Вопрос конечно не всегда очевидный, но он определяется требованиями к системе. Например, необработанные запросы на чтение можно и похерить, если протокол взаимодействия с клиентом предполагает, что они их повторят после сбоя. Так же часто нет жесткого требований для сохранения пользовательских сессий. Грубо говоря, покупатель наложил что-то себе в корзину, но пока не купил, и оно все храниться в контексте пользовательской сессии. По идее, конечно, имеет смысл хранить это состояние между сессиями, ибо клиент может потом вернуться и все такое. С другой стороны, если сервер накроется по тем или иным причинам, то все же наверное это не очень ужасная потеря — клиент соберет корзину еще раз. Тем более, что "старые" корзины и так обычно обнуляются.

Далее, необходимо подумать о протоколе восстановления после сбоев — можем ли мы состояние получить откуда-то еще. И насколько система должна быть coupled с другими системами, в том смысле, что при восстановлении какие-то сервисы могут оказаться недоступны и помешать полноценному фунциклированию системы.

К примеру, рассмотрим систему, которая шлёт ордера на биржи. Мы скорее всего не хотим делать ее тесно-связанной, ибо даже если мы можем получить список ордеров и их состояние с самих бирж. Но что делать, если соединение с одним брокером отвалилось? Возможна ситуация, когда нам надо бы вытащить состояние, но мы можем сделать это лишь частично. Соответственно, нужно как-то уметь обрабатывать такие ситуации на уровне бизнес-логики и пользовательского интерфейса, что может быть весьма заморочным. Гораздо проще все-таки хранить свое состояние в персистентном хранилище. Т.е. типа "развязать" систему с системами бирж/брокеров.
С другой стороны, если у нас всего один контрагент, типа там брокер, то можно восстанавливаться с него — если брокер недоступен, то все равно особо ничего не сделаешь.

Ну и наконец, когда понятно какое состояние нужно персистировать в системе, то можно определить когда нужно делать output commit. Если мы делаем видимым для внешнего мира наличие того или иного состояния, которое мы решили персистировать, то прежде чем послать подобное сообщение, нужно убедиться, что вся информация, необходимая для успешного восстановления после сбоя, сохранена в надежных хранилищах. Ибо по разным причинам, эти операции могут откладываться, группироваться с другими, либо просто занимать заметное время. Завершение подобных "отложенных" операций мы и называем output commit, что близко по смыслу commit'у в транзакциях.
Subscribe
  • Post a new comment

    Error

    default userpic

    Your reply will be screened

    When you submit the form an invisible reCAPTCHA check will be performed.
    You must follow the Privacy Policy and Google Terms of use.
  • 0 comments