Управление проектами - статьи

         

Designing for FAILURE - ключ к успеху?Беседа с Брюсом Линдсеем


Прокомментировать>>

Перевод: Сергей Кузнецов
Оригинал: A Conversation with Bruce Lindsay, ACM Queue, Vol. 2, No. 8 - November 2004.

Брюс Линдсей – это одна из легендарных фигур в истории баз данных. Он появился в IBM на закате проекта System R, и сразу привлек внимание в качестве соавтора многих замечательных статей. Но, прежде всего, он системный архитектор и программист. Именно под его архитектурным руководством был выполнен один из наиболее интересных проектов в области баз данных Starburst. В предлагаемом вашему вниманию интервью он говорит, в основном, не о базах данных, хотя все время имеет их в виду. Он говорит о том, как следует разрабатывать программные системы, чтобы добиться их общей надежности в условиях ненадежности компонентов, программистов, языков программирования и окружающей среды. Замечательно, что это интервью у Линдсея брал не менее легендарный персонаж из истории операционной систем Unix Стив Борн, создавший первый язык и интерпретатор Shell. Приятного и полезного вам чтения. Сергей Кузнецов

Трудно найти более квалифицированного специалиста в области проектирования и разработки систем управления базами данных, чем почетный сотрудник IBM (IBM Fellow) Брюс Линдсей. Он начал заниматься архитектурой РСУБД (реляционных систем управления базами данных) еще до того, как появились такие системы. В 1978 г., закончив аспирантуру Калифорнийского университета в Беркли и получив степень PhD, он поступил на работу в Исследовательскую лабораторию IBM в Сан-Хосе, где исследователи тогда работали над тем, что впоследствии стало основой языка SQL и продукта IBM DB2. С того времени Линдсей играет ведущую роль в развитии РСУБД.

В конце 1980-х гг. он участвовал в разработке протокола DRDA (Distributed Relational Database Architecture, архитектура распределенной реляционной базы данных), а позже являлся главным архитектором Starburst, расширяемой системы баз данных, которая со временем превратилась в оптимизатор запросов и интерпретатор для DB2 на платформах Unix, Windows и Linux.
Линдсей разработал концепцию экстендеров баз данных, в которых мультимедийные данные – изображения, голосовые и видеоданные – трактуются как объекты, являющиеся расширениями стандартной реляционной базы данных, и эти данные можно запрашивать с использованием стандартного SQL (Structured Query Language). Сегодня он по-прежнему глубоко погружен в работу в лаборатории управления данными в Исследовательском центре IBM в Алмаден, участвуя в создании продуктов управления данными следующего поколения.

Интервьюером был Стив Борн (Steve Bourne), знаменитый своим Борн-shell (Bourne Shell) для Unix. В течение 20 лет он занимал высокие должности в подразделениях проектирования и разработки компаний Cisco Systems, Sun Microsystems, Digital Equipment и Silicon Graphics, а в настоящее время является техническим директором в товариществе венчурного капитала El Dorado Ventures в Менло Парк, Калифорния. В начале своей карьеры он проработал девять лет в Bell Laboratories в составе группы Seventh Edition Unix. В эти годы он разработал командный язык для ОС Unux (Борн-shell), который используется для разработки сценариев в среде Unix, а также создал отладчик ADB. Борн закончил King’s College в Лондоне и получил степень Ph.D. в области математики в Кембриджском Trinity College (Великобритания).

Стив Борн: Почему бы нам не начать с той мысли, что нельзя избавиться от ошибки, пока ее не обнаружишь?

Брюс Линдсей: Давайте немного поразмышляем над тем, как возникают ошибки. Они возникают на всех уровнях системы, и их причины могут простираться от воздействия альфа-частиц, разряжающих конденсаторы основной памяти, до пожаров, наводнений или террористических актов, разрушающих вычислительные системы целиком. Все делается неправильно, начиная с грубых промахов в логике программирования и заканчивая данными, поступающими не с того сектора диска. Нужно производить разработку систем с учетом возможности отказов на всех возможных уровнях системы, от уровня электронных схем до уровня подсистем, таких как СУБД или файловая система, и даже до уровня самих прикладных программ.



«Engineering for failure (разработка с учетом возможности отказа)» – это, возможно, плохое выражение, но в действительности это именно то, что требуется для обеспечения надежной и заслуживающей доверия обработки информации.

