Учебник веб-разработки
Разделы учебника
На этой странице

X. Инструменты и проверки

Лаборатория диагностики: симптом, гипотеза, проверка

Оглавление · Наблюдаемость · Типичные проблемы

Задача: выбрать проверку, которая различает причины, и оценить результат без лишних изменений среды. Примеры используют вымышленные данные.

Начните с наблюдаемого симптома

«CMS сломалась» — гипотеза. «В 10:00 content-health вернул 503, а health — 200» — наблюдение. Зафиксируйте время, URL/операцию, среду, версию и ожидаемое поведение. Отдельно укажите масштаб: один запрос, один route, все посетители или только одна сеть. Не помещайте реальные tokens и приватные payload в публичный отчёт.

Исходник схемы
flowchart TD
  Symptom["Точный симптом и границы"] --> Hypotheses["Несколько возможных причин"]
  Hypotheses --> Prediction["Различимые предсказания"]
  Prediction --> Probe["Одна целевая проверка"]
  Probe --> Evidence["Наблюдение и его ограничения"]
  Evidence --> Next{"Причина достаточно подтверждена?"}
  Next -->|нет| Hypotheses
  Next -->|да| Fix["Исправить и проверить исходный сценарий"]

Проверка должна менять уверенность в гипотезе. Если все причины предсказывают один и тот же ответ команды, нужен другой сигнал. Сначала соберите доступные свидетельства: перезапуск может стереть память и полезное состояние процесса, а смена сразу нескольких параметров затрудняет установление причины.

Опыт 1: health 200, content-health 503

ГипотезаРазличающее наблюдениеОграничение
CMS недоступна по сетиЧтение CMS по тому же внутреннему пути не устанавливает соединениеВнешний admin URL проверяет другой путь
Token не подходитCMS отвечает отказом авторизации на чтение нужного APIУспех server health не проверяет token
Ответ CMS не проходит контрактДоступ есть, выбранный ответ не соответствует схеме адаптераВалидный JSON ещё не подтверждает контракт
Ошибка одной коллекцииОстальные чтения успешны, конкретное чтение падаетОбщий content-health не сообщает это отдельно

Таблица задаёт предсказания, а не обещает четыре разных HTTP-статуса наружу. Сверьте код endpoint: Promise.all объединяет чтения, catch возвращает общий 503. Отсюда нельзя определить, какая коллекция дала ошибку. Выберите одну проверку пути/операции; не включайте debug-печать полного окружения ради поиска token.

Если кешированная страница продолжает открываться, это согласуется с отказом CMS. Она могла не запрашивать зависимость. Чтобы проверить именно свежий путь чтения, используйте соответствующий контракт; не удаляйте все кеши и volumes по умолчанию.

Опыт 2: среднее, хвост и неверное объединение процентов

Этот блок выполняется Node 24, не открывает сервер и не подключает мониторинг. Используем nearest-rank: для отсортированных n>0 значений quantile q — элемент с номером ceil(q × n), начиная с 1. Это определение опыта, не алгоритм всех dashboards.

node --input-type=module <<'JS'
import assert from "node:assert/strict";

const durations = [...Array(95).fill(10), ...Array(5).fill(1000)];
const sorted = [...durations].sort((a, b) => a - b);
const nearestRank = (q) => sorted[Math.ceil(q * sorted.length) - 1];
const mean = durations.reduce((sum, value) => sum + value, 0) / durations.length;
assert.equal(mean, 59.5);
assert.equal(nearestRank(0.95), 10);
assert.equal(nearestRank(0.99), 1000);

const windows = [{ errors: 10, requests: 100 }, { errors: 1, requests: 1 }];
const unweighted = windows.reduce((sum, w) => sum + w.errors / w.requests, 0) / windows.length;
const totals = windows.reduce((a, w) => ({
  errors: a.errors + w.errors, requests: a.requests + w.requests,
}), { errors: 0, requests: 0 });
const combined = totals.errors / totals.requests;
assert.equal(unweighted, 0.55);
assert.equal(combined, 11 / 101);
console.log(`mean=${mean}ms p95=${nearestRank(0.95)}ms p99=${nearestRank(0.99)}ms`);
console.log(`без весов=${(unweighted * 100).toFixed(2)}%; вместе=${(combined * 100).toFixed(2)}%`);
JS

Ожидается mean=59.5 ms, p95=10 ms, p99=1000 ms; 55% без весов против 10.89% по суммарным запросам. Пять медленных запросов не исчезают от хорошего p95. Агрегаты теряют часть информации: одинаковый p95 может соответствовать разным хвостам.

У функции quantile здесь намеренно узкая область: непустой заданный массив, q из (0,1]. Для общего входа добавьте валидацию; для пустого окна не делите на ноль. Измените опыт: 94 быстрых и 6 медленных; предскажите p95 и подтвердите результат. Средние p95 двух экземпляров не заменяют p95 объединённой выборки.

Опыт 3: нет записи в логах

Предположим, Caddy отдал ответ, но в Docker logs нет строки о запросе. До вывода «запрос не дошёл» проверьте, включён ли access log, где его output, какое окно просматривается и есть ли sampling/retention. Текущий Caddyfile.production не включает access log. Отсутствие записи при отсутствии инструмента не является свидетельством отсутствия события.

Во втором варианте trace существует только для части запросов. Sampling и неполная propagation могут объяснять пробелы; сравните trace с логами и общими счётчиками. Не экстраполируйте частоту сбоев по выборке traces без понимания отбора. Это концептуальный опыт: tracing в проект не добавлен.

Опыт 4: после релиза стало медленнее

Вымышленные сведения: общий p95 вырос, но релиз одновременно совпал с притоком новых посетителей и большим числом cache MISS. Сначала разделите route, HIT/MISS, объём запросов и время окна. Затем проверяйте выбранную гипотезу: количество CMS чтений, задержку зависимости, CPU или ожидание соединения. Совпадение по времени не позволяет выбрать причину автоматически.

Снятый CPU-профиль показывает исполняемые участки процесса; сетевое ожидание требует других измерений. Полное упражнение по profiling остаётся отдельной темой. Не запускайте нагрузочный тест на VPS без отдельной задачи и плана нагрузки.

Результат расследования

Запишите исходный симптом, проверенные гипотезы, различающее свидетельство, изменение и результат того же пользовательского сценария. Если причина не найдена, назовите текущую границу и следующий сигнал. «После restart всё работает» подтверждает восстановление доступности, но не установленную первопричину и не отсутствие повтора.

Существующие команды по конкретным симптомам — разбор проблем. Production Docker-команды выполняются через оболочку эксплуатации; в рамках этой лаборатории их не запускаем.