Одна проба врёт в обе стороны: как мы мерили доступность и дважды ошиблись
Первая проверка сказала «заблокировано», вторая — «всё работает». Обе были неверны. 28 проб за две минуты, 29 % отказов и почему доступность — это доля.
Мы проверяли, доступен ли внешний API с наших боевых машин. Первая же проба дала таймаут на 8–10 секунд. Вывод напрашивался сам: заблокировано.
Вывод был неверный. Следующий, противоположный, — тоже.
Почему первая проба сказала «недоступно»
По IPv4 адрес действительно молчит. По IPv6 — отвечает за 44 миллисекунды.
Утилиты, которые по умолчанию берут A-запись, показывают пустой вывод, и из
него рождается «недоступно». Никто не врёт: инструмент честно сообщает, что
по тому адресу, который он спросил, ответа нет. Просто спросил он не всё.
Почему вторая проба сказала бы «работает» — и тоже соврала
Мы прогнали серию: 28 проб за две минуты. 20 успешных, 8 отказов — 29 %.
Одиночная успешная проба сказала бы «всё работает». А маршрут с почти третью отказов как основной не годится: на нём нельзя строить ни отправку, ни получение — он будет ронять каждый третий запрос и выглядеть при этом случайным сбоем.
Вот полная картина, которую даёт серия и не даёт одиночный запрос:
| Что спросили | Что ответило |
|---|---|
| IPv4, один раз | таймаут 8–10 с |
| IPv6, один раз | 44 мс |
| IPv6, 28 раз за 2 минуты | 20 успехов, 8 отказов |
Три ответа об одном и том же адресе, и первые два одинаково бесполезны.
Что мы делаем теперь
Проверяем обеими версиями протокола принудительно и смотрим на фактический
remote_ip ответа, а не на имя хоста. Имя хоста не говорит, куда вы на самом
деле пришли.
Меряем серией, а не одним запросом. Доступность внешней службы — это доля успеха на выборке, а не «да» или «нет». Число проб важнее их качества: двадцать восемь дешёвых говорят правду, одна тщательная — нет.
Помним про Happy Eyeballs. В Node fetch сам выбирает версию протокола, и
на холодном соединении это иногда стоит ожидания мёртвой ветки. То есть ваш код
может ждать восемь секунд там, где рабочий маршрут отвечает за сорок
миллисекунд, — и в логе это будет выглядеть как «сервис тормозит».
Как проверить у себя
# принудительно каждой версией протокола
curl -4 -sS -o /dev/null -w "v4: %{http_code} %{time_total}s %{remote_ip}\n" https://api.example.com
curl -6 -sS -o /dev/null -w "v6: %{http_code} %{time_total}s %{remote_ip}\n" https://api.example.com
# серия: доля успеха, а не единичный ответ
ok=0; for i in $(seq 1 28); do
curl -sS -o /dev/null --max-time 5 https://api.example.com && ok=$((ok+1))
done
echo "успешных: $ok из 28"
Если во второй команде получилось не 28 — у вас не «работает», у вас доля.
Что из этого следует
Проверка доступности отличается от обычного теста тем, что её результат непостоянен по природе. Тест либо проходит, либо нет; сеть — отвечает в восьмидесяти случаях из ста. Одиночная проба превращает распределение в двоичный ответ, и знак этого ответа определяется тем, в какую секунду вы нажали Enter.
Отсюда правило, которое мы держим и для себя, и для клиентских серверов: вывод о доступности делается по выборке, и в выводе называется размер выборки. «Сервис доступен» без числа проб — это не факт, а впечатление.
У нас проверки доступности идут снаружи и по расписанию, а не в тот момент, когда кто-то решил посмотреть: HTTP, TCP, TLS и остаток срока сертификата, с историей по каждой. Как это устроено — на странице Мониторинг доступности.
Читайте также
Автор
Александр ЦапковОснователь Скоупворк
Веду платформу и её боевой контур сам: разработка, выкат, дежурство. Пишу о том, на чём мы обожглись, — с датами, замерами и ссылками на решения в репозитории.