СБ: Безусловно верно, что при написании кода и разработке систем в голове должна сидеть мысль: «А что здесь может поломаться?». Причин может быть множество, и возможные линии поведения зависят от типа приложения или программного обеспечения. При написании программы типа Microsoft Word они совсем не те, что при разработке кардиомонитора.



БЛ: В случае кардиомонитора вы обязаны поддерживать работу сердца, а в случае Microsoft Word можно всего лишь выдать пользователям «синий» экран, к которому все привыкли.

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

БЛ: Иногда можно спрашивать пользователя, хотя лучше было бы спросить подсистему, что она собирается сделать для самовосстановления, или прекратить ее работу в зависимости от ситуации.

СБ: Вы действительно противопоставляете отказы системы ошибкам пользователей?

БЛ: Я не считаю «отказами» ошибки пользователей, такие как ввод неверных данных. Это нормальное явление. Вряд ли неправильное написание go to вызовет ошибку в работе компилятора. Такие вещи являются ожидаемыми.

СБ: Что же такое, по Вашему мнению, ошибка?

БЛ: Ошибка возникает в каждом случае, когда некоторый используемый компонент не выдает ожидаемого результата, будь то обращение к основной памяти, которое не может быть выполнено из-за проблем с контролем четности, безрезультатный обмен данными с диском или вызов подпрограммы, в которой возникает исключительная ситуация. «Меня попросили выполнить действие X, а я этого не сделал.»

Хороший принцип разработки состоит в следующем: либо делайте то, что вас просят, либо говорите, что вы это делать не будете, и объясняйте причину, но не делайте ничего другого.



СБ: Я думаю, что имеется еще один вариант: «Я этого делать не буду, так что выясните, нет ли какого-то другого способа получить ответ на интересующий вас вопрос или выполнить требуемую вам функцию». Это ситуация с несколькими возможностями выбора.

БЛ: Ну конечно, так всегда и происходит. Например, функция состоит в том, чтобы зарегистрировать некоторый UserID, и выясняется, что мы пытаемся зарегистрировать UserID, который уже зарегистрирован. На уровне выше уровня регистрации требуется принять решение о том, что делать дальше. На этом уровне можно сказать: «Пардон, но в нашей системе может быть только один Брюс, и вас мы в нее никогда не впустим. Пожалуйте прочь» или «А не смените ли вы свое имя на Стив?».

СБ: А как Вы учитываете возможности отказов при разработке системы, а не одиночного приложения, когда система может быть достаточно крупной и сложной, и ей свойственны проблемы масштабируемости?

БЛ: Конечно, здесь нужно придерживаться позиции врача: не навреди в случае отказа. Другими словами, не следует предпринимать опрометчивых действий при отсутствии уверенности в их правильности. По другому это называется «быстрым выявлением сбоев» (fail fast), а я здесь имею в виду системы, в которых, большей частью, поддерживается персистентное состояние. Может оказаться очень опасным продолжать работу системы при отсутствии понимания сути сбоя. Поэтому достаточно успешной является идея быстрого выявления сбоев и склейки согласованного состояния системы на основе тщательно поддерживаемых резервных данных.

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



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

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

СБ: Здесь стоило бы прояснить, какие методы используются для обнаружения ошибок.

БЛ: Обработка сбойных ситуаций всегда начинается с обнаружения сбоя – чаще всего путем использования в системе какой-либо разновидности избыточных средств, будь то биты четности или «санитарная проверка» (sanity check) в коде программы, когда 10% строк кода тратятся на проверку согласованности состояния с целью выявления того, не следует ли заняться обработкой ошибок. Таймауты, строго говоря, не являются избыточным средством, но это еще один способ выявления ошибок.

Здесь важной мыслью является то, что если вы отправляетесь в морское путешествие, имея только одни часы, то вы не можете сказать, показывают ли они правильное время. Требуется какой-то способ проверки. Например, при чтении сообщения, поступающего по сети, чтобы удостовериться в том, что ожидается именно это сообщение, можно проверить некоторые биты в заголовке сообщения и сказать: «Ага, кажется, можно продолжать работать».

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


Если не задумываться о возможности сбоев, то зачем помещать в блок диска адрес самого этого блока?

СБ: Значит, на самом деле Вы стараетесь убедиться в том, что понимаете то, что происходит в системе?

