Отказы оборудования — это одна из наиболее частых причин остановки счета в супервычислительных установках. При этом повышенная надежность оборудования весьма заметно отражается на его цене. С точки зрения экономической целесообразности, значительно выгоднее наращивать способность дешевых систем к реконфигурированию в случае отказов, нежели создавать «бесконечно» надежные программно-аппаратные комплексы. Это одна из причин, обуславливающих высокую актуальность обеспечения отказоустойчивости вычислений.
В последнее время классическая идея избыточных вычислений (перевычислений) принимает новые эффективные формы, которые являются более привлекательным, чем простое сохранение полных контрольных точек в надежную энергонезависимую память.
Наиболее эффективной формой реализации отказоустойчивости в случае Т-системы является сохранение пренатальных процессов (только что вызванных Т-функций), так как они еще не получили стека для своей работы и находятся в наиболее компактной (и даже адресно-независимой) форме.
Т-система изначально предполагала функциональный стиль программирования, и рассматриваемый подход отказоустойчивости на основе модели перевычислений предполагает активное использование специфики функциональных программ для того, чтобы получить эффективный алгоритм восстановления в случае сбоев.
Мы будем предполагать, что прикладная программа написана в функциональном стиле, и в случае его расширения прикладной программист так же точно отвечает за отказоустойчивость своей программы при использовании отказоустойчивой OpenTS, как и за простые корректность и детерминированность результатов счета в случае обычной Т-системы.
|
Неготовые значения и незавершенные по причине сбоя вычисления
С точки зрения прикладного программиста наиболее существенное новшество Т-системы (языка T++) по сравнению с базовым языком (C++) состоит в поддержке Т-функций и неготовых значений. Неготовые значения генерируется Т-функциями, и служат удобными абстракциями, инкапсулирующими синхронизацию и обмен данных между параллельно вычисляющимися функциями-потоками.
Для того чтобы у прикладного программиста появилась возможность самостоятельно принимать решение об обработке сбоев, мы пополним диаграмму состояний неготового значения еще одним состоянием — «незавершенное». Это состояние является альтернативой готовому состоянию и отличается от него следующими ключевыми свойствами:
· Неготовое значение становится незавершенным, если его вычисление было остановлено вследствие сбоя.
· Незавершенное значение является готовым в том смысле, что попытка прямого получения находящегося в нем реального значения не приводит к остановке процесса, но ведет к исключительной ситуации в программе. То есть, значение можно получить, но если сделать это непосредственно, то произойдет исключение.
· В случае, когда прикладная программа использует примитив языка T++ twait(), то она получает событие, означающее что величина готова, но вычисление не было завершено. В этом случае она может продолжать счет, предприняв то или иное действие в соответствии с выбранной «заказной» моделью обработки сбоя.
Для того чтобы обеспечить полностью автоматическую обработку сбоев, будет реализован дополнительный режим, который будет приводить к циклической попытке вычислить значение неготовой переменной, до тех пор, пока примитив twait() не вернет успех в плане завершенности вычислений. Этот цикл будет организован внутри Т-системы, так что пользователь никогда не получит исключительной ситуации — счет с его точки зрения будет происходить как обычно, а Т-система будет производить перезапуск соответствующих Т-функций автоматически.
|
Вектор перерождений
Популяция исправных узлов, образующих устойчивое множество, может меняться в случае временного выхода отдельных узлов из строя. Будем называть процесс выхода узла из устойчивого множества гибелью, а входа — рождением. Тогда в каждый момент времени устойчивое множество характеризуется вектором чисел — порядковыми номерами перерождения для каждого узла и текущим их статусом работоспособности.
Если ограничить значение счетчика перерождений некой разумной величиной (например, типом данных int), то можно хранить вектор перерождений в массиве байт (каждый элемент содержит 31 бит на порядковый номер и 1 бит на текущий статус работоспособности).
Такой способ кодирования очень удобен для представления вектора перерождений, так как он является «монотонным объектом» - каждый байт меняется строго последовательно: в момент старта коммуникационная подсистема, переведенная в отказоустойчивый режим, начинает с полностью нулевого вектора перерождений. Далее, по мере обнаружения работоспособных узлов, их байты перерождений становятся равными единице (то же воплощение, но переход из нерабочего состояния в рабочее — младший бит установлен). В случае сбоя байт становится равным двум (следующее воплощение, пока не работоспособен), затем трем и так далее.
Вектор перерождений хранится в одной из ячеек суперпамяти, размещенной на нулевом узле, так как это глобальный объект.
Вектор посещений
Каждой Т-функции мы сопоставим вектор посещений, который содержит номера перерождений тех узлов, на которых либо она находилась сама, либо находились ее предки. В случае обнаружения несовместимости вектора посещений Т-функции с вектором перерождений (при несовпадении порядкового номера перерождения хотя бы одного из узлов, где считалась данная Т-функция либо ее предки) данная Т-функция является «безнадежной» и подлежит самоуничтожению с освобождением всех захваченных ресурсов.
Очевидно, вектор посещений является характеристикой Т-функции и должен передаваться (и корректно при этом модифицироваться) вместе с ней в случае миграции.