Математика катастрофы: от дифференциальных уравнений до очередей G/G/1 и формулы Кингмана

Знакомая картина: в 3 часа ночи падает база данных, Alertmanager засыпает каналами, через 5 минут сеть восстанавливается, но… система остаётся «мёртвой». CPU на воркерах 100 %, Goodput на нуле, а перезапуск подов только усугубляет ситуацию. Все указывает на то, что вы столкнулись с метастабильным отказом — самым коварным типом аварий в распределённых системах.
Меня зовут Артём Баранов, в «Базисе» я занимаюсь автоматизацией и развитием инфраструктуры. В этой статье я хочу рассказать о математике метастабильного отказа и через теорию массового обслуживания — конкретно через модель и формулу Кингмана — проложить мост от математических абстракций к реальным логам, метрикам и алертам. Далее, предложить практикующему SRE-специалисту набор методик для анализа, диагностики и предотвращения катастроф, возникающих в информационных системах по тем или иным причинам (ошибки конфигурирования, сбои «железа» и т. д.). Это позволит инженеру распознать sustaining-эффект по логам и метрикам и осознанно выбрать рычаг (backoff, retry budget, shedding, circuit breaker), а не просто объяснить постфактум, почему возникла предыдущая авария.
Несколько замечаний, прежде чем мы начнем
Поводом для написания данной статьи послужила публикация «Когда повторы убивают: метастабильные отказы в распределённых системах», которая знакомит читателя с метастабильном отказом. Ее можно использовать для получения общего представления об этом эффекте.
Второе, эта статья — инженерная компиляция нескольких моделей (fluid-ODE, Линдли, Max-Plus, аппроксимация Кингмана), а не единое строгое доказательство. Каждая модель отвечает на свой вопрос и имеет жёсткую зону применимости; ниже мы явно фиксируем эти границы.
Третье, рассмотреть весь материал, в полной мере освещающий проблематику метаотказов, в рамках одной статьи невозможно. Формальные методы (TLA, model checking, алгебры процессов) сознательно оставлены за рамками: здесь они упомянуты как карта местности, но не разворачиваются.
1. Складка, гистерезис и два аттрактора
Классический пример бистабильности и аттракторов в метастабильных отказах — это система обработки интернет-трафика (микросервис или база данных) с механизмом автоматических повторов (retries). При временном всплеске нагрузки система из здорового состояния переходит в состояние «залипания» с нулевой пропускной способностью, где и остаётся даже после исчезновения исходной причины сбоя. Именно этот механизм и называется метастабильностью: внешний триггер уже исчез, но внутренняя петля (сустейнер) продолжает питать аттрактор отказа.
С точки зрения теории катастроф это — складка (fold): множество стационарных решений при изменении
образует на плоскости
характерную ветвящуюся кривую — в классической картине с двумя устойчивыми ветвями и одной неустойчивой между ними (оговорки о числе корней — ниже и в разд. 2; рис. 1 построен по двухпороговой модели из того же раздела).
Точки складки и гистерезис
Устойчивая ветвь смыкается с неустойчивой в точках складки; между ними сосуществуют три равновесия, вне них — одно. Разница по между верхней и нижней точками и есть гистерезис: чтобы выйти из сбойного режима, нужно снизить не до порога срыва, а значительно ниже. Именно поэтому снятие триггера само по себе не возвращает систему в норму.
Что определяет форму складки
Амплитуда складки по растёт с
— интенсивностью повторов. При
складка вырождается в монотонную кривую, и бистабильность исчезает: без повторов метастабильного отказа не бывает. Крутизна перехода задаётся
(жёсткостью клиентского таймаута); сдвиг по оси
— параметром
(порог срабатывания повторов). Глубина N-образности функции
определяется параметром
в аппроксимации
: при
функция монотонна, и складки нет вовсе (см. разд. 2).
Форма кривой на рис. 1 — ветви равновесий двухпороговой модели из численного примера разд. 2 (не схематический кубический fold). Для строгой S-складки с двумя устойчивыми внутренними ветвями нужна ещё и ; наша модель даёт бистабильность нуля и
(см. замечание о классической S-складке в разд. 2). В обоих случаях — с одним или двумя порогами — бистабильность возникает при наличии петли повторов: это качественный механизм, а не конкретная форма кривой.
Однопараметрический срез
В общем случае поверхность равновесий живёт в пространстве параметров и устроена сложнее S-образной складки: при изменении нескольких параметров форма поверхности может меняться качественно. Для практических выводов статьи достаточно однопараметрической картины: мы рассматриваем складку как срез многомерного пространства по одному параметру при фиксированных остальных. Именно поэтому защитные механизмы из разд. 8 формулируются как способы изменить саму форму складки (retry budget и backoff уменьшают ; shedding снижает эффективную и возвращает рабочую точку в область, где складки нет), а не как способ её «обойти».
Фазовое пространство и два аттрактора
Система обладает бистабильностью, то есть имеет два устойчивых состояния равновесия (аттрактора), разделённых барьером:
Аттрактор 1 (здоровое состояние): низкая задержка, высокая пропускная способность, очередь запросов пуста.
Аттрактор 2 (отказ / livelock): задержка максимальна, полезный трафик равен нулю, очереди забиты до отказа.
Механизм перехода (как работает метастабильность)
Всплеск (триггер): кратковременный рост внешнего трафика или сбой одного узла заставляет систему замедлиться.
Петля обратной связи (сустейнер): клиенты начинают массово повторять таймаутнутые запросы. Повторы добавляются к реальному трафику.
Смещение барьера: дополнительный объём повторов переводит систему через разделительный порог (сепаратрису) в «бассейн притяжения» второго аттрактора.
Залипание: внутренний объём работы (повторы) полностью утилизирует ресурсы процессора и сети. Даже если внешний всплеск исчез, лавина внутренних повторов продолжает питать аттрактор отказа. Система не может вернуться в норму сама.
Сравнение состояний системы
Параметр | Аттрактор 1 (норма) | Аттрактор 2 (метастабильный отказ) |
|---|---|---|
Нагрузка | Внешняя полезная | Внутренние повторы |
Задержка (Latency) | Низкая (миллисекунды) | Стремится к бесконечности (таймауты) |
Пропускная способность | Максимальная | Около нуля |
Реакция на снятие триггера | Работает штатно | Система остаётся в отказе («залипла») |
Гистерезис проявляется в том, что при снятии триггера (возврате к норме) система не возвращается на нижнюю ветвь: она заперта на верхней, потому что для перехода нужно либо пройти обратную точку складки (жёсткий сброс нагрузки), либо изменить топологию — разорвать петлю повторов.
2. Математическая модель метастабильного отказа
Математически метастабильный отказ описывается через динамические системы и дифференциальные уравнения, где скорость изменения длины очереди запросов зависит от входящего потока и реальной производительности системы.
Базовое уравнение динамики очереди
Обозначим через текущую длину очереди запросов (или уровень нагрузки) в момент времени
. Изменение очереди во времени описывается уравнением
где:
— суммарный входящий поток запросов (функция генерации работы);
— реальная скорость обработки запросов системой (функция мощности обслуживания).
Границы применимости
Fluid-ODE предполагает : очередь рассматривается как непрерывная величина. При
модель теряет смысл — там работает дискретная рекурсия Линдли (разд. 3).
Повторы: функция генерации работы
При росте очереди растёт задержка, и клиенты начинают генерировать повторные запросы (retries). Суммарный поток состоит из базового полезного трафика и лавины повторов:
Здесь — предельная надбавка к базовому потоку от повторов (при полном насыщении сигмоиды).
— вероятность того, что запрос превысит таймаут: удобная модель поведения клиентов с фиксированным таймаутом. Вероятность превышения порога резко растёт, как только очередь переваливает через критический размер (возможны и другие формы — ступенчатая, кусочно-линейная — с тем же качественным эффектом). При
вероятность стремится к 1, поток повторов насыщается на уровне
.
Деградация: функция обслуживания
В идеальной системе скорость обработки постоянна. Но при перегрузке (рост ) ресурсы процессора тратятся на накладные расходы: контекст-свитчинг, сборку мусора, lock contention, очистку буферов. Производительность падает. Мы используем феноменологическую аппроксимацию функции обслуживания :
Это не универсальный физический закон планировщика ОС, а удобная кривая, которая ловит конкуренцию параллелизма и thrashing. Параметры: — предельная скорость обслуживания при ;
— масштаб нелинейности;
— степень ускорения деградации с ростом .
Параметр играет ключевую роль: он даёт слабый начальный рост (параллелизм), а дальше — падение. Функция
при этом становится немонотонной.
Важное уточнение о числе корней
С одной сигмоидой и одним горбом функция
имеет не более одного экстремума: производная
представляет собой разность колокола и функции с единственной сменой знака, поэтому
обнуляется не более одного раза. Это даёт не более двух корней
— но не классическую S-складку с тремя.
Полная S-складка с тремя корнями (два устойчивых равновесия и одно неустойчивое между ними) возникает при двух порогах срабатывания повторов: например, две группы клиентов с разными , дающие две сигмоиды в . Схематически ветви равновесий такой модели показаны на рис. 1; явный численный пример — ниже. В обоих случаях — с одним или двумя порогами — сама бистабильность возникает из-за петли повторов, а не из-за конкретной формы кривой: это ключевой качественный механизм.
Численный пример: три корня при двух сигмоидах
Утверждение о трёх корнях при двух порогах срабатывания повторов неочевидно; приведём явный пример. Возьмём
Тогда
Значения :
Последовательность знаков : , то есть три смены знака и, по теореме о промежуточном значении, три корня
на интервалах
,
,
; численно , , .
Об устойчивости трёх корней
Знак в окрестности корня определяет устойчивость равновесия : — устойчивое,
— неустойчивое. В нашем примере
то есть картина «неустойчивое — устойчивое — неустойчивое». Это надо прочесть так. Устойчивое равновесие одно — : это и есть «сбойный» аттрактор (аналог из разд. 1). Роль здорового аттрактора
играет граница : при
, и очередь дренируется к нулю. Точка
— сепаратриса
: если триггер вытолкнул систему правее , она сваливается в . Третий корень
— дополнительное неустойчивое равновесие: выше него очередь растёт без ограничения.
Замечание о «классической» S-складке
В нашем примере картина не полностью совпадает с классической кубической складкой, у которой два устойчивых внутренних равновесия разделены неустойчивым: для этого нужно , а в нашей модели . Качественный механизм — наличие порога (сепаратрисы) и залипание в устойчивом равновесии — сохраняется; в терминах аттракторов система по-прежнему бистабильна (нуль и ), только первый аттрактор лежит на границе области, а не во внутренней точке. Буквальная S-складка с двумя устойчивыми внутренними равновесиями требует дополнительной нелинейности — например, насыщения повторов при
или полки в ; это выходит за рамки статьи.
Инженерное примечание
В реальных ИТ-системах классическая бистабильность с двумя устойчивыми внутренними состояниями возникает даже при одной сигмоиде , если:
ёмкость очереди жёстко ограничена сверху (
): за этой границей поток обрезается, и приобретает разрыв;
функция деградации падает до нуля быстрее, чем насыщаются повторы: тогда у появляется второй горб и, соответственно, два экстремума.
В модели этой статьи мы используем аналитически более чистую двухпороговую конструкцию, но качественный вывод о петле повторов как источнике бистабильности сохраняется для обоих случаев.
Математически при имеем для всех
. Поскольку для сигмоиды
всегда (сигмоида монотонно возрастает), получаем
всюду: даже на плато, где
, вклад
сохраняет строгую монотонность . Следовательно, больше одного корня
быть не может: бистабильности в этой fluid-модели при не существует. Немонотонность из-за конкуренции «параллелизм
деградация» — необходимое условие складки; для полной S-складки с тремя корнями нужны ещё два порога повторов (см. выше).
Аттракторы: точки равновесия
Стационарные состояния системы — корни уравнения (точки пересечения кривой притока и кривой оттока). Число корней зависит от формы кривых: в однопороговой модели (одна сигмоида ) равновесие, как правило, единственное; для трёх равновесий нужны два порога срабатывания повторов (см. обсуждение выше).
Там, где три корня существуют, система качественно бистабильна: неустойчивый корень (сепаратриса) разделяет бассейны притяжения здорового и сбойного режимов. Ниже — каноническая карта / / (та же, что в разд. 1); как она ложится на численный пример с граничным аттрактором — сразу после списка.
В численном примере выше роль играет граница (аттрактор на краю области), роль — ; сепаратриса совпадает с . Классическая картина с внутренним требует дополнительной нелинейности (см. замечание о классической S-складке выше).
Сепаратриса — не то же самое, что порог таймаута : первая разделяет бассейны притяжения складки, вторая — порог срабатывания повторов.
Load shedding: математика выхода из аттрактора
Чтобы вернуть систему в здоровое состояние , нужно временно изменить топологию фазового пространства — уничтожить аттрактор . Это делается через сброс нагрузки (load shedding). Администраторы или алгоритмы принудительно режут входящий трафик до значения , при котором
для всех от текущего значения очереди до . Тогда
на всём пути к здоровому равновесию: очередь начинает разжиматься, и система «стекает» обратно. Как только очередь упала ниже , жёсткое ограничение трафика можно снимать — система вернётся в здоровый аттрактор .
Почему система остаётся в сбойном состоянии после снятия триггера
Система остаётся в сбойном состоянии из-за внутренней регенеративной петли обратной связи. Когда внешний триггер (всплеск ) исчезает, накопленная внутри системы «энергия» (очередь запросов) продолжает сама себя воспроизводить. В уравнениях этот эффект удобно разложить на три аспекта одного механизма.
1. Повторы от «прошлого» трафика (память системы)
Даже если новый внешний трафик вернулся к норме, в очереди уже находится огромное количество запросов. Клиенты, чьи запросы встали в очередь во время всплеска, не дождались ответа и получили таймаут. Вероятность для них близка к 1, потому что очередь физически превышает . Слагаемое выдаёт свой максимальный вклад: потока самого по себе достаточно, чтобы удерживать точку пересечения графиков в районе .
2. Падение полезной мощности (феномен деградации)
Из-за гигантской очереди реальная скорость упала до минимума. Сервер тратит почти все ресурсы CPU не на полезную работу, а на обслуживание самой перегрузки: прерывания от сети, переключение контекста между тысячами потоков, парсинг заголовков повторных запросов, которые он тут же откидывает по таймауту. Замкнутый круг: система обрабатывает слишком медленно, потому что очередь большая — очередь остаётся большой, потому что система обрабатывает слишком медленно.
3. «Бассейн притяжения» аттрактора
Исчезновение триггера лишь слегка смещает положение точки , но не уничтожает сам аттрактор. Текущая координата уже находится глубоко внутри бассейна притяжения сбойного аттрактора — правее сепаратрисы . Для возврата нужно преодолеть энергетический барьер, но у системы нет для этого внутренних ресурсов.
Три аспекта не независимы: память питает деградацию, деградация закрепляет бассейн притяжения. Но защитные меры адресуются каждому из них по отдельности.
Что это даёт на практике
Три «кита» этого раздела — память системы, падение мощности и «бассейн притяжения» — это три разных повода для аварии, и лечатся они по-разному. Память разрывается клиентскими рычагами (backoff, retry budget); деградация — серверными (shedding: снять нагрузку, пока не восстановится); бассейн притяжения — только жёстким сбросом ниже сепаратрисы . Если на инциденте вы видите только один из трёх признаков, этого недостаточно: sustaining-эффект держится одновременно на всех трёх, и защита должна закрывать каждый.
3. Переход к рекурсии Линдли
Если дифференциальное уравнение описывало систему непрерывно и макроскопически (fluid approximation), то рекурсия Линдли (Lindley’s recurrence) позволяет заглянуть на уровень дискретных запросов и связать время ожидания конкретного клиента с поведением системы в условиях очередей типа .
Что такое рекурсия Линдли в базе
В классической теории очередей время ожидания для
-го запроса (до начала его обработки) вычисляется исходя из времени ожидания предыдущего запроса:
где — время обслуживания предыдущего запроса,
— интервал между приходом
-го и -го запросов.
Оператор отражает нелинейность: если сервер простаивал (
), новый запрос обрабатывается мгновенно, а время ожидания равно нулю.
Как Линдли объясняет метастабильный отказ
В контексте метастабильности и повторов параметры и
перестают быть независимыми случайными величинами. Они становятся эндогенными функциями состояния очереди
: зависят от бэклога и больше не являются независимыми внешними величинами.
Эффект повторов: изменение интервала прихода
Когда система здорова, определяется только внешним трафиком. Но если растёт и превышает клиентский таймаут , клиент шлёт повтор. Интервал между входящими пакетами на сервере резко сокращается: . Плотность входящего потока растёт, среднее
уменьшается.
Эффект деградации: изменение времени обслуживания
Из-за огромного количества параллельных запросов и накладных расходов (обработка повторов, которые в итоге будут отброшены) эффективное время полезного обслуживания увеличивается. Каждому запросу достаётся меньше квантов CPU.
Фазовый переход в терминах рекурсии
Математическое условие стабильности очереди по Линдли: среднее время обслуживания должно быть меньше среднего интервала между запросами
(плюс , где
).
Классическое условие справедливо для изолированных систем с i.i.d. интервалами
и ; в условиях эндогенной зависимости (когда и становятся функциями бэклога , см. выше) оно применяется локально как квазистационарное приближение, а не как строгий критерий стационарности марковской цепи.
В метастабильной системе из-за нелинейных петель обратной связи возникают две зоны стабильности:
Почему система не возвращается сама?
Даже если внешний источник уменьшает генерацию исходных задач (мы убрали триггер), накопленное гигантское значение в рекурсии определяет поведение системы на много шагов вперёд. Пока бэклог настолько велик, что для всех новых прибытий (обозначения
,
,
введены в разд. 4), каждый новый запрос таймаутит: сервер физически не успевает начать его обработку до
, клиенты генерируют , и условие
поддерживается. Система «заперта» внутри рекурсивного роста.
Что это даёт на практике
Рекурсия Линдли превращает качественную картину «система залипла» в два числа, которые считаются из логов до аварии: среднее время обслуживания и минимальный интервал прихода
, который возникает при шторме повторов. Пока
с запасом — система в здоровом аттракторе. Как только уходит ниже , рекурсия превращается в линейный рост, и это и есть переход в сбойный режим. Оба числа измеряются на живом трафике; методика — ниже.
Как измерить по логам среднее время обслуживания и минимальный интервал прихода
Обе величины берутся с конкретного ingress-шлюза приложения (не усреднённо по кластеру) за скользящее окно 5-15 мин.
Время обслуживания . Для каждого запроса — время от начала его обработки на сервере до отправки ответа, без времени ожидания в очереди. Источник: отдельная метрика обработки (например,
http_request_durationбез queue wait) либо разность timestamp в access-логе. Если метрика включает и wait, и service — разделить, иначе будет расти автоматически, и причина не отличится от следствия.Интервалы прихода . Для каждой пары соседних принятых запросов — разность их timestamp прихода.
Три окна. Разбить анализируемый период на «до инцидента», «во время», «после снятия триггера».
Три числа на окно. (или медиана , если хвосты тяжёлые), и 5-й перцентиль — последний и есть (компромисс: 1-й перцентиль слишком шумный в коротком окне, 10-й теряет хвост).
Тренды. Смотреть на скользящее окно, а не на средние за час: инцидент живёт минуты.
Что искать в числах
— здоровый режим;
(на практике выше
) — приближение к сепаратрисе; — сбойный аттрактор. Признак активного sustaining-эффекта —
: даже самые «быстрые» приходы не успевают за обслуживанием, и каждый повтор продлевает петлю.
В примере разд. 5: мс в норме и
мс в сбое;
мс; при
мс
мс. В норме
; в сбое и — оба индикатора срабатывают.
4. Модифицированная рекурсия: таймаут, drop и повторы
В классической модели заявки живут в очереди, пока не будут обработаны. Чтобы учесть поведение клиентов и ограничения ИТ-систем, введём отсечение по таймауту и генерацию повторов. Пусть:
— жёсткий таймаут ожидания в очереди: при
клиент закрывает соединение;
— вероятность повторного запроса после таймаута;
— задержка перед повторной отправкой;
— цена выброса: время CPU на приём и отбрасывание просроченной заявки.
Drop-on-timeout
Под временем ожидания здесь понимается время до начала обработки (), а не до её завершения: таймаут отсекает заявку ещё в очереди. Мы моделируем сервер, который определяет просрочку при извлечении из очереди. — это стоимость обработки просроченной заявки: чтение заголовков, запись в лог, инкремент метрик, отдача ответа, освобождение сокета. Для сервиса с логированием и метриками это порядка 10-20 мс, а не 1-3 мс: даже минимальный лог и одна запись в метрику съедают больше миллисекунды. В симуляции мы используем 20 мс как реалистичную оценку для сервиса с активным логированием. Если заявка ждала начала обработки дольше , клиент уже ушёл, а сервер всё равно платит
.
Здесь — длительность полезной работы, зависящая от состояния зависимости в момент начала. Рекурсия на момент начала:
Ключевой момент
Если — модель восстанавливается сама: сервер не тратит CPU на мёртвые заявки, очередь дренируется. Именно
замыкает петлю: сервер платит за каждую просроченную заявку, и лавина повторов поддерживает себя. Сама по себе очистка по таймауту спасает систему при отсутствии повторных запросов; как только появляется шторм повторов, одной очистки уже недостаточно — петля снова замыкается.
Связь с fluid-моделью
Инженерный смысл в микро-модели: это стоимость бесполезной работы, которая в fluid-модели (разд. 2) соответствует снижению эффективной производительности — сервер тратит ресурсы на просроченные заявки, а не на полезные. Дискретная петля в Линдли и непрерывная деградация в ODE — две стороны одного механизма.
Шторм повторов (sustaining effect)
Клиентский таймаут срабатывает по часам клиента в момент , независимо от того, когда сервер извлечёт просроченную заявку. С вероятностью повтор планируется на
. Поток прибытий перестаёт быть экзогенным: интервал до следующей заявки зависит от состояния очереди. Именно эта обратная связь превращает оператор
в ловушку метастабильности.
5. Симуляция
Параметры штатного режима
Здесь обозначает время обслуживания — то же, что в разд. 4; индексы и
— норма и режим сбоя.
Параметр | Значение |
|---|---|
/ | 100/200 мс |
120 мс () | |
150 мс | |
20 мс | |
(вероятность повтора) | 0,9 |
50 мс | |
40 заявок | |
Число прогонов | 100 |
Порог по
Стоит отметить, что не просто «немного больше нуля» замыкает петлю. При
,
,
мс устойчивая петля требует
мс. Это прямо согласуется с оценкой «единицы–десятки мс»: если бы
мс, сервер успевал бы дренировать буфер, и метастабильный отказ не наступал бы. Значение 20 мс в таблице — не «подгонка ради красивого графика», а реалистичный порядок величины выше этого порога. Порог проверен численно: скрипт
lindley_sweep_cdrop.py прогоняет baseline-сценарий по 100 прогонов при от 1 до 20 мс.
Результат свипа резкий: при мс система восстанавливается во всех 100 прогонах; при
мс восстанавливается
; при
мс восстановление практически отсутствует. Переход сосредоточен в узкой полосе 10-15 мс, что подтверждает бистабильный характер отказа: граница между аттракторами резкая, а не размытая. Значение 20 мс выбрано с запасом над серединой перехода (
мс), чтобы результат не зависел от джиттера.
Эта оценка согласуется с грубым аналитическим порогом. При rps и в полном коллапсе (все заявки таймаутят) суммарный поток на входе равен
rps: базовый поток и лавина повторов оба платят . Сервер обрабатывает просроченные заявки со скоростью
заявок в секунду, поэтому дренирование возможно лишь при
мс (при
мс система коллапсирует). Это совпадает с эмпирикой свипа: именно при
мс восстанавливается
прогонов — на границе аналитического порога. Аналитическая оценка — нижняя граница: она не учитывает джиттер задержек повтора и переходные процессы, поэтому эмпирическая полоса перехода шире и сдвинута в сторону меньших .
Сценарий
Окно сбоя — wall-clock с: функция обслуживания переключается на
мс. Пока очередь ещё пуста, первая заявка действительно обслуживается за (таймаут отсекает ожидание в очереди,
, а не полное время ответа). Но
мс даёт
: бэклог растёт, и уже через доли секунды ожидание превышает
мс. С этого момента почти все заявки уходят в ветку drop-on-timeout и платят
мс вместо полезной работы, а клиенты взрывают поток повторами. То есть — триггер набора бэклога; фаза сбоя на графиках поддерживается уже связкой
повторы. После 48 с корень проблемы исчезает ( снова 100 мс), но клиенты агрессивно шлют повторы. Сценарий baseline в этой статье — drop-on-timeout с и фиксированной задержкой повтора , без backoff, retry budget и shedding (см. разд. 8). Ось
на рис. 3 — время в секундах (
).
Почему drop-on-timeout остаётся в baseline
Drop-on-timeout сам по себе — простейший механизм защиты от перегрузки: сервер не тратит CPU на просроченные заявки, и очередь дренируется. Мы оставляем его в baseline сознательно, чтобы изолировать эффект клиентской политики повторов: при модель восстанавливается сама (см. разд. 4), и роль повторов была бы не видна. Такой выбор отделяет клиентский рычаг (backoff/jitter/budget) от серверного (shedding) и делает сравнение защит в разд. 8 честным.
Результаты
На основе моделирования получается классическая картина метастабильного отказа:

Итог фазы без защиты. Внешний триггер исчез, сервер снова готов работать за 100 мс, но перегруженная очередь сама генерирует избыточный входящий поток. Нужны либо клиентские (задержка / джиттер), либо серверные (сброс нагрузки / предохранитель) разрывы контура.
Воспроизводимость
Скрипты симуляции (генератор Линдли с drop-on-timeout, свип по , ансамбли по 100 прогонов для baseline, backoff+jitter+budget и shedding) и исходные данные прогонов доступны в репозитории https://github.com/redbeardster/metastable.
Фрагмент: оценка виртуального ожидания (FIFO + drop).
def _service_at(start):
return D_BAD if FAULT_START <= start <= FAULT_END else D_OK
def offer_wait(now, server_free, buffer, tau):
t_free = server_free
for arr, _rcnt in buffer:
start = max(t_free, arr)
t_free = start + (C_DROP if start - arr > tau
else _service_at(start))
return max(0.0, t_free - now)
6. Тропическая математика: без мистики
Раздел можно пропустить при первом чтении: практический вывод сведён к процедуре в конце этого раздела («оставить не больше трафика на самом жёстком цикле»). Аппарат
приведён здесь, потому что он даёт язык для сетевого анализа петель повторов и объясняет, почему одна лишь маскировка связей не спасает.
Рекурсия Линдли содержит оператор . В тропической алгебре (idempotent / tropical semirings) он становится линейным: нелинейная рекурсия превращается в линейную систему, к которой применим аппарат собственных значений и собственных векторов. Этот аппарат позволяет описывать метастабильные отказы через спектр матрицы переходов: знак тропического собственного значения задаёт, растёт очередь или релаксирует к нулю.
Переход к тропическому полукольцу
В тропическом полукольце операции и
превращают рекурсию Линдли в линейное уравнение
где ,
— тропическая единица. Нейтральный элемент по
—
(в терминах топологии сети это отсутствие ребра между узлами); для одного сервера критерий роста сводится к знаку
, содержательный спектр появляется только для сети узлов.
Обозначения
Ниже — тропическое собственное значение (maximum cycle mean), а не интенсивность входящего потока из ODE/Kingman. Коэффициент загрузки сервера по-прежнему
. Тропический спектральный радиус матрицы обозначаем
; в одномерном случае
.
Сеть сервисов: где Max-Plus уместен
Аппарат матриц и осмыслен для сети дискретно-событийных узлов (тандем, синхронизации) в детерминированном худшем случае. Пример
ниже — именно сеть, а не замена модели . Он не доказывает стохастическую метастабильность; он оценивает, когда детерминированный цикл перегрузок растёт (
) и какую долю трафика нужно срезать, чтобы
.
Представим два связанных микросервиса в состоянии метастабильного отказа. Пусть узел 1 имеет время обслуживания мс при собственном интервале прихода
мс, а узел 2 —
мс и
мс. В обоих случаях
: каждый узел сам себя перегружает.
Элемент — чистое превышение обслуживания над приходом на ребре
: положительное значение означает, что бэклог узла
растёт, когда триггер приходит от узла
. Диагональные элементы — само-петли узлов, внедиагональные — перекрёстные потоки: узел 1 вызывает узел 2 и наоборот. Подставляя числа, получаем матрицу тропических переходов
Тождество — это просто суммарное
; оно выполняется автоматически для любой матрицы вида
. Циклы:
цикл 1–1: вес 4, средний вес 4;
цикл 2–2: вес 6, средний вес 6;
цикл 1–2–1 (взаимное влияние): вес
, средний вес 5.
Максимум достигается на цикле 2–2: это доминирующая петля обратной связи, по которой узел 2 сам себя подпитывает. При каждой итерации (один полный круг по доминирующему циклу — «событие» в терминологии Max-Plus) вектор очередей в среднем прибавляет 6 единиц времени, то есть система находится в аттракторе отказа и сама себя перегружает. Именно эту петлю — или её эквивалент в реальной системе — и нужно разрывать: защиты из разд. 8 снижают долю сохраняемого трафика настолько, чтобы максимум по всем циклам стал отрицательным.
Физический смысл
Тропическое собственное значение — это доминирующая петля обратной связи: цикл с наибольшим средним весом на ребро. Знак задаёт асимптотику очереди:
— бэклог растёт линейно со скоростью за событие,
— дренируется,
— критический режим. Единицы — те же, что у и (в примере — мс).
Тот же цикл длины с
мс и
мс даёт mean cycle
— один из порогов в разд. 6.3; финальный берётся как минимум по всем циклам.
Критическая доля сброса
Критическую долю сброса не стоит путать с загрузкой и с тропическим : здесь — доля сохраняемого трафика, при которой
(ниже — сброс
).
Пусть — доля сохраняемого трафика. Когда мы отбрасываем часть запросов, средний интервал между теми, которые сервер всё-таки берёт в обработку, растёт как . Для цикла
длины
с суммарным service
и суммарным interarrival
тропический радиус равен
а условие равносильно
: каждому циклу соответствует свой порог дренирования.
Поскольку — это максимум по всем циклам (разд. 6), доля сохраняемого трафика должна удовлетворять самому жёсткому из этих порогов:
Доминирующий цикл (в примере — цикл 2–2) задаёт нижнюю границу.
Пример
Для того же сценария, что в разделе выше:
цикл 1–1: порог
;
цикл 2–2: порог
;
цикл 1–2–1: порог
.
Минимум — , задаваемый доминирующей петлёй 2–2. Это верхняя граница доли сохраняемого трафика: сбросить придётся
, то есть агрессивнее, чем требуется для перекрёстного цикла в одиночку.
Важно: если бы мы ориентировались только на цикл 1–2–1 (порог ), при
доминирующая петля 2–2 оставалась бы положительной, и не стал бы отрицательным. Оценка должна идти по самому жёсткому циклу.
Физический смысл и подстановка чисел
Если (пропускаем слишком много трафика), — очереди продолжат расти, сброс неэффективен. Если
— все циклы, включая доминирующий 2–2, становятся отрицательными: уравнение Линдли превращается в сжимающее отображение, и вектор очередей «стекает» к нулю.
Подставим числа в рекурсию Линдли на доминирующем цикле 2–2. При мс и мс эффективный интервал между принятыми заявками равен , а дрейф одного шага:
Три режима:
, мс |
| Режим | |
|---|---|---|---|
рост бэклога | |||
критический режим | |||
дренирование |
При рекурсия принимает вид
то есть каждое событие срезает с бэклога фиксированные
мс. Из насыщенного состояния
мс очередь дренируется за
событий. При знак противоположен, и та же рекурсия даёт линейный рост
мс.
Как только очереди опустели, можно безопасно вернуть .
Реальная доля сброса обычно выше этой нижней границы: джиттер (разброс интервалов прихода/обслуживания) и тяжёлые хвосты требуют запаса, а не точного равенства .
Важно: разрыв одного ребра не спасает (элемент в матрице — отсутствие дуги). После удаления дуги матрица принимает вид
— очередь продолжает расти. Нужен реальный спад локальных (сброс нагрузки на узлах), а не только маскирование связей.
Инженерное резюме
Тропическая алгебра даёт инженеру три вещи, которых нет ни в fluid-ODE (разд. 2), ни в дискретно-событийной симуляции (разд. 5). Во-первых, узкое место в сети сервисов — это цикл обратной связи, а не отдельный узел: два микросервиса, вызывающих друг друга с повторами, образуют петлю, которая живёт своей жизнью и не видна при анализе по одному узлу. Во-вторых, знак доминирующего собственного значения (максимум по средним весам циклов) за одну операцию отвечает на вопрос «система растёт или дренируется». Отличие от классического спектра: там (экспоненциальный рост), здесь
(линейный) — для очередей естественна именно линейная метрика. В-третьих, критическая доля сброса вычисляется по логам, без запуска симуляции.
Практическая процедура
Построить граф вызовов между сервисами с рёбрами
:
— среднее время обслуживания узла ,
— средний интервал между принятыми заявками от узла .
Найти простые циклы (на практике — длины 2-3; длиннее уже редкость).
Для каждого цикла вычислить
.
; сбросить не менее
трафика.
Вернуть нагрузку только после того, как очереди опустеют.
Главный вывод на инженерном языке: если узел сам себя перегружает через собственную петлю повторов, он задаёт порог сброса жёстче, чем перекрёстные циклы, — сбрасывать надо по самому жёсткому циклу, а не «по среднему по сети». В нашем примере это , а не , как подсказала бы наивная оценка по перекрёстному циклу 1–2–1. Именно поэтому adaptive concurrency control в проде держит нагрузку существенно ниже половины входного трафика: порог из этого примера задаёт нижнюю границу, а с запасом на джиттер и тяжёлые хвосты практический ориентир оказывается в районе 10-20%.
Оценка — детерминированная, worst-case, и даёт нижнюю границу доли сброса; реальная доля выше из-за джиттера и тяжёлых хвостов. Стохастический аналог — формула Кингмана: она даёт среднюю картину в открытой системе без обратной связи. Обе модели дополняют друг друга: тропический анализ отвечает на вопрос «что и на сколько рвать», Кингман — «где мы стоим по загрузке прямо сейчас».
7. Формула Кингмана: количественный компас
До сих пор мы двигались в мире абстрактной математики: дифференциальные уравнения, рекурсия Линдли, тропические полукольца, спектральные радиусы. Это дало качественное понимание метастабильности — как возникает бистабильность и гистерезис, что сепаратриса — та точка, после которой система не возвращается сама, как оценивать критическую долю сброса .
Но всё это — качественные или полуколичественные модели. Они показывают механизм, но не дают простого способа ежедневно мониторить реальную систему и отвечать на вопросы: насколько я близок к сепаратрисе прямо сейчас? Какие у меня и
в часы пик? При каком росте трафика я пересеку опасную зону?
Для ответа нужна теория массового обслуживания — модель и формула Кингмана:
где — загрузка, — квадраты коэффициентов вариации интервалов прихода и времени обслуживания, — среднее время обслуживания. Именно эта формула становится компасом, который превращает абстрактную теорию в повседневный инструмент SRE-инженера.
Что такое i.i.d. и почему это важно.
Формула Кингмана опирается на предположение, что интервалы прихода — это i.i.d. (independent and identically distributed). Термин содержит два независимых требования.
Independent (независимые): величина
не зависит от предыдущих
Знание истории не помогает предсказать следующий интервал.
Identically distributed (одинаково распределённые): все имеют одно и то же распределение, не меняющееся во времени и не зависящее от состояния системы.
Физически это означает: система не имеет памяти, и входящий поток не зависит от того, что происходит на выходе. Именно в такой системе формула Кингмана честно описывает стационарную задержку как функцию загрузки. Если хотя бы одно из двух требований нарушено, стационарного режима может не быть вовсе, и из формулы теряет смысл.
Повторы нарушают оба требования сразу: клиенты реагируют на задержку (при превышении шлют повтор), поэтому зависит от состояния очереди — нарушена независимость; интенсивность входного потока растёт вместе с деградацией — нарушена одинаковость распределения. Именно поэтому формула Кингмана не описывает sustaining-эффект даже при малой : в ней нет обратной связи от бэклога к входящему потоку.
Область применимости
Формула Кингмана справедлива при , i.i.d. интервалах прихода (см. выше) и конечных вторых моментах и . Инженерно это означает: она корректна в открытой системе без обратной связи. Пока повторов нет, поток внешний, и — это функция загрузки.
Она теряет применимость в двух случаях.
Именно поэтому в примере из разд. 7 (при ) формула уже неприменима. Для таких сценариев работают рычаги из разд. 8: retry budget и shedding, а не расчёт по Кингману.
Сравнение моделей
Модель | |||
|---|---|---|---|
1 | 1 | ||
1 | произв. | ||
(Кингман) | произв. | произв. |
— частный случай при и одновременно частный случай аппроксимации Кингмана при
. Формула Поллачека–Хинчина для точна; формула Кингмана для — приближение.
Как читать по логам
Как увидеть угрозу в своих логах до того, как она превратится в аварию.
Собрать интервалы , посчитать
. Если
— поток пуассоновский; если
— трафик идёт пачками (первый признак угрозы).
Собрать времена обслуживания , посчитать
. Если
— все запросы одинаковы; если
(а иногда 10 или 50) — «тяжёлые» запросы блокируют быстрые.
Если
и
— система в красной зоне. Порог
— эмпирическое «правило большого пальца» SRE-команд, а не теорема. При такой вариативности и высокой загрузке любой сбой сети на
мс с высокой вероятностью запустит лавину повторов. Правило контекстно-зависимо: если клиентский таймаут многократно превышает среднее время обслуживания (
), лавина повторов может не запуститься даже при высокой вариативности.
Важно для распределённых систем
нужно измерять на конкретном ingress-шлюзе приложения, а не усреднять по кластеру в Prometheus. При нескольких шлюзах потоки на каждом независимы, и общая окажется ниже, чем на любом отдельном: для оценки лавины повторов ориентироваться следует на худший (наиболее пачечный) из шлюзов, а не на усреднённый показатель. Балансировщик (round-robin, least-connections) искусственно сглаживает пачки трафика и маскирует thundering herd.
Что важно понимать: множитель растёт гиперболически, а не экспоненциально. При он равен 9, при — уже 49, то есть в 5,4 раза больше при росте нагрузки всего на 8 п.п. Это и есть «крутой обрыв»: любой микро-всплеск мгновенно превращает очередь в бесконечную.
Пример
Сервис заказов с нормальным потоком 80 rps и временем обработки 10 мс (). Из-за блокировки БД время обработки выросло до 13 мс (
). Клиенты с 3 повторами (то есть до 4 попыток на запрос) дают до 320 rps — при номинальном это
; при актуальных 13 мс ещё выше. Система парализована, хотя база данных уже восстановилась. При формула Кингмана неприменима дважды:
нарушает условие сходимости, а повторы делают поток эндогенным — теряется i.i.d. Именно поэтому здесь важны не расчёты , а рычаги из разд. 8: retry budget и shedding.
8. Защитные механизмы
Чтобы разорвать петлю, нужно целенаправленно уменьшать параметры формулы Кингмана и разрывать эндогенную обратную связь. Ниже — те же сценарии симуляции, но уже с защитами: клиентской и серверной.
Клиентская защита: backoff, jitter, бюджет повторов
Фиксированная мс синхронизирует повторы в плотные пачки. Экспоненциальная задержка (exponential backoff) с full jitter (Brooker, AWS Builders’ Library) разносит повторные попытки по времени:
(с потолком MaxBackoff). Это уменьшает и возвращает .
Retry budget. Лимит попыток обязателен: 3 попытки = 1 исходная + 2 повтора. Без него вложенные повторы на разных уровнях стека перемножаются, и один пользовательский запрос порождает десятки внутренних.
Почему это спасает рекурсию: экспонента отодвигает повторные попытки далеко вперёд; джиттер уничтожает синхронизацию («пульсацию») и размазывает паразитный трафик; средняя задержка между повторными становится больше мс, оператор перестаёт накапливать бэклог, буфер очищается.
Результат симуляции: медиана времени восстановления по 100 прогонам с, goodput на хвосте
, ценой abandon
транзакций на прогон. Drop-on-timeout с не отключается: меняется только клиентская политика повторов (backoff + jitter + budget).
Серверная защита: shedding и circuit breaker
Клиентский backoff — «хороший тон», но чужих клиентов не контролируют. Сервер обязан защищать себя сам. Сброс нагрузки защищает сам сервис от перегрузки, а предохранитель — от каскадных сбоев при обращении к проблемным зависимостям (БД, кэш и т. п.): он изолирует внешний вызов, который всё равно упадёт, и даёт зависимости время восстановиться.
Load shedding. Порог (
мс), чтобы сброс срабатывал раньше, чем клиент сдастся. Оценивает offer wait с учётом FIFO-бэклога. Отдаёт 503 без клиентского повтора — иначе плотность ошибок умножится на стоимость обработки, и gate запрётся сам. В терминах формулы Кингмана shedding снижает и не даёт вырасти за счёт бесполезной работы. Затраты прокси на fail-fast (в коде — константа
мс) в модели не бронируются на тред-пуле приложения (
server_free): gate сидит перед очередью сервиса и отвечает 503 без занятия воркера.
Ограничение модели (клиенты и 503)
Симуляция предполагает, что клиент различает таймаут () и отказ прокси (503): на 503 повтор не планируется, транзакция сразу уходит в abandon. На практике это идеализация: сторонние библиотеки и «глупые» клиенты нередко шлют повторы и на 503, и тогда shedding переносит петлю на уровень прокси. В проде это закрывается retry budget на клиенте и/или заголовком Retry-After; в модели такой сценарий не воспроизводится.
Circuit breaker. Следит за долей ошибок и таймаутов в скользящем окне. При превышении — Open, вызовы к проблемной зависимости превентивно отвергаются, не тратя локальные ресурсы на заведомо неуспешные попытки. Circuit breaker обнуляет и даёт время на восстановление.
Результат симуляции: медиана времени восстановления по 100 прогонам с, goodput , без abandon. Drop-on-timeout сохраняется; добавляется load shedding на прокси.
Про circuit breaker
В этой статье CB упоминается как дополнительный механизм изоляции проблемной зависимости — концептуально. В симуляции реализован только load shedding на прокси; для разрыва петли повторов этого достаточно, а CB в проде добавляется поверх того же gate.
Сравнение
Таблица 1 суммирует ансамбль из 100 прогонов. Колонка «Восстановление» — число прогонов с восстановлением (из 100). Колонка — медиана времени восстановления по прогонам, в которых восстановление зафиксировано; для baseline значение не определено (восстановления нет).
Таблица 1. Сравнение сценариев защиты по 100 прогонам. Baseline — сценарий без backoff/budget/shedding; drop-on-timeout включён.
Сценарий | Восстановление | Goodput хвост | (медиана) |
|---|---|---|---|
Baseline (без backoff/budget/shedding) | — | ||
Backoff + jitter + budget |
| ||
Shedding (прокси, без повтора) |
|
Итог: обе защиты возвращают систему в норму, но с разной ценой. Backoff сохраняет больше транзакций, shedding — быстрее восстанавливает сервер.
Почему мс в профиле DET
Если генератор квазидетерминированный ( мс),
мс, а
мс
, сервер гарантированно успевает «остыть». Формально
, но микро-всплески на границе дают среднее
мс — на два порядка меньше шага дискретизации.
При пуассоновском приходе (та же ) DES даёт мс — в согласии с аналитическим
мс. Порог
нужно калибровать по реальному закону прихода: если DET-настройки выкатить в пуассоновский прод, gate будет отдавать ложные 503 ещё до аварии.
Этот раздел подтверждает, что все эффекты разд. 5 вызваны именно сбоем, а не законом прихода: в отсутствие сбоя система ведёт себя штатно при обоих режимах генератора.
Полевые наблюдения Huang et al. (OSDI ’22) показывают, что большинство метастабильных отказов в проде развивается именно по сценарию «повторы после таймаута»: диагностика по goodput/badput и долям таймаутов в скользящем окне — первый практический шаг.
Промышленные рычаги
Эти механизмы уже реализованы в промышленных прокси и планировщиках; ниже — что именно искать в конфигурации.
Envoy / Istio: лимиты пула соединений, outlier ejection, retry budgets, circuit breaking — cool-down вместо бесконечного буфера.
Adaptive Concurrency / Adaptive Token Bucket — держать перегрузку на прокси до приложения, нащупывая в реальном времени.
CoDel / fq_codel (RFC 8289) — на qdisc ядра; не путать с очередью приложения.
Resilience4j (наследник идей Hystrix) — CircuitBreaker, RateLimiter, Bulkhead, Retry в JVM-сервисах.
Задача инженера — не «изобрести backoff», а согласованно включить эти рычаги, измерить и вариативность и в chaos-прогоне проверить, что удаление триггера действительно возвращает систему в норму.
9. Что осталось за рамками
За пределами статьи остаётся ещё несколько важных методов, их стоит хотя бы назвать: формальные методы помогают не только в конкретных SRE-задачах, но и формируют способ мышления инженера.
Формальная верификация контракта восстановления (TLA, Timed CCS, model checking) — отдельная тема: на конечных моделях проверяют контракт — «после снятия триггера система за время возвращается в здоровый режим». Эти методы не заменяют эмпирическую проверку, но без них легко чинить симптомы вместо причины; с ними — целенаправленно устранять метастабильные контуры.
Без моделирования — или хотя бы без ответа на вопрос «что произойдёт с системой при изменении одного или нескольких параметров?» — инженер превращается в шамана.
Чек-лист архитектора
Собирайте логи и регулярно вычисляйте и .
Мониторьте и через формулу Кингмана.
Внедряйте exponential backoff с full jitter на всех клиентах.
Ставьте лимит попыток (retry budget) — один, общий, на всех уровнях стека.
Ставьте circuit breaker на все межсервисные вызовы (Istio / Envoy / Resilience4j).
Настраивайте load shedding с .
Настраивайте CoDel (RFC 8289) для внутренних очередей.
Применяйте адаптивные лимиты (adaptive concurrency) на входе.
Проводите chaos-эксперименты: инъекция задержки
снятие триггера проверка контракта восстановления.
И главное — проверяйте контракт восстановления: удаление триггера должно автоматически возвращать систему в здоровое состояние. Если этого не происходит, вы попали в ловушку метапетли и должны действовать.
KioskNews shows a cleaned-up reading view extracted from the publisher’s page — the original always lives on their site, not ours.