БЛ: По большому счету это именно так. И еще я стараюсь проверить то, что данные или состояние, над которыми требуется производить дальнейшую работу, являются самосогласованными.

СБ: Имеется ли какая-нибудь языковая поддержка обнаружения ошибок?

БЛ: По сути, языковая поддержка обнаружения ошибок отсутствует. В языках имеются средства обработки уже обнаруженных ошибок. Генерация исключительной ситуации средствами языка программирования – это нечто, что делается в соответствии с логикой программы.

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

В языках имеются некоторые довольно опасные возможности – в частности, инициирование ошибки (raise error) или исключительной ситуации, а также обработчики исключений. Как это соотносится со стеком вызовов процедур? В ранних подходах к языковой поддержке обработки ошибок стек сокращался без совершения каких-либо дополнительных действий, пока не достигался некоторый уровень, на котором было объявлено нечто, представляющее интерес для обработки данной исключительной ситуации, т.е. ошибки.

Вообще говоря, свертывание вызова процедуры или подпрограммы – вызова метода – без устранения беспорядка, который мог уже образоваться, т.е. частично произведенного этой функцией преобразования состояния, является очень опасным. Например, если имелись запросы динамической памяти, а соответствующий участок стека просто сокращается, запрошенная память так и не будет возвращена.

Поэтому очень важно дать каждому вызову процедуры возможность «аккуратно сложить свою палатку», даже если эти действия не имеют отношения к обрабатываемому исключению.



СБ: Да, похоже, что людям нужно не забывать про такие вещи, как динамическое распределение памяти. Я не знаю, имеются ли в связи с этим какие-нибудь хорошие правила? Есть ли у Вас какие-нибудь еще соображения по этому поводу?

БЛ: Ну, конечно, множество грехов в этой области покрывается сборкой мусора. Полезны также автоматические вызовы деструкторов C++ для объектов, состояние которых сохраняется в стековом фрейме. Хотя сборка мусора порождает некоторые серьезные проблемы производительности, особенно в многопотоковых приложениях, она способствует устранению ошибок, ведущих к потере памяти, а такие ошибки чрезвычайно трудно изолировать.

СБ: Кажется, что в некоторых современных языках проблема восстановления после возникновения ошибок, по крайней мере, принимается во внимание. Например, некоторые средства имеются в языке Python. По моему мнению, хорошо уже то, что люди начали думать об этой проблеме и пытаться оснащать языки соответствующими возможностями. Не знаю, насколько правильно они ее понимают.

БЛ: Хорошо, что люди начинают об этом задумываться хотя бы из-за того, что появляются новые языковые средства, побуждающие их к раздумьям о восстановлении после проявления ошибок. Но ни в каком языке нет волшебных возможностей, которые могли бы нас спасти. Все равно нужно думать о причинах того, что вызываемая процедура может не вернуть ожидаемые результаты, и о том, что можно сделать в этой ситуации.

СБ: Предположим, нам удалось обнаружить ошибку, и что следует делать далее? Можно проинформировать кого-то об этом, но встает вопрос: кого и о чем следует информировать?

БЛ: Имеются две разновидности обнаружения ошибок. В первом случае я смотрю на собственные внутренности и вижу, что они выглядят неправильно, и тогда я говорю, что возникла ошибочная ситуация. Во втором случае я вызываю некоторый другой компонент, который не делает то, что требуется. В обоих случаях я сталкиваюсь с обнаруженной ошибкой. Прежде всего, нужно «свернуть свою палатку», т.е.


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

В наших проектах из области баз данных информация об ошибочной ситуации обычно передается все выше и выше по цепочке вызовов, пока на каком-то очень высоком уровне не будет сказано: «Да, дела действительно плохи. Пора производить массивный дамп памяти». При информировании об ошибке следует ее классифицировать. Следует присвоить ей имя. В каждом компоненте, информирующем об ошибках, должен иметься исчерпывающий список ошибок, информация о которых может передаваться этим компонентом.

