Files
progcode/editorial/agent-rewrites/004.json
T
huncode 2d914b543f
Build and deploy / deploy (push) Failing after 15s
Publish rewritten technical article archive
2026-08-02 22:19:34 +03:00

2 lines
16 KiB
JSON
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
{"index":4,"slug":"editorial-2027-11-field-mistakes-revisions","title":"Incident runbook: как пройти от симптома к проверенному rollback","excerpt":"Практический маршрут инцидента: зафиксировать симптом и границу влияния, проверить одну гипотезу, выполнить обратимое действие и убедиться, что восстановилась система, а не только один график.","contentHtml":"<p>Проблема во время инцидента начинается с фразы «сервис упал после релиза». Она смешивает наблюдение, время и причинность. На практике симптом может выглядеть иначе: HTTP 503, рост p95 latency, очередь сообщений или ошибки только у одного метода и в одном регионе.</p><p>Цена такой неточности — лишние изменения и потеря следов причины. Оператор перезапускает всё подряд, меняет несколько параметров или откатывает код, не проверив совместимость со схемой базы. Ошибка исчезает на минуту, а команда не знает, что именно помогло и не осталось ли повреждённое состояние.</p><h2>Тезис: runbook должен ограничивать неопределённость</h2><p>Хороший runbook не угадывает первопричину за оператора. Он превращает тревожный сигнал в короткий цикл: зафиксировать факт, сузить область, сформулировать гипотезу, выполнить одну обратимую проверку и измерить результат. Если гипотеза не подтверждается, инструкция должна вести к следующей проверке, а не подталкивать к тому же действию.</p><p>Начинайте с полей, которые можно заполнить без интерпретации: timestamp, affected endpoint, регион, версия, доля ошибок, latency, saturation и baseline. Запись «всё медленно» не задаёт ни области поиска, ни критерия улучшения. Запись «POST /payments, EU, 5xx 8%, p95 1,8 с, baseline — предыдущее окно» уже позволяет выбрать следующий шаг.</p><h2>Механизм: сигнал, гипотеза, действие, восстановление</h2><p>Статус 500, задержка и error rate описывают разные стороны одного события. Они могут иметь общий корень, но не обязаны. Поэтому классификация сигнала должна быть детерминированной и прозрачной: высокий приоритет получает отказ или превышение error threshold, затем проверяется latency threshold. Классификатор помогает выбрать срочность, но не доказывает причину.</p><p>Гипотеза связывает наблюдение с проверяемым признаком: «после изменения лимита pool выросло ожидание соединения; проверка — метрика pool wait в том же временном окне». В формулировке должны быть условие и ожидаемый сигнал. Гипотеза «виноват backend» слишком широкая: её нельзя опровергнуть одним локальным измерением.</p><p>Первое изменение должно уменьшать blast radius. Подойдут отключение feature flag, остановка нового consumer или перевод небольшой доли трафика на старую версию — если такой путь предусмотрен архитектурой. Перезапуск без фиксации метрик может убрать симптом и одновременно удалить полезный контекст.</p><figure><img src=\"/assets/editorial/2027/mistakes-revisions-2027-evidence-handoff-loop.svg\" alt=\"Петля incident runbook: симптом и область влияния переходят в проверку гипотезы, одно обратимое действие и измерение восстановления\" loading=\"lazy\" /><figcaption>Каждое действие в runbook должно иметь ожидаемый эффект, условие возврата и отдельную проверку результата.</figcaption></figure><h2>Минимальный рабочий пример</h2><p>Ниже — небольшая чистая функция. Она принимает HTTP status, latency и error rate, применяет два явно заданных порога и возвращает классификацию. Числа в примере учебные: функция не знает бизнес-критичность endpoint, не отправляет уведомления и не выбирает rollback.</p><pre><code>function classifySignal({ status, latencyMs, errorRate, latencyLimitMs = 1000, errorLimit = 0.05 }) {\n if (!Number.isInteger(status) || !Number.isFinite(latencyMs) || !Number.isFinite(errorRate)) {\n return { ok: false, reason: 'signal-invalid' };\n }\n if (status &gt;= 500 || errorRate &gt;= errorLimit) {\n return { ok: true, severity: 'high', reason: 'availability-or-error-threshold' };\n }\n if (latencyMs &gt;= latencyLimitMs) {\n return { ok: true, severity: 'medium', reason: 'latency-threshold' };\n }\n return { ok: true, severity: 'low', reason: 'signal-below-threshold' };\n}\n\nconsole.log(classifySignal({ status: 503, latencyMs: 820, errorRate: 0.08 }));\n// { ok: true, severity: 'high', reason: 'availability-or-error-threshold' }</code></pre><p>Проверяемость здесь важнее полноты. Для одинакового входа функция возвращает одинаковый результат; невалидный status или нечисловая метрика не превращаются в уверенный severity. Реальный runbook должен отдельно описать источник каждой метрики, допустимое окно и владельца действия.</p><h2>Карта диагностики</h2><div class=\"table-scroll\"><table><caption>Симптом → причина → проверка → действие</caption><thead><tr><th scope=\"col\">Симптом</th><th scope=\"col\">Возможная причина</th><th scope=\"col\">Проверка</th><th scope=\"col\">Первое действие</th></tr></thead><tbody><tr><td>5xx растёт сразу после выката</td><td>Новая версия или flag затронули часть трафика</td><td>Сравнить версии, долю трафика и error rate по endpoint</td><td>Ограничить flag или traffic split, затем повторить измерение</td></tr><tr><td>p95 растёт, 5xx не меняется</td><td>Ожидание соединения, CPU или внешнего ответа</td><td>Сопоставить latency breakdown, saturation и pool wait</td><td>Не перезапускать всё; убрать один изменённый лимит, если он подтверждён</td></tr><tr><td>Очередь увеличивается после отката</td><td>Consumer остановлен или не справляется с потоком</td><td>Проверить lag, rate обработки и ошибки consumer</td><td>Вернуть только совместимый consumer или остановить входной поток по policy</td></tr><tr><td>5xx снизился, операции не завершаются</td><td>Сервис доступен, но есть незавершённое состояние</td><td>Сверить успешные операции, очередь, дубли и состояние данных</td><td>Не закрывать инцидент; запустить recovery-проверку и сохранить факты</td></tr><tr><td>Timeout при rollback</td><td>Изменение не применилось или control plane недоступен</td><td>Проверить фактическую версию и audit log, а не только код команды</td><td>Остановить новые изменения и выбрать заранее описанный ручной путь</td></tr></tbody></table></div><p>В таблице указаны направления проверки, а не универсальные причины. Один симптом может иметь несколько объяснений. Оператор должен подтвердить выбранную ветку своим сигналом и записать результат рядом с timestamp.</p><h2>Rollback не равен восстановлению</h2><p>Rollback возвращает конфигурацию, feature flag или binary. Recovery означает, что система снова выполняет допустимую работу и данные остаются согласованными. Можно успешно вернуть старую версию и оставить очередь, двойные записи или несовместимые события. Поэтому после rollback проверяйте не только 5xx, но и lag, успешность операций, saturation и консистентность данных.</p><p>До отката проверьте совместимость. Старая версия должна читать текущую схему базы и понимать события, которые уже создала новая версия. Если перед инцидентом была миграция, порядок действий нельзя восстанавливать по памяти: в runbook нужны матрица совместимости, ожидаемый эффект, риск и способ вернуть сам rollback.</p><p>Критерий завершения должен быть измеримым. Формулировка «график выглядит лучше» не подходит. Нужны конкретные условия: error rate ниже согласованного порога, latency вернулась к допустимому диапазону, очередь не продолжает расти, а контрольная бизнес-операция проходит. Интервал наблюдения зависит от окна метрики и характера нагрузки; его задают заранее, а не в момент усталости.</p><h2>Порядок действий</h2><ol><li>Запишите timestamp, scope, версию и один измеримый симптом. Сохраните ссылку на dashboard и baseline.</li><li>Проверьте границу влияния: регионы, методы, версии, долю трафика и затронутые зависимости.</li><li>Сформулируйте одну гипотезу и одну проверку. Для проверки заранее назовите сигнал, который должен измениться.</li><li>Выберите одно обратимое действие. Запишите ожидаемый эффект, риск и условие возврата до выполнения команды.</li><li>После действия выдержите заданное окно и сравните error rate, latency, saturation, очередь и бизнес-сигнал.</li><li>Если сигнал не улучшился, остановите ветку и зафиксируйте отрицательный результат. Не добавляйте второе изменение, пока не понятен эффект первого.</li><li>После стабилизации сохраните timeline, фактическое состояние версии и оставшиеся риски. Не удаляйте временные изменения до проверки recovery.</li></ol><h2>Ограничения и критерий готовности</h2><p>Этот маршрут не заменяет on-call график, права доступа, SLO, уведомления и локальную матрицу severity. Классификатор не видит traces, не отличает бизнес-критичный endpoint от второстепенного и не знает, можно ли безопасно повторить операцию. Порог 5% и latency 1000 мс в примере не являются рекомендацией для конкретной системы.</p><p>Runbook готов, если оператор может без устного контекста ответить на пять вопросов: какой факт зафиксировать, где проходит граница влияния, какую гипотезу проверить, какое действие обратимо и по какому сигналу считать recovery подтверждённым. Для каждого опасного шага должны существовать условие остановки и путь возврата.</p><p>Минимальная проверка инструкции — проиграть один сценарий на тестовом окружении с контролируемым 503 и пройти ветку до конца. Такой прогон проверяет структуру runbook, но не доказывает поведение production, отсутствие флаков или безопасность реального rollback. Фактические результаты конкретной системы нужно приложить отдельно.</p><h2>Проверяемые источники</h2><ul><li><a href=\"https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-61r2.pdf\" target=\"_blank\" rel=\"noopener noreferrer\">NIST SP 800-61 Revision 2 — Computer Security Incident Handling Guide</a> — структура обработки инцидента: подготовка, обнаружение и анализ, containment, eradication/recovery и post-incident activity. Документ не задаёт архитектуру, пороги severity или права на rollback конкретной системы.</li><li><a href=\"https://www.rfc-editor.org/rfc/rfc9110.html\" target=\"_blank\" rel=\"noopener noreferrer\">RFC 9110 — HTTP Semantics</a> — семантика HTTP-методов, статусов и условных запросов. Документ не является runbook и не заменяет метрики, логи, traces и проверку конкретного сервиса.</li></ul>"}