Расскажу, какой баг отлаживал недавно. Как обычно, он оказался вложенным, когда один баг основывается на другом, тот — на третьем и так далее.

Ситуация: один сервис генерит CSV-файл и складывает в S3. После чего вызывает почтовый сервис со словами “отправь этот файл по почте тому чуваку”. Все это работало до тех пор, пока народ не пожаловался, что файлы не приходят на почту.

Смотрю логи, файл сгенерился и лежит в S3. Почтовый сервис при попытке скачать его кидает исключение “stream is closed”. Оказалось, особо умный разработчик намудрил с классами. Ему нужен был не только InputStream, но и поле из заголовков. Вместо того, чтобы вернуть пару (stream, field) или мапу с ключами stream и field, он строит анонимный экземпляр InputStream, который проксирует исходный стрим и несет дополнительное поле.

Разумеется, он не протестировал все как следует, и при особых условиях стрим был nil, а JVM считает, что нулевой стрим означает его закрытие. Отсюда “stream is closed”, хотя причина в другом.

Эта шелуха отняла порядочно времени. Но я все-таки выяснил, что исключение было от того, что файла в S3 нет.

Лезу в бакет — файл есть.

Смотрю логи еще раз. Выясняется, что файл появился через секунду после того, как почтовый сервис обратился к нему. Это бред, потому что файл записан в S3 еще до вызова почтового сервиса!

Смотрю внимательней и замечаю: я записываю файл csv, а почтовый сервис ищет такой же, но с расширением .xlsx. И до сих пор это срабатывало: рядом с CSV автоматом появляется xlsx, и кто его генерит — не понятно.

После нескольких часов выяснилось следующее.

Люди, которые получали по почте CSV, пытались открыть его экселем, что кончалось слезами и болью. В точности то, что я описал в другой заметке. Они попросили выслать эксель, но разработчики сервиса слишком заняты, чтобы делать другим удобно. Нашелся девопс, который сделал следующее:

  • он повесил на бакет хук, который срабатывал при загрузке файла с расширением csv

  • этот хук вызывал лямбду с именем файла

  • лямбда была скриптом на Питоне, который переколбашивал CSV в эксель и записывал рядом

  • он поправил сервис отчетов так, что при вызове почтового сервиса расширение файла менялось с csv на xls.

Вот как это работало: сервис отчета сбрасывал файл csv в S3. Запускался хук с лямбдой, которая писала рядом xlsx. К тому времени как вызывался почтовый сервис, файл xlsx был уже в бакете, и отправка работала без ошибок.

А потом что-то случилось: то ли Амазон поправил тайминги, то ли слишком много сообщений в очереди, то ли Луна повернулась не тем боком, но — хук стал вызываться чуть позже, буквально на секунду или около. Этой секунды оказалось достаточно, чтобы почтовый сервис запустился раньше и упал.

Разумеется, нужно выкорчевать это осиное гнездо и сделать так, чтобы сервис отчетов писал готовый xlsx безо всяких хуков. Но у нас все срочно, сроки горят, поэтому просто подняли тайминги с обещанием сделать все как надо в следующий раз.

За это я и не люблю облачную инфраструктуру. Код в ней играет лишь малую роль. Много логики можно задать какими-то хуками, эвентами, очередями и прочими штучками. Они не прописаны в коде, они могут быть заданы вручную в дашборде AWS. Они могут быть в зарыты в YAML-файлах в devops-репозиториях. Человек, который воткнул этот костыль, может быть в отпуске, и кроме него о нем никто не знает.

Это подводка к следующему посту на тему айти. Кто-то думает, что разработчик учит Питон и пишет код, зарабатывая 300 тысяч в секунду. На деле разработчик занимается в том числе тем, что описано выше: два дня ищет баг в стоге сена размером со стадион, и правит его сменой цифры в конфиге.

Вы точно хотите в айти?