В этом состоит одна из реальных проблем архитектуры сегодняшних языков программирования в части обработки исключительных ситуаций. В каждом компоненте должны регистрироваться все исключительные ситуации, которые могут быть возбуждены: обычно, если я вас вызываю, и вы говорите, что возбуждаете исключения A, B и C, а сами можете вызвать Джо, который возбуждает исключения D, E и F, а вы эти исключения игнорируете, то я неожиданно сталкиваюсь на своем уровне с D, E и F, хотя в вашем интерфейсе ничего не говорилось, что от вас могут исходить ошибки D, E и F. Это явление повсеместно распространено в программировании и языковых средствах. От вас никогда не требуется гарантия того, что вы объявили все ошибки, которые могут проистекать от вызова вашей подпрограммы. И поэтому разрешается игнорировать ошибки. Иногда я высказываюсь в пользу того, чтобы не разрешать игнорировать никакие ошибки. Можно изменить классификацию ошибки и передать информацию о ней на более высокий уровень, но ты не должен разрывать цепочку.

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



БЛ: В случае систем баз данных мы все более и более склоняемся к консервативному дампингу – т.е. к включению в дамп всего, что может представлять интерес. Это делается на основании того, что при наличии какого-либо используемого приложения, если при работе какого-либо производственного приложения приходится столкнуться с гейзенбагом, который трудно воспроизвести, часто бывает трудно заставить пользователей еще раз специальным образом запустить приложение, говоря примерно следующее: «Привет! Мне хотелось бы еще раз поломать вашу систему, хотя у меня имеются собственные средства для отслеживания того, что происходит».

Обычно они отвечают так: «Что? Оно же сейчас работает. Оно работает уже шесть часов. Да, эта проблема возникает раз в неделю, и нас это довольно-таки раздражает. Устраните ее, наконец. Но мы не собираемся ломать систему для вашего удовольствия».

Поэтому мы включаем в дамп все больше и больше информации. Мы не включаем в дамп саму базу данных, но если обнаруживаем подозрительную страницу, то, безусловно, направляем в дамп все ее содержимое – почти поразрядно – для возможного анализа. Слишком большой объем дампа – это не такая плохая вещь в нашей области, если приходится обслуживать эксплуатируемый код, и вам не позволительно сказать: «Привет, устройте-ка для меня еще разочек аварийный отказ вашей системы».

СБ: Когда дела идут плохо, и вы решаете выдавать дамп всего, что приходит в голову, вам могут задать вопрос: «А не лучше ли было бы узнать, что происходило в нужном месте в нужное время?» вместо того, чтобы получить дамп стека и стараться выяснить все на основе этих фотографий? Похоже, что вам нужен фильм, а не фотографии.

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


Они воспроизводят ошибку при наличии подключенного к системе микроскопа и анализируют дампы трассировки – вот и получается кино.

СБ: Когда Вы разрабатываете код с учетом возможности сбоев, думаете ли Вы о ситуациях, информацию о которых следует зафиксировать, поскольку это может помочь при возможном поиске ошибки?

БЛ: Бывает так, что при раскрытии основной причины ошибки производится модификация подпрограмм построения дампа с целью включения в дамп дополнительной информации, которая помогает улучшить процесс раскрытия проблемы.

СБ: Было бы интересно поговорить о файловых системах и журналах и узнать вашу точку зрения по поводу того, насколько далеко мы ушли от прошлого, в котором имелась утилита fsck (file system check) для восстановления файловой системы после ее отказов.

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

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

СБ: Давайте рассмотрим пример разработки системы, допускающей ухудшение эксплуатационных характеристик (graceful degradation). В системе Sabre, предназначенной для резервирования авиабилетов, допускается наличие дубликатов записи о бронировании (около одного дубликата на тысячу записей), поскольку оказалось, что наличие записей-дубликатов о бронировании не является слишком существенным.


Рано или поздно кто-нибудь обнаруживает наличие дубликатов и что- нибудь предпринимает. Эта система не является черно-белой. У нее имеются нечеткие границы (fuzzy edge), и в ней допускается наличие некоторых несогласованностей, потому что имеются свои способы восстановления согласованности.

БЛ: На всех уровнях системы наблюдаются деградирующие операции, начиная от систем основной памяти, в которых можно просто удалить неисправную микросхему и больше ее не использовать – следует надеяться, что это делается после восстановления состояния памяти с использованием избыточного кода с исправлением ошибок, до общесистемных явлений, когда в случае отказа системы можно перезапустить ее на резервной аппаратуре, которая, возможно, уже была частично задействована. В этих случаях имеется деградация производительности.

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

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

Так что временное ухудшение эксплуатационных характеристик наблюдается на всех уровнях системы.

