Практика минимизации ошибок

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

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

1. «Выполни и умри!» (Do and Die)

Это уже давно известный шаблон, который, тем не менее игнорируют постоянно. Смысл в том что приложение разделяется на несколько модулей — процессов или нитей (которые Thread) взаимодействующих друг с другом, но способных выживать независимо. Эти процессы делятся на центральный (диспетчек) и рабочие процессы выполняющие «грязную работу» перемалывания данных. В основном данный подход применим к серверным приложениям, что впрочем не отменяет его полезности для разного типа программ.

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

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

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

2. «Большой брат» (Big Brother)

Когда приложение является сложным, взаимоувяанным и разнесённым по разным серверам и географическим площадкам, то неизбежно возникают ситуации когда где-то запрос теряется, не принимается или же принимается некорректно. И хорошо ещё если это «one vendor solution» где все транзакции на виду, но куда чаще это интеграция нескольких приложений и слишком часто я лично наблюдал как создатели чешут затылок и часами выясняют в чём же проблема в их Web, Cache, SOAP, WSDL, Remoting, Corba монстроидальном приложении.

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

При этом обеспечить такой удалённый мониторинг не столь уж сложно, мне довелось в своё время как создавать его вручную для нескольких задач, так и использовать такой продукт как AdventNet OpManager который бесплатен до определённого числа пробов. Было несколько проектов когда это реально спасало лично мне в итоге много нервов. Я знал что если что-то идёт не так, я или служба поддержки получим об этом сообщение.

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

— в файл;

— в таблицу в БД;

— публикуя показатели через SNMP;

— через proc, если Linux

— через веб страницу с возможностью машинной обработки

3. «Большой брат с дубинкой» (Big Brother with a Stick)

По аналогии с подходом Большой брат в данном случае помимо отслеживания критических состояний и уведомления о проблемах ещё и принятие немедленных мер по их устранению. Зависла служба — перезапустить службу. Потеряна связь с интернетом/другой сетью перезапустить роутер/модем по необходимости. И так далее.

С подобной логикой я сталкивался не один раз и каждый раз это были системы работающие годами. «Красота кода» каждого отдельного модуля вцелом мало кого волнует. Главное чтобы всё работало вместе.

Зед Шау которые разряжался ранее наездом на Ruby on Rails и приводивший пример что Mongrel падал сотни раз на работающем сайта, так вот этот самый Зед Шау — это и есть пример талантливого, но долбанутого, перфекциониста. Даже если бы этот веб сервер перезагружался внешним скриптом сотни раз в час, но работал — это важнее для production системы чем любые рассуждения о ошибкам и необходимости их устранения.

4. «Трасса» (Trace)

Трасса — от слова трассировка. Не просто отладочный лог, а отладочный лог с записью на каждый чих. На вход и выход из процедуры/функции и на все существенные активности внутри.

Когда я видишь подобную конструкцию впервые, первая мысль это: «Господи, куда я попал…» и только после первой дюжины ошибок выловленных только благодаря трассировке понимаешь зачем это всё таки нужно и насколько _хреново_ когда этого нет.

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

5. «Вначале тесты потом код»

То что agile методики рекомендуют писать тесты, хотя бы в виде, mock до того как будут писать сам код — я думаю все и так знают. Типовые ответы начинающих разработчиков на вопрос о подобных тестах зачастую заканчиваются одним из двух ответов:

a. «Я не тестировщик, пусть они этим занимаются».

b. «Зачем мне тратить время на тесты? У меня и так всё работает.»

Если первое ещё хоть как-то поддаётся убеждению, по факту того что тесты QA — это другие тесты. Они не будут проверять внутреннюю целостность программы, внутренние функции и так далее — максимум внешнее API. И тесты своего кода разработчику необходимо проделать не для внешнего тестирования и багтрекинга, а для того чтобы другой человек в команде с ним был уверен что этим кодом можно пользоваться.

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

5. «Далёкий журнал» (Remote journal)

Вести трассу правильно и нужно. Ситуация усугубляется в тех случаях когда приложение является распределённым и в нём все те же проблемы мониторинга и отладки. Часть их них можно закрыть используя подход «Большой брат», но при выявлении ошибок практически всегда это заканчивается десятками открытых окон и сидением с сопоставлением трасс/логов с разных серверов.

Человечество не так уж тупо и системы ведения удалённых журналов существуют не один год, как бесплатные и стандартные вроде syslog, syslog-ng, EventLog так и обилие различных движков для Java, .NET и других.

Поэтому каждый раз когда я вижу очередной самопальный механизм ведения журнала, я задаюсь вопросом — этот парень действительно ненавидит всё живое или это только временное явление?

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

6. «Гарантированное восстановление» (Guaranteed Recovery)Однажды мне пришлось работать над продуктом с весьма низким уровнем управляемости. Это был тематический сервер включая железку и единственные возможности общения с ним были через консольный порт и через веб-интерфейс.

Но, как и любой сервер его необходимо обновлять, при этом обновление может затронуть и веб интерейс, собственно и как в этом случае быть если наступает жопа веб интерфейс перестаёт работать после обновления? Это почитай сразу кричи караул, пока не прибудет человек из техподдержки, ничего поделать будет невозможно. Решалось это в итоге довольно радикально. В отличии от веб интерфейса, скрипт на консольном порту был сделан предельно тупым максимально простым. Хотя и залить старую версию или обновление через консольный порт было бы морокой, вместо этого при обновлении всегда делалась _полная копия_ всех файлов предыдущей и консоль позволяла откатить изменение назад к предыдущей работоспособной версии.

Аналогичный принцип работает со всеми обновлениями серверного ПО. Даже если Вы заменяете на серверу пару файликов со _100 %_ хорошим исправлениями, то более чем полезно при этом сохранить те файлы которые Вы заменяете в отдельную директорию с пометкой когда и зачем Вы это сделали. 5 минут которые уйдут на эту ерунду, будут сущим пустяком по сравнению с тем что случится если это 100% хорошее исправление сломает, например, биллинг у любимого клиента, а взмыленный саппорт в непонимании будет названивать Вам пока Вы спокойно спите.

Конечно, список неполон, в нём нехватает ваших примеров.

About This Author

Яндекс.Метрика