Надёжность API
Circuit breaker для AI API: защита от каскадных сбоев
Circuit breaker для AI API — это серверное правило, которое временно останавливает новые вызовы к нездоровому маршруту, чтобы не превратить краткий сбой в длинную очередь, лавину повторов и непредсказуемые расходы. Оно не «чинит» поставщика и не обходит ограничения. Зато даёт приложению честный ответ, контролируемый fallback или постановку задачи в очередь до восстановления.
Короткий ответ: отключайте маршрут, а не наблюдаемость
Когда один вызов начинает отвечать тайм-аутом, кажется логичным запустить его ещё раз. При десятках параллельных запросов эта реакция усугубляет проблему: процессы ждут, воркеры заняты, очередь растёт, а пользователь получает несколько одинаковых попыток. Circuit breaker добавляет между приложением и клиентом API небольшое состояние: при серии подтверждённых временных ошибок он открывается и не отправляет новую нагрузку на короткий период.
Это руководство для backend-разработчика или SRE, который уже разделил секреты и умеет отличать ошибки доступа от временных сбоев. Оно особенно полезно для чат-сценариев, фоновой обработки документов и видео-задач, где одна операция может жить минуты. Прямой ответ пользователю должен быть понятным: «задача принята в очередь», «маршрут временно недоступен, повторите позже» или «использован заранее согласованный резервный маршрут» — но только когда это действительно произошло.
Три состояния и их смысл
У простого предохранителя есть три состояния. В closed запросы идут как обычно, а сервис собирает события. В open новые вызовы на конкретный маршрут не выполняются: приложение возвращает контролируемую ошибку либо передаёт работу в очередь. Через установленный интервал наступает half-open: разрешаются один или несколько пробных запросов, чтобы проверить восстановление без немедленного наплыва трафика.
| Состояние | Что делает приложение | Что измерять |
|---|---|---|
| Closed | Вызывает маршрут в пределах deadline и rate limit. | Доля временных ошибок, задержка, размер очереди. |
| Open | Не создаёт новый upstream-вызов; отвечает или ставит работу в очередь. | Время открытия, отклонённые задачи, причина. |
| Half-open | Пускает малое число тестовых вызовов. | Успех проб, задержка и повторное открытие. |
Состояние должно принадлежать не всему продукту, а понятному ключу: например, маршрут + модель + регион приложения или маршрут + класс задачи. Слишком широкий ключ выключит здоровые сценарии вместе с проблемным; слишком узкий не остановит шторм. Сначала зафиксируйте эту границу в конфигурации и в дашборде, затем проверьте её синтетической нагрузкой.
Какие сигналы безопасно учитывать
Открывать breaker по любому неуспешному ответу нельзя. Код 401 или 403 обычно означает проблему с собственным ключом, endpoint или правами; код 400 — некорректный запрос; невалидный JSON — дефект контракта. Их нужно показать разработчику и исправить. Подробная безопасная диагностика есть в разборе 401 и 403.
Для временного контура подходят заранее определённые события: сетевой тайм-аут после server-side deadline, несколько 5xx за окно времени, отказ соединения или согласованный класс перегрузки. Не используйте жесткое правило «три ошибки всегда открывают breaker». Порог зависит от потока, критичности функции и допустимой задержки. Начните с небольшого тестового окружения: например, окно 60 секунд, минимум 10 попыток, порог временных ошибок 50 %, время открытия 30 секунд и одна проба в half-open. Эти числа — старт для эксперимента, а не универсальная настройка production.
Отдельно считайте отменённые пользователем действия, истёкшие локальные deadline и ошибки валидации. Если сложить их с авариями маршрута, мониторинг начнёт объяснять одну причину другой. Полезны поля: внутренний request ID, класс операции, состояние breaker, код результата, длительность, число попыток и версия политики. Не записывайте Authorization, API key, полный prompt или персональные данные.
Минимальный серверный пример
Ниже не библиотека и не готовая production-реализация, а прозрачный каркас для Node.js. Он показывает важную последовательность: проверить открытое состояние, выполнить одну ограниченную попытку, записать только безопасный результат и изменить состояние. Клиент API здесь намеренно опущен: подключите его на сервере с RUSSIAAPI_API_KEY, выбранной из текущего каталога моделью и собственным deadline.
const state = { failures: 0, openedUntil: 0 };
export async function guardedCall(run) {
const now = Date.now();
if (state.openedUntil > now) {
throw new Error('AI route is temporarily paused');
}
try {
const result = await run(); // one server-side API attempt
state.failures = 0;
return result;
} catch (error) {
if (!isTemporary(error)) throw error; // 4xx/config errors stay visible
state.failures += 1;
if (state.failures >= 5) state.openedUntil = now + 30_000;
throw error;
}
}
function isTemporary(error) {
return error?.code === 'ETIMEDOUT' || error?.status >= 500;
}В реальном сервисе состояние нельзя бездумно держать в памяти одного процесса: несколько реплик увидят разные ошибки. Выберите общее хранилище с коротким TTL или библиотеку, совместимую с вашей топологией, и заранее продумайте отказ самого хранилища. Не используйте breaker как способ молча повторять работу: для операций с побочным эффектом сначала нужна идемпотентность.
Как сочетать retry, очередь и fallback
Retry остаётся полезным только для небольшого числа временных ошибок и в пределах общего deadline. Добавьте backoff с jitter, ограничение параллелизма и лимит попыток; не создавайте новый запрос сразу после 429. Практика таких повторов описана в статье про 429 и backoff. Когда breaker открыт, retry не запускают: он лишь снова нагрузит маршрут, который уже признан нездоровым.
Очередь подходит для несрочных задач: сохраните минимальные данные операции, её idempotency key и срок действия, а затем повторите после контролируемой паузы. Для синхронного пользовательского запроса лучше быстро вернуть статус и предложить повтор. Fallback допустим, когда его проверили на том же наборе тестов: формат, безопасность, допустимая задержка и стоимость успешной задачи могут отличаться. Политика fallback должна быть видна пользователю системы и измеряться отдельно, как показано в руководстве по резервным моделям.
Проверьте политику до аварии
- Назовите маршрут и класс задачи, для которых breaker ведёт отдельное состояние.
- Согласуйте временные ошибки, окно, минимальную выборку, время открытия и лимит проб.
- Сымитируйте 5xx, timeout, 401, 429 и невалидный ответ; проверьте, что их классы не смешиваются.
- Посмотрите, что очередь не создаёт дубликаты, а лог не содержит секретов или пользовательского текста.
- Проведите controlled rollout с малой долей трафика и оставьте rollback к прежней политике.
Для наблюдаемости публикуйте не только число ошибок. Полезнее видеть процент открытого времени, длину очереди, число быстро отклонённых вызовов, результат half-open и стоимость успешно завершённой задачи. Так команда отличит здоровую защиту от постоянного отключения функции. О расходах и безопасных метриках читайте в материале о мониторинге стоимости API.
Начните с изолированного тестового маршрута
Откройте консоль RussiaAPI, выберите доступную модель из текущего каталога и проверьте политику на синтетических запросах. Храните собственный ключ только на сервере и фиксируйте наблюдаемые, а не предполагаемые результаты.
FAQ
Нужен ли circuit breaker, если уже есть retry?
Да. Retry ограниченно повторяет временно допустимый запрос, а circuit breaker временно прекращает новую нагрузку на нездоровый маршрут. Вместе они защищают очередь и время пользователя, но не исправляют неверную конфигурацию.
Какие ошибки открывают circuit breaker?
Обычно это подтверждённые временные сбои: повторяющиеся 5xx, сетевые тайм-ауты и согласованная перегрузка. Ошибки 401, 403, 400 и валидации требуют диагностики приложения, а не скрытия.
Можно ли сразу переключить задачу на другую модель?
Только после заранее проведённого теста качества, формата, стоимости и допустимых условий. Переключение должно быть явной, измеримой политикой, а не скрытым обещанием одинакового результата.