Нужно вести себя очень осторожно в случае деградаций, при которых результат, возвращаемый системой, может быть каким-то образом дискредитирован. Примером может быть приложение по бронированию авиабилетов, о потере в котором записи о бронировании вы узнаете позже от клиента. Здесь требуется серьезная проверка на уровне проектирования системы, которая должна помочь ответить на вопросы: «Допустимо ли это для приложения, для внедрения этой системы?». Безусловно, мы сталкиваемся с этим в среде Web. Если полностью проигнорировать один запрос к Web-сайту из ста, ничего страшного не произойдет.


Пользователь нажмет на клавишу «refresh» своего браузера, и запрос будет обработан со второй попытки. Но если игнорировать 100% запросов в течение хотя бы нескольких минут, то известие об этом попадет в New York Times.

Так что мы видим, что решение о том, как реагировать на ошибки – не выдавать ли ответ вообще или выдавать частичный ответ, в действительности зависит от того, для чего будет использоваться этот ответ, и какова будет реакция клиента на неправильный ответ или отсутствие ответа.

СБ: Что Вы можете сказать относительно связи между отладкой системы и обнаружением ошибок и восстановлением системы после их проявления?

БЛ: Имеется две обширных категории сообщений об ошибках. Сообщения первой категории означают, что система предвидела данную проблему, например, попытку регистрации пользователя с уже использующимся в системе именем или выход значения параметра за пределы допустимого диапазона, а теперь сообщает вам, что эта проблема действительно возникла. Большинство систем с этим справляется плохо. Получаемые пользователями сообщения об ошибках в лучшем случае являются непонятными, а в худшем – вводящими в заблуждение. Это является результатом ряда проблем. Одна из них заключается в том, что во многих средах разработки программного обеспечения имеется отдельная небольшая подсистема, предназначенная для поддержки сообщений об ошибках. Для создания нового сообщения об ошибке нужно потратить полчаса на ввод сообщения об ошибке, нужно убедиться в том, что это сообщение является пригодным для перевода на национальные языки, и нужно присвоить ему код ошибки, отличный от тех, которые уже были выделены для других сообщений об ошибках. Чтобы избежать этой работы, многие люди повторно используют старые сообщения об ошибках, говоря, что данная ошибка похожа на ту, которая уже зарегистрирована в системе, и что можно использовать существующее сообщение.

Программистская лень приводит к отсутствию конкретности в сообщениях об ошибках.


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

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

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

БЛ: Зачастую текст сообщения об ошибке формулируется разработчиком, погруженным во внутренности системы, такой текст, очевидно, не отвечает потребностям конечных пользователей. Как я уже говорил, такие тексты являются недостаточно конкретными.

Но пока разговор шел про первую категорию сообщений об ошибках, т.е. тех, для которых достаточно понятна связь между данными, вводимыми пользователем, и соответствующей ошибкой. Сообщения об ошибках второй категории выдаются в тех случаях, когда система понимает, что она повреждена. В таких случаях сообщения в действительности совсем не рассчитаны на конечных пользователей и часто вообще ничего не говорят им. Подобные сообщения выглядят примерно так: «Оператор не может быть выполнен. Попробуйте выполнить другой оператор».

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

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



СБ: Что такое гейзенбаги?

БЛ: Этот термин родился в моем присутствии. Гейзенбагами называются ошибки (баги), которые явным образом демонстрируют некорректное поведение системы, но такие, что при попытке разобраться в причинах этой некорректности проблема исчезает. Обычно для того чтобы можно было разобраться в причинах некорректности, включается трассировка, или добавляются дополнительные параметры, или что-то изменяется. И из-за этих изменений проблема исчезает. Довольно часто подобные проблемы проявляются в связи с параллельной работой программ. Или же они могут проявляться в зависимости от конкретного способа размещения программ и данных в основной памяти.

Так что настоящее определение гейзенбага состоит в следующем: когда вы смотрите на эту ошибку, она исчезает – с уважением к доктору [Вернеру] Гейзенбергу, который говорил: «Чем более пристально вы смотрите на что-то одно, тем менее отчетливо можете увидеть что-либо иное».

СБ: Я думаю, что хорошо, когда случается нечто, представляющее известную реальную неполадку, поскольку обычно это означает, что неисправность возникла на более низком уровне системы – при распределении памяти или выполнении каких-то других подобных действий.

