Команда, поставляющая Python runtime размером 5,5 МБ в браузеры, обнаружила, что 69 % ошибок, зафиксированных во время недавнего спринта, попали под один вводящий в заблуждение заголовок, и 89 % из них на самом деле были сетевыми таймаутами. Неверная отчетность направила разработчиков по ложному пути отладки и оставила значительную часть пользователей с «тихими» сбоями загрузки — проблема, с которой вскоре может столкнуться любое веб-приложение, объединяющее крупные ассеты.
Дашборд ввел в заблуждение
Система отслеживания ошибок автоматически группирует инциденты по месту в коде, где они впервые возникают. Полученный заголовок выглядел как обычный баг в загрузчике runtime, поэтому весь спринт ушел на поиск путей в коде, которые никогда не вызывали таймаутов. Когда команда проанализировала исходные метаданные, открылась истинная картина: большинство сбоев вовсе не были багами, а представляли собой зависшие сетевые соединения, вызывавшие таймаут.
Вывод: Заголовок ошибки — это лишь удобство, а не диагноз. Периодически углубляйтесь в сырые данные, чтобы проверить, что на самом деле стоит за заголовком.
API браузерного соединения выдало заглушку
Чтобы не заставлять пользователей с медленным интернетом скачивать 5,5 МБ, разработчики обратились к браузерному Network Information API (navigator.connection). API сообщал о постоянной пропускной способности 1,7 Мбит/с для каждого нового посетителя.
Браузеры выдают значение по умолчанию, если у них нет исторических данных о новом пользователе. Это значение по умолчанию — лишь подсказка, а не точная скорость. Когда для каждой новой сессии появляется одна и та же заглушка, это сигнализирует о том, что API еще не откалиброван для данной аудитории.
Вывод: Относитесь к любому сетевому сигналу, который никогда не меняется, как к резервному значению (fallback), а не как к точному показателю.
Разовые снимки состояния ненадежны
Отказавшись от ненадежной подсказки о пропускной способности, команда переключилась на другой сигнал, который, казалось, работал в их тестовом наборе. Один прогон теста прошел успешно, но три повторных запуска каждый раз приводили к сбоям. Скорость сети постоянно колеблется. Код сделал разовый снимок состояния, принял окончательное решение и продолжил работу, даже если соединение изменилось мгновение спустя.
Вывод: Не основывайте долгосрочные действия на одном замере динамического показателя. Вместо разового опроса (polling) подписывайтесь на события изменений.
Практические исправления, внедренные командой
- Подписка на изменения соединения. Вместо разового чтения
navigator.connectionкод теперь слушает событиеchangeи реагирует, если пропускная способность падает или растет во время загрузки. - Добавление «сторожевого таймера» (watchdog) отсутствия прогресса. Таймер прерывает любой запрос, который не продвигается вперед в течение короткого интервала, позволяя браузеру повторить попытку или использовать резервный вариант.
- Отказ от переключения CDN во время загрузки. Переключение источника большого файла на медленном соединении перезапускает передачу с нуля, тратя уже полученные байты. Теперь загрузка привязана к изначально выбранному CDN на протяжении всего времени.
- Отложенная тяжелая работа с кэшем. Задачи по записи больших объемов данных в кэш откладываются до завершения загрузки runtime, что позволяет сократить критический путь.
Если ваши дашборды рисуют подозрительно идеальную картину, копните глубже. Если сетевой показатель никогда не меняется, считайте его заглушкой. А если один разовый снимок определяет судьбу многомегабайтной загрузки, вы делаете ставку на мираж. Такие ставки оборачиваются «тихими» сбоями, которые подрывают доверие пользователей — а это то, что не сможет полностью исправить даже самый умный код постфактум.
