Все статьи из цикла AWS

Расскажу о еще одном случае с AWS, который стоил мне пару бессонных ночей.

В Амазоне есть славный сервис Athena — супер-пупер агрегатор всего и вся. Он тащит терабайты данных из разных источников, обрабатывает и складывает в другие сервисы. Хадуп на стероидах. По-русски читается “Афина”.

Мы пользуемся им так. Есть лямбда, которая складывает в S3 сущности в JSON. Сущностей более миллиона. Другим сервисам нужны все сущности разом — и оказывается, выгрести их из S3 невозможно.

Почему? Даже если предположить, что за секунду мы скачаем 10 сущностей параллельно (что невозможно), за 900 секунд мы получим 9000 сущностей, что меньше одного процента. А нам нужно не девять тысяч, а миллион. Напомню, что 900 секунд — это 15 минут, максимальное время работы лямбды.

Архитектуру дизайнил не я, поэтому не спрашивайте, почему так.

На помощь приходит Афина. Мы говорим ей: склей все JSON файлы из бакета в один и положи туда-то. Афине, при всей мощи Амазона, нужно на это 4 минуты. Чудес не бывает, и чтобы забрать из S3 миллион файлов, Амазону нужно попотеть.

В ответ на нашу просьбу Афина дает айдишник задания, и мы его поллим. Готово? Нет. Готово? Нет. Готово? Да. И если да, в ответе будет ссылка на файл-агрегат.

Таких агрегатов у нас несколько, и я столкнулся с тем, что лямбда не укладывается в 15 минут. Если тратить по 4 минуты на агрегат, то на ожидание трёх уйдёт 12 минут. Процессинг файлов занимает еще 5-6 минут, и готово — ты не успел.

Тратить 12 минут впустую глупо, поэтому я сделал поллинг Афины параллельным. В самом деле, зачем ждать 4 минуты, если можно запустить второй поллинг? Логично же? Но вот к чему это привело.

Кто-то заметил, что в отчетах стали появляться “дырки”, то есть пустые ячейки. С точки зрения кода это выглядит так, словно в агрегате не было записей. Сначала я отнеткивался, но потом проверил размеры агрегатов. Оказалось, что сегодняшний файл, собранный Афиной, в два раза меньше вчерашнего. Или наоборот: вчера ок, а сегодня половина. Файл не битый, открывается, просто в нем половина данных.

После гуглений, осбуждений и вырванных волос обнаружились сразу три бага.

Первый — разработчик, который писал код до меня, допустил ошибку. Он поллил Афину по условию “пока статус pending”. Если что-то другое, он читал результат. Оказалось, что у задачи может быть три статуса: pending, ready и error. И в нашем случае статус был error.

Второй — даже если задача в статусе error, она содержит ссылку на собранный файл. Да, Афина не смогла, и файл собран частично. Считается, что это лучше, чем ничего.

Третий — в чем была причина error? Напомню, что я запускал в Афине несколько задач параллельно. Каждая задача собирала файлы из S3. В итоге сработал лимит на доступ к S3 — он ответил, что кто-то слишком часто обращается ко мне, убавьте пыл, господа. Поэтому задача упала.

Интересно, что S3 не волнует, что обращается к нему не сторонний потребитель, а сама Афина. При всем абсурде я считаю это правильным, потому что если лимит задан, он должен соблюдаться глобально, без поблажек “для своих”.

В итоге я сделал следующее. Все отчеты, которые обращаются к Афине, я разнес по времени с разницей в 5-10 минут. Раньше они стартовали одновременно, что порождало много задач в Афину, а та насиловала S3. С разницей по времени стало легче.

Потом я додумался до решения лучше. Сделал фейковый warmup-отчет, который работает как прогрев кеша. Он запускается первым и триггерит все задачи в Афине. Когда другим отчетам что-то нужно из Афины, они проверяют, была ли задача с такими параметрами за последние 2 часа. Если да, ссылка на агрегат берется из старой задачи.

Вот такая она, борьба с AWS. Перечитываю и понимаю, что, хоть и звучит умно, хвастаться здесь нечем. Не будь этой архитектуры, результат обошелся бы дешевле. Усилия потрачены, но неясно зачем.

Я пишу комментарии к коду, надеясь, что следующий разработчик поймет хоть толику все этой котовасии. Но не особо на это надеюсь: скорее всего, он скажет — что это наговнокодили такое? Пойду переделаю. И все повторится.