БЛ: Такие ошибки очень трудно находить, но, конечно, чем труднее ошибка, тем больше удовольствия доставляет ее обнаружение.

СБ: Как происходит обнаружение ошибок и восстановление после их проявления в распределенных системах, которые по своей природе обладают высоким уровнем параллелизма и включают массу слабо связанных компонентов?

БЛ: Распределенные системы разбиваются на две обширные категории. В первой категории одна система использует другую систему для выполнения некоторых функций. Здесь первая система зависит от возможности второй системы выполнять эти функции. Примером такого зависимого использования являются Web-сервисы. Во второй категории распределенные машины совместно обеспечивают некоторый единый сервис. Примером могут служить распределенные файловые системы.



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

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

СБ: Одним из интересных аспектов здесь является соотношение между временем, требуемым на обнаружение чего-либо, и временем, которое приходится затрачивать на реальное восстановление системы.

БЛ: А также, что значит удалить отказавший компонент, поскольку здесь имеется проблема расщепления разума (split brain problem), когда я думаю, что отсутствуете вы, а вы считаете, что отсутствую я? Кто несет ответственность?

СБ: Ну да, и пока они спорят об этом, ничего не происходит.

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

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


Если нас всего пятеро, и четверо считают, что пятый мертв, то для гарантии того, что он мертв, стоит выпустить в него еще три пули.

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

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

Имеются два аспекта совместной работы в распределенных системах. Один из них состоит в выявлении участников игры, а другой – в том, что если кто-то считается выбывшим из игры, то необходимо каким-то образом абсолютно убедиться, что он действительно больше не играет.

СБ: Обнаружение ошибок и восстановление затруднительны в многопотоковых приложениях. Значит ли это, что не следует создавать многопотоковые системы?

БЛ: Я думаю, что нам нужно разрабатывать многопотоковые системы, но это трудное дело. Я обычно говорил людям: «Если вы не занимались созданием многопроцессного кода в течение пяти лет, то у вас нет квалификации, достаточной для написания такого кода». А потом я понял, что, пока я придерживаюсь такой позиции, никто не собирается меня заменять. Поэтому я стараюсь помогать людям в понимании того, как следует писать многопроцессный код. Имеется куча методов, но в основном необходима синхронизация критических участков и масса проверок.


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

СБ: Мне кажется, что состояние дел в области обнаружения ошибок и восстановления не слишком изменилось за последние 20 лет.

БЛ: Одной из вещей, которые мы теперь понимаем немного лучше, является так называемая область действия ошибки (scope of the error). Испортила ли ошибка только одну функцию? Вы вызываете эту функцию, а она не работает. Она говорит: «Я сломалась, возврат». В этот момент областью действия ошибки является эта функция. Конечно, если вы делаете возврат, если вы возбуждаете исключительную ситуацию, то область действия ошибки расширяется. Вы должны решить, ограничена ли область действия ошибки данным потоком управления. Если ошибка проявляется вне каких-либо манипуляций с разделяемыми данными, вы можете сказать: «Может быть, если мы всего лишь ликвидируем этот поток управления, то остальные потоки смогут продолжать работать, а может быть, мы даже сможем перезапустить этот поток управления, и в следующий раз его судьба окажется более удачной».

СБ: А как это представляется в коде?

БЛ: Обычно посредством механизмов управления потоками, которые требуются при написании любого многопотокового приложения. Поток может закончиться, и тогда менеджер потоков скажет: «О, этот поток относился к такому виду потоков, которые нужно перезапускать, если они заканчиваются».

Например, в системе баз данных вы можете подать в систему некоторый оператор, а система может разволноваться и сказать: «Ой, я не могу разобраться с синтаксисом этого оператора». Это ошибка пользователя, и мы были бы счастливы сообщить ему это, но потоку, который мог бы это сделать, нужно принимать на обработку другой оператор или даже повторно тот же самый оператор.

Если посмотреть на другой край спектра, то может выйти из строя диск журнала, и мы не сможем писать в журнал записи. В этот момент лучше приостановить все шоу.Должна иметься возможность перезапустить программное обеспечение и начать обеспечивать сервис на резервной аппаратуре. Нам требуется справляться с аварийными ситуациями масштаба вычислительной системы. Обо всем этом нужно задумываться с самого начала разработки приложений и подсистем.

Прокомментировать>>


Содержание раздела