Три ложных зацепки, прежде чем я нашёл реальную причину бага
Два часа отладки, две уверенные и полностью неверные версии причины, и только третья, самая скучная, оказалась настоящей. Строю автоматизированный конвейер, который сам подбирает изображения к статьям. В какой-то момент часть статей стала выходить с брендированной заглушкой вместо реальной фотографии, хотя неделей раньше всё работало стабильно. Разбираю по шагам, как я два раза подряд убедительно доказал себе неправильную причину, прежде чем добрался до настоящей, и почему это нормальная часть отладки, а не признак того, что что-то делаешь не так.
Версия первая: рейт-лимит
Первая мысль была самая очевидная: закончился лимит бесплатных запросов к сервису поиска стоковых фото. Проверил документацию, свою квоту, счётчик использования за месяц. Лимит был использован процентов на двадцать. Версия отпала за пять минут, но забрала эти пять минут впустую, потому что я не проверил её первой строчкой, а сразу полез читать документацию вместо простого теста одним запросом.
Версия вторая: битые данные
Дальше начал отлаживать сам код и заметил, что в консоли кириллический текст запроса к API выводится вопросиками вместо букв. Логичный вывод: где-то по пути ломается кодировка, и API получает мусор вместо реального поискового запроса. Потратил час на проверку кодировок в разных местах пайплайна, добавил принудительное приведение к UTF-8 везде, где мог. Ничего не изменилось.
Оказалось, дело было не в данных, а в самом терминале: он показывал кириллицу криво только на экране, а реальный файл на диске содержал корректный UTF-8 текст. Проверил это, открыв файл в бинарном режиме и посмотрев на настоящие байты, а не на то, что печатает консоль. Вывод в терминале и реальное содержимое файла - это два разных источника истины, и в такой ситуации доверять стоит второму.
Версия третья, настоящая: сеть, а не код
Только после этого дошли руки до простого сетевого теста: вызвать API напрямую и посмотреть на реальную ошибку, а не гадать по косвенным признакам. Ошибка оказалась предельно скучной: таймаут на этапе SSL-рукопожатия, причём нестабильный, не каждый раз. Не рейт-лимит, не баг в коде, а обычная сетевая нестабильность у провайдера, через которого шёл трафик.
Решение вышло тоже скучным: запросы стали идти через резервный прокси-сервер с retry на несколько попыток, а на всякий случай для устойчивости добавил второй независимый источник фотографий, чтобы одна нестабильная точка отказа не могла полностью остановить процесс.
Что вынес для себя
Первые два часа отладки ушли не на поиск бага, а на две неверные, но правдоподобные версии, каждая из которых на своём этапе казалась самой логичной. Оглядываясь назад, порядок должен был быть обратным: сначала простой прямой тест самого внешнего вызова, и только если он ничего не покажет, копать глубже в код и кодировки. Косвенные признаки (использованная квота, кривая кириллица в консоли) выглядели как улики, но обе оказались не связаны с реальной причиной, просто совпали по времени с обнаружением проблемы.
Отдельно про доверие к выводу в терминале: если что-то выглядит сломанным на экране, но логика подсказывает, что это странно, стоит на секунду остановиться и проверить сырые данные напрямую, а не саму печать. В моём случае это сэкономило бы час, если бы я сделал так сразу.