Canary-выкат работает только если есть чем измерять разницу. Большинство команд сравнивают долю ошибок и p95 латентности — и пропускают целый класс регрессий, которые проявляются иначе.

Проблема абсолютных порогов

Правило «откатывай, если 5xx выше 1%» ломается дважды. Ночью трафика мало, и одна ошибка даёт 3% — ложное срабатывание. Днём при большом трафике реальная регрессия может держаться на 0.9% и не сработать вообще.

Сравнивать нужно canary с базовой ревизией за то же окно. Тогда суточный профиль сокращается, и остаётся собственно эффект изменения.

Четыре недооценённых сигнала

1. Глубина очереди, а не латентность

Латентность растёт, когда очередь уже образовалась. Глубина очереди реагирует на десятки секунд раньше и не зависит от того, успел ли клиент отвалиться по таймауту.

2. Распределение, а не перцентиль

p95 может не измениться, пока форма распределения ползёт. Сравнивайте гистограммы целиком — например, расстоянием Вассерштейна между бакетами.

def histogram_distance(baseline: list[int], canary: list[int]) -> float:
    """Земляная мера между двумя нормированными гистограммами латентности."""
    total_b = sum(baseline) or 1
    total_c = sum(canary) or 1

    cum, distance = 0.0, 0.0
    for b, c in zip(baseline, canary):
        cum += b / total_b - c / total_c
        distance += abs(cum)

    return distance

3. Доля повторов на стороне клиента

Если клиент ретраит, сервер видит больше запросов, но меньше ошибок на запрос. Метрика «успешных с первого раза» показывает реальную картину.

4. Потребление памяти на запрос

Утечка не даст ошибок в течение десятиминутного окна — она проявится через сутки. Рост RSS, нормированный на число обработанных запросов, ловит её сразу.

Как мы это настраиваем

[deploy.canary]
steps = [5, 25, 50, 100]
step_duration = "15m"

  [[deploy.canary.guard]]
  metric = "queue_depth"
  compare = "baseline"
  max_delta_ratio = 1.3

  [[deploy.canary.guard]]
  metric = "memory_per_request"
  compare = "baseline"
  max_delta_ratio = 1.15

  [[deploy.canary.guard]]
  metric = "first_attempt_success"
  compare = "baseline"
  min_delta = -0.005

Что не стоит делать

  • Не ставьте шаг canary меньше 5%: статистики не хватит для вывода.
  • Не сравнивайте canary с самим собой десять минут назад — это ловит тренд, а не эффект выката.
  • Не продвигайте выкат в пятницу вечером только потому, что метрики зелёные. Часть регрессий проявляется на суточном цикле.