За последние полгода я уже неоднократно наблюдал, как инженерию определяли через внешние признаки: должности, стек, процессы, лексикон. Неудивительно, что такое определение не всегда работает, поскольку существуют организации, где всё перечисленное есть, а инженерии как таковой (и ожидаемых от её функционирования результатов) нет, при том что известны и обратные случаи. Ниже предлагается определение через устройство: три вопроса, ответы на которые позволяют понять, с чем имеем дело (инженерия или карго-культ). Ни один из них не требует ни специального инструмента, ни кода, ни пятилетнего плана, ни бюджетов уровня Т-Банка, потому что каждый показан на устройстве самих вещей: на определении, на арифметике, на документированном случае.
Вокруг любой технической системы накапливаются артефакты трёх видов: числа, по которым о системе судят, утверждения о её свойствах и действия в ответ на отклонения. Разница между инженерией и деятельностью, внешне на неё похожей, видна по устройству этих артефактов и в каждом случае сводится к одному вопросу.
Вопрос первый: что именно измеряет это число?
У любой метрики есть две части. Первая часть отвечает за процедуру: что и как считаем. Вторая часть содержит утверждение о связи: почему полученное число говорит о том предмете, который вынесен в название метрики. Процедура есть всегда, иначе не было бы числа. Утверждение о связи стоит отдельно: оно либо проверено, либо принято на веру. Этому различению почти сто лет (его сформулировал физик Перси Бриджмен), однако в повседневной практике оно забыто настолько прочно, что вторую часть метрики обычно вообще не осознают как существующую.
Проверим на самой обычной метрике, на среднем времени ответа. Возьмём для определённости сто запросов, из которых девяносто девять обработаны за 50 мс, а один провалился в пятисекундный тайм-аут. Среднее составит (99 * 50 + 5000) / 100 = 99,5 мс, и в этом числе не осталось никакого следа того, что каждый сотый пользователь ждал целых пять секунд. Здесь нет ни ошибки измерения, ни статистического парадокса: среднее арифметическое по своему определению учитывает событие пропорционально его величине, а не его тяжести для пользователя. Пятисекундный запрос добавил к итогу ровно 49,5 мс, то есть столько же, сколько добавило бы равномерное замедление всех ста запросов на те же 49,5 мс. По этому числу нельзя отличить систему, где всем одинаково терпимо, от системы, где каждому сотому невыносимо, хотя и для самих пользователей, и для бизнеса это два разных мира.
Стандартная поправка “смотреть на перцентили” верна, но недостаточна, потому что перцентиль остаётся процедурой, у которой есть собственные условия применимости. Величина p99 по определению отвечает на вопрос, быстрее какого значения обработаны 99% запросов совокупности, поэтому всё, что целиком умещается в оставшийся один процент, в неё по построению не попадает. Пусть кластер состоит из двухсот шардов и один из них деградировал на порядок: его собственный p99 вырос со 100 мс до секунды с копейками. Трафик этого шарда составляет полпроцента совокупного трафика, то есть меньше отсекаемого хвоста, поэтому агрегатный p99 по кластеру не сдвинется сколько-нибудь заметно, хотя каждый пользователь дефектного шарда получает десятикратную деградацию на каждом своём запросе. Дефект не в перцентиле как таковом, а в выборе совокупности: вопрос “p99 чего именно, кластера, шарда или пользователя” и есть тот же вопрос о связи процедуры с предметом, только заданный на уровень глубже.
Так же устроен и опрос /health: процесс вернул код 200 на служебный запрос, и на этом основании сервис считается живым. Процедура измеряет способность вернуть код 200. Метрика при этом называется “доступность сервиса”, хотя сквозной путь пользователя, от балансировщика через очереди до записи в хранилище и обратно, в измерение не входит вовсе.
Первый вопрос, таким образом, звучит не как “какая у нас метрика”, а как “чем проверено, что эта процедура измеряет названный предмет: на этой совокупности, в этом окне, при этой агрегации”. Число, у которого проверена только процедура, но не связь с предметом, представляет собой не знание о системе, а ритуал с числом (та самая “симуляция” из заголовка).
Вопрос второй: при каких условиях это утверждение ложно?
Инженерное описание системы устроено как карта возможного и невозможного: оно объявляет, какие исходы невозможны, и тем самым само указывает способ своей проверки. Описание “схема репликации переживает отказ любого одного узла без потери подтверждённых записей” является инженерным, поскольку из него прямо следует процедура проверки: отключить узел и сверить данные. Описание “мы повысили надёжность” инженерным не является, поскольку не существует наблюдения, способного его опровергнуть, а утверждение, совместимое с любым исходом, не сообщает о системе ничего. Этот критерий сформулировал Карл Поппер, разграничивая науку и её имитации, и к инженерии он применим даже жёстче, чем к науке: опровержение научной теории стоит её сторонникам репутации, тогда как опровержение инженерного описания или утверждения приходит в форме потерянных данных или остановленной промышленной системы со всеми вытекающими последствиями.
Разберём это на самой фундаментальной из инженерных гарантий, на утверждении “данные записаны”. Тернистый путь записи данных многослоен, и на каждом слое слово “записано” означает разное. Системный вызов write() возвращает управление после того, как данные скопированы в страничный кэш ядра; записанных данных на носителе в этот момент ещё нет, а при сбое питания эти данные исчезают. Вызов fsync() возвращает управление только после передачи данных носителю, однако между ядром ОС и пластинами или ячейками носителя находится энергозависимый кэш самого устройства, и без корректной передачи команд сброса (FLUSH, FUA) всё “записанное” остаётся в оперативном буфере носителя. Ниже расположен RAID-контроллер: содержимое его кэша переживает сбой питания только при исправной батарейке резервного питания. А в виртуализации под всем этим ещё есть и гипервизор, и выбранный на его стороне режим работы виртуального диска модифицирует гарантии всех слоёв выше как угодно.
Иными словами, “записано” представляет собой не факт, а цепочку утверждений, у каждого из которых есть собственные условия ложности. Индустрия знает это не из теории: в 2018 году (внезапно!) выяснилось, что PostgreSQL при определённых ошибках ввода-вывода годами мог терять данные, т.к. ядро Linux после неудачного fsync сбрасывало флаг ошибки, а повторный fsync возвращал успех, при этом PostgreSQL считал записанными те данные, которых на диске не было. История известна как fsyncgate, и существенно в ней не то, какой слой оказался виноват, а то, каким образом дефект был найден: люди целенаправленно искали условия, при которых утверждение “если fsync вернул ноль, то данные на носителе” ложно. Поскольку утверждение было сформулировано как опровержимое, его удалось опровергнуть раньше, чем оно успело проявиться невосполнимой потерей.
Процедура опровержения для этой гарантии собирается из стандартных средств за один вечер. Стенд состоит из виртуальной машины qemu, у которой диск настраивается в двух вариантах: cache=unsafe, когда гипервизор подтверждает запись, удерживая её в памяти хоста, и cache=writethrough. Внутри машины работает fio с параметром fsync=1 и ведёт журнал каждой подтверждённой записи. Снаружи процессу qemu в произвольный момент под нагрузкой отправляется сигнал SIGKILL (kill -9), а после рестарта журнал подтверждений сверяется с фактическим содержимым диска. В первом варианте обнаруживаются записи, которые система подтвердила и которых не существует, во втором таких записей нет. Эта разница между двумя конфигурациями и есть цена слова “записано”, измеренная экспериментом, а не вычитанная из документации.
Тот же критерий немедленно сортирует и утверждения, далёкие от системных вызовов. При каких условиях ложно утверждение “у нас есть бэкапы”? Если процедуры восстановления на чистое железо со сверкой результата не существует, то у утверждения нет условий ложности, а значит, нет и содержания: оно сообщает о настроенном и выполняемом расписании копирования, но не о возможности восстановиться.
Второй вопрос, таким образом, требует, чтобы у каждого утверждения о системе существовала процедура, способная его проверить и опровергнуть, и чтобы эта процедура была исполнена раньше, чем её исполнит Его Величество Случай (очень громкое и накладное исполнение).
Вопрос третий: перед нами поведение системы или событие в ней?
Любая работающая система обладает присущим ей разбросом: латентность колеблется, малая доля запросов всегда завершается ошибкой, глубина очередей меняется от минуты к минуте. Разброс представляет собой не происшествие, а свойство устройства. Происшествием является выход поведения за границы присущего разброса. Чтобы отличать одно от другого, нужна модель: какой разброс системе присущ и где проходит граница, за которой отклонение перестаёт быть случайным. Для этого различения Уолтер Шухарт ещё в 1920-х годах построил контрольные карты, а Уильям Эдвардс Деминг на простом механическом опыте показал, что происходит при управлении без такой границы.
Опыт Деминга устроен так: над мишенью закреплена воронка, в неё бросают шарик, и шарик падает с естественным случайным разбросом вокруг точки прицеливания. Дальше сравниваются две стратегии: в первой воронку не трогают, и разброс попаданий равен собственному разбросу падения шарика; во второй после каждого броска воронку сдвигают в сторону, противоположную последнему промаху, то есть применяют самое тривиальное управленческое правило (компенсировать отклонение). Результат у второй стратегии хуже, чем у первой, и причина видна без всякой математики: каждое новое попадание теперь складывается из двух случайных промахов, сегодняшнего собственного и вчерашнего, перенесённого рукой оператора. Итоговый разброс возрастает примерно в полтора раза (точное значение равно корню из двух). Существенно, что это свойство самого правила, а не его неудачного исполнения: любая реакция на случайное отклонение как на неслучайное добавляет к сегодняшнему шуму вчерашний (предыдущий), причём правило “компенсируй последнее отклонение” в этом семействе ещё самое щадящее.
В переводе на повседневность: перенастройка после единичного всплеска не нейтральна, она вносит в систему дополнительную изменчивость. После нескольких кварталов таких перенастроек конфигурация системы становится суммой разрозненных реакций на случайные события, представление о её нормальном поведении утрачивается, и вместе с ним утрачивается возможность заметить настоящую поломку (истинный источник проблемы) на фоне рукотворного шума.
Способ отличать одно от другого известен столько же, сколько сама проблема. Из собственного прошлого поведения процесса берётся типичная величина его колебаний от замера к замеру, и граница проводится с троекратным запасом относительно этой величины, поэтому случайный шум за неё практически не выходит. Точка внутри границ означает, что система ведёт себя в пределах присущего ей разброса; причинного объяснения у такой точки нет, и единственное правильное действие состоит в том, чтобы не вмешиваться. Точка за границей или устойчивый сдвиг целой серии точек означает событие, у которого есть причина и которое поэтому имеет смысл расследовать. Граница превращает вопрос “реагировать или нет” из предмета спора в предмет вычисления.
Третий вопрос, таким образом, звучит так: известен ли присущий системе разброс, и проходит ли каждое отклонение проверку на выход за его границы прежде, чем породить действие?
Общий корень
Три вопроса выглядят разными (про метрики, про утверждения, про реакции), но упираются в одно и то же: существует ли модель устройства, то есть понимание того, как и почему система делает то, что делает. Без модели нечем проверить связь числа с предметом, неоткуда взять условия ложности и проверяемости утверждения, и не из чего вычислить границу присущего разброса. Все три дефекта оказываются одним дефектом, наблюдаемым с трёх сторон.
Отсюда определение, ради которого писался текст: Инженерией называется деятельность, обладающая тремя описанными свойствами. Во-первых, у неё есть модель механизма, из которой следуют проверяемые предсказания. Во-вторых, последнее слово в ней принадлежит самой системе: система держит нагрузку или падает независимо от того, кто и с каким авторитетом утверждал обратное, и “договориться” с этим исходом невозможно. В-третьих, она опровержима по механизму, то есть способна сказать, почему система упала, и предсказать, при каких условиях упадёт снова.
В этом определении намеренно нет ни слова об Инженерии как о профессии, поскольку речь идёт именно о способе работы с системами, а граница способа проходит поперёк должностей: встречаются администраторы с двадцатилетним стажем, ни разу не задавшие (ни себе, ни другим) ни одного из трёх вопросов, и встречаются люди, год назад вышедшие из ВУЗа с дипломом, но задающие (себе; другим; и себе, и другим) эти вопросы системно. Должность выдаётся приказом, чего не скажешь о способе работы: он либо практикуется, либо нет.
Инженерия и неполнота знания
Здесь полагается справедливое возражение: а сама-то инженерия всё знает о своих системах?
Нет: полного знания у инженерии нет, и, что ещё существеннее, она на него и не претендует, однако распоряжается этим обстоятельством иначе, чем варианты её имитации.
Полное знание недостижимо в принципе: реальная система всегда сложнее своей модели, среда меняется, отказы приходят из неучтённых местностей, которых нет на карте. Отличие инженерии не в том, что она знает больше других, а в том, что она устроена вокруг неполноты знания: запас прочности осознанно закладывается именно там, где модель может ошибаться. Контрольная сумма существует потому, что “записано” является гипотезой, которую надо проверять, а не фактом, в который надо свято верить. Отказоустойчивость исходит из презумпции, что компоненты всё равно откажут, причём способом, которого нет в документации (Мерфи, привет!). Мониторинг устроен как непрерывная сверка модели здоровья с реальностью, а контрольные границы отличают ситуацию “система шумит в пределах ожидаемого” от ситуации “модель сломалась”. Postmortem работает как механизм, втягивающий неучтённое знание в модель, чтобы незнание уменьшалось проверяемо, а не декларативно (слова на стене). И инженер говорит “это работает до такой-то нагрузки при таких-то условиях”, а не “ну, это работает”. Опыт и доверие к проверенным решениям инженерия тоже использует, но любое доверие здесь пересматривается самой системой: если она упала, то решение признаётся ошибочным, чьим бы оно ни было.
Поэтому честный вопрос к любой деятельности, называющей себя инженерией, звучит не как “всё ли вы знаете?”, а как “что у вас сделано на случай обнаружения того, чего вы не знаете?”. У инженерии на этот вопрос есть предметный ответ: избыточность, проверки, неизбежность отказа, явные границы применимости. Неполнота знания остаётся общим условием для всех, разница лишь в том, что одни свою неполноту инструментируют (закладывают избыточность, обвешивают мониторингом e2e, вносят в модель через разборы Postmortem), а другие её маскируют, перекрашивая незнание в уверенное число, сбой в зелёную панель (т.н. арбузный мониторинг), в единогласное решение “уважаемых людей”. Первое является инженерией, а второе лишь симуляцией из заголовка.
Три вопроса из этого текста не образуют ни методики, ни фреймворка: внедрять их некуда, сертификата по ним не существует, продать это нельзя. Это проверки, которым подлежит каждый артефакт инженерной работы: число проверяется на связь с предметом, утверждение проверяется на условия ложности, а реакция на отклонение проверяется на выход за границу присущего разброса. Две из трёх проверок выполняются на бумаге, третья требует стенда, который собирается за вечер, и этого достаточно, чтобы отличать деятельность, понимающую свои системы, от деятельности, которая ими только оперирует.
ссылка на оригинал статьи https://habr.com/ru/articles/1043616/