Как двухканальная изоляция подняла доступность моделей с 97% до 99,8%
Где потолок доступности одного канала поставки? Мы разбираем 18 месяцев продакшен-данных и показываем реализацию и эффект двухканальной изоляции.
Проблема: потолок одного канала
В конце 2024 года наши основные модели (Claude Sonnet, GPT-4o) подключались к вышестоящим провайдерам через единственный канал. Месячная доступность колебалась между 96,5% и 97,2%. Звучит нормально?
Посчитайте — и нет. 97% доступности означает около 21 часа простоя в месяц. Для команд, которые используют нас как инфраструктуру, это неприемлемо.
Корень проблемы не в нашем коде — это волатильность самих вышестоящих провайдеров. Один канал падает — и страдает весь трафик. Единая точка зависимости — это проблема архитектуры, а не эксплуатации.
Проектирование: двухканальная изоляция
Идея проста: держать по два независимых канала поставки на модель (Channel A / Channel B), взаимно резервирующих друг друга.
Логика маршрутизации
Когда приходит запрос, маршрутизатор делает три вещи:
- Запрашивает реальные health-оценки обоих каналов (по ошибкам и задержке за последние 5 минут)
- Предпочитает канал с более высокой оценкой
- Если основной вернул 5xx или таймаут — немедленно повторяет на резервном (в подавляющем большинстве случаев прозрачно для пользователя)
async function route(model: string, req: Request) {
const [chA, chB] = await getChannels(model)
const primary = chA.healthScore >= chB.healthScore ? chA : chB
const fallback = primary === chA ? chB : chA
try {
return await primary.forward(req, { timeout: 30_000 })
} catch (e) {
// основной упал, переключаемся сразу — прозрачно для пользователя
metrics.record('failover', { model, from: primary.id })
return await fallback.forward(req, { timeout: 30_000 })
}
}
Расчёт health-оценки
Health-оценка каждого канала — значение 0–100 на скользящем окне:
- Доля ошибок, вес 70% — 5xx / всего запросов за последние 5 минут
- Задержка, вес 30% — насколько P95 отклоняется от базовой линии
Канал ниже 50 автоматически понижается в весе; канал ниже 30 в течение 3 минут подряд временно выводится из пула маршрутизации, периодически прощупывается отдельным heartbeat-зондом и возвращается после восстановления.
Результат: 18 месяцев продакшен-данных
После запуска двухканальной изоляции доступность выросла до 99,81%, а время восстановления упало с p95 4 минуты до p95 8 секунд — подавляющее большинство пользователей вообще не заметили сбой на стороне провайдера.
| Метрика | Один канал (2024 Q4) | Два канала (2025 Q3) |
|---|---|---|
| Доступность | 97,0% | 99,81% |
| Средний простой в месяц | 21ч | 1,4ч |
| failover p95 | — | 8с |
Набитые шишки
- Сначала не изолировали бюджет таймаута: у резервного канала timeout тоже был 30с, что удваивало ожидание при сбоях. После перехода на основной 30с / резервный 20с воспринимаемая задержка упала на 35%.
- Запаздывание health-оценки: 5-минутное окно медленно реагирует на внезапные сбои, поэтому мы добавили быстрый 30-секундный сигнал «недавняя ошибка».
- Шторм переключений: при кратковременном дрожании основного обе стороны опрашивали и перегружали резервный. Помогли клиентское ограничение частоты + backoff.
Что дальше
Двухканальность уже решает сбой одного вендора. Но мы по-прежнему зависим от единственного сетевого выхода, единственного биллинг-аккаунта, единственного региона. Следующий пункт плана — распространить изоляцию на эти уровни; напишем об этом, когда реализуем.