Представим, что в системе есть баг, но абсолютно непонятно, как его воспроизвести, а на поиск причин тратится куча времени, и ты чувствуешь себя детективом. Наверное, вам это знакомо?
Как-то в начале моей карьеры «проводник» в мир продуктовой разработки, помогая разобраться с ошибкой, говорил: «Чудес не бывает». С тех пор идёт восьмой год, а эта фраза частенько вспоминается в момент возникновения таких ситуаций, либо когда кто-то из коллег не может найти проблему и опускает руки.
О чём говорим?
В web-разработке бывают самые разные ситуации, когда «что-то идёт не так». Может как-то неожидаемо работать библиотека. Может изредка падать автотест. А может быть, мы получили сообщение про баг, но не можем понять, в чём дело. Во всех этих случаях приходится копаться в деталях.
Поговорим про это на примере разбора обращений клиента в тех.поддержку (тикетов). Как и при попытке найти «сбойное» место в коде, здесь тоже бывают нетривиальные ситуации.
Когда непонятно, в чём ошибка и как с ней разобраться, мозг зачастую пытается сэкономить энергию и придумывает объяснение.
Так сошлись звёзды. Время лечит. Больше не воспроизводится.
Но то, что ситуацию сложно воспроизвести - не повод опускать руки, и уж точно не повод оставлять её без внимания. Как говорится, «нет дыма без огня». Если запахло дымком, то безопаснее будет проверить, не горит ли коридор за дверью.
Примеры с полей
Вот несколько нетривиальных в разборе случаев из жизни:
Заходит клиент на страницу услуги, пытается её купить по предварительно заполненным данным и получает ошибку. Других подобных жалоб нет. Проблем с доступностью нет.
При поиске в логах нашлось, что приходящие с фронтенда данные не сходятся с теми, которые система предварительно заполняет. А дальнейшие раскопки показали, что одна из JS-библиотек при парсинге определенного значения даты в определенном часовом поясе смещала дату на 1 день в прошлое.
Клиент пишет о том, что при покупке услуги данные не заполнились и пришлось заполнять их руками. Проверяем, все HTTP-запросы были успешными. Никаких ошибок на бэке и фронте не возникало. Записи о запросе данных во внешний сервис из аудита, логи асинхронных задач — всё говорит о том, что данные были успешно получены. Полностью воспроизводим среду запуска за клиента, предварительное заполнение отрабатывает как следует...
В процессе разбора обнаружилось, что один из шлюзов с данными притормозил и ответ на запрос был получен спустя 60 секунд. Конечно, данные-то есть, но с такой задержкой они, скорее всего, уже не нужны.
При переходе по ссылке на услугу данные из черновика не заполнялись. Проверяем запросы — никаких ошибок. Последовательность запросов, найденных по заголовку User-Agent, не нарушена. В трекерах ошибок при посекундном поиске никакой информации. Время ответа в норме.
Оказалось, ссылка на ресурс из СМС у клиента открывалась в мобильном приложении, и на определённой версии терялись параметры запроса. При отсеивании логов по IP этот переход стал четко виден по смене заголовка User-Agent.
Разбирая такие проблемы, порой ощущаешь себя детективом. Перебираешь запрос за запросом, находишь, что именно делал клиент, какие статусы ответов получал. Поиск затягивает, а когда причина проясняется, мозг получает свою дозу дофамина.
Инструментарий
Помощники настоящего детектива — качественные инструменты. Благо, для наших задач их предостаточно. Для простого приложения с небольшой нагрузкой может быть достаточно одних логов. С ростом инфраструктуры и количества (микро)сервисов на каждом уровне будут возникать новые прослойки, в которых полезно собрать больше информации. Балансировщики, прокси, шины, фаерволы, сайдкары, асинхронное и синхронное межсервисное взаимодействие — чем больше точек соприкосновения в инфраструктуре, тем больше в них можно собрать информации, тем больше инструментов необходимо для удобной работы с ней.
Не вдаваясь в подробности, приведу краткий и общий перечень инструментов и приёмов, которые могут вам помочь при отладке web-приложения:
Инструменты сбора «следов»:
Логи. Хорошо, если они есть на всех уровнях инфраструктуры и с длинным периодом хранения. Пример: Kibana+ Elasticsearch + Kafka. А еще рассказ о том, как наши админы настроили такой стек.
Запись ошибок. Трекер всех возникающих исключений с максимально подробной информацией о контексте возникновения. Примеры: Sentry; Errbit.
Аудит изменений в БД. Сохранение всех вносимых изменений в бизнес-сущности. Для разных ЯП есть свои инструменты. Примеры (для Ruby): paper_trail, logidze. Откуда в ДомКлик Ruby?
Сохранение асинхронных задач, завершенных с ошибкой. Примеры: (для Rabbit) отдельная очередь с ошибками, куда можно переносить сообщения после неудачных попыток. Или мертвая очередь в Sidekiq, в которую попадают задачи после неудачных попыток.
Запись в БД самого запроса и его промежуточных статусов.
Запись токенов при межсервисном взаимодействии. Поможет понять, кто внёс изменение.
Инструменты комплексного анализа производительности приложения. Могут помочь отследить причину медленных запросов или ошибок. Пример: Elastic APM.
Мониторинг длительности и статусов ответов других (микро)сервисов.
Приёмы поиска следов применительно к web-приложений «в бою»:
Поиск/сопоставление по HTTP-заголовку User-Agent.
Поиск/сопоставление по IP. Поможет, например, даже если из браузера человека перекинуло в мобильное приложение через deep link.
Поиск/сопоставление по версии приложения или сборки (наиболее актуально для отладки WebView внутри мобильного приложения).
Поиск/сопоставление по HTTP-заголовку Rerefer. Заголовок позволяет понять, с какой страницы и из какого сервиса был запрос.
Анализ длительности выполнения запроса.
Если есть скриншот проблемы, то стоит его внимательно рассмотреть. Возможно, получится найти зацепки.
Если даже ничто из этого не дало подсказки, то можно при просмотре инструментов мониторинга просто ограничить время поиска в рамках секунды, зачастую это позволяет найти «следы».
Отдельным «приёмом» можно выделить воспроизведение условий. Например:
Установить такой же браузер.
Соблюсти те же условия авторизации/аутентификации.
Установить такие же HTTP-заголовки. Например, с помощью расширения
ModHeader
.Установить ту же версию мобильного приложения.
Вводить точно такие же данные.
Выбрать часовой пояс на компьютере как у клиента.
Вдобавок к инструментам «детектива» рекомендую использовать инструменты для предупреждения - мониторинг с оповещением о возникающих проблемах. Например, будет полезно смотреть на количество не ожидаемых ответов от бэкенда ( HTTP-статусы 4xx
и 5xx
), нагрузку на сервис и др. Это позволит обнаружить и начать решать проблему, не дожидаясь обращений от клиентов. В качестве инструмента здесь подойдет Grafana с оповещением на почту, в Telegram или иной подходящий мессенджер. Сами же метрики можно собирать, например, в Prometheus. Благодаря грамотно построенной системе оповещений работы у детектива станет меньше.
Целесообразность
Порой бывает, что на разбор сложных случаев может уходить очень много времени. Наверное, не стоит впадать в крайность и тратить целые дни на поиск проблемы по единичному обращению клиента, а целесообразнее решить её в отдельном (ручном) порядке..
Или всё-таки стоит? Ведь может оказаться «всё не так плохо, как вы думаете, всё намного хуже». Если жалоба от клиента одна, то это далеко не всегда означает, что затронут один клиент. Возможно, другие просто не стали тратить время, чтобы сообщить о проблеме. Либо, что еще хуже, они просто ушли и воспользовались другим продуктом. Либо, например, обращения попадают не по адресу, или в разборе обращений такая очередь, что до них просто не дошли руки.
Думаю, что всё-таки стоит попытаться разобраться, в чём дело, а уже потом, понимая первопричину, оценить степень влияния и приоритет проблемы. Даже если в адекватный срок побороть проблему не удалось, можно навесить на подобные случаи дополнительные логи и мониторинги.
А еще, вовремя предупрежденная или решенная проблема позволит избежать так называемых «ручников».
То есть сократит время, затрачиваемое на ручное устранение проблем, причина которых не найдена и не исправлена.
Настоящий детектив ведь не ждёт новых преступлений, чтобы разобраться в деле?
Немного мотивации напоследок
Если попробовать привести в качестве аналогии «непонятного бага» ситуацию из прошлого, наверное, получилось бы что-то такое:
Древний человек, соплеменника которого убило молнией️ в сопровождении мощного раската грома, наверняка списал бы это на волю Создателя.
Сейчас наука с легкостью объяснит нам это природное явление, и мы уже не считаем молнию магией и посылом «высших сил». Так же и здесь: не стоит давать волю простым объяснениям и опускать руки, всегда можно докопаться до истины, вопрос лишь в желании и инструментах.
Чудес не бывает, а магии не существует. Ищите, думайте, находите!
P.S. А какие приёмы помогают вам при поиске реальных причин проблем, которые приходится разбирать в процессе работы?