решить данный кейс. В данный момент он решён, но меня не покидает ощущение, что сделано это плохо и мозги набекрень уже. Смотришь всякие конференции по питону, high load, у всех rps чуть ли не 200к, а у тебя 20 машин еле-еле выдают 1000 rps 🥴. Такое чувство, что фреймворки работают у всех, но не у меня (утрирую конечно, 100% я делаю что-то плохо, но тем не менее хотелось бы разобраться)
Итак. Представим, что у вас есть .csv файл с двумя колонками uuid и url_array, где uuid — уникальный идентификатор (не повторяется), url_array — список ссылок количеством от 0 до n, n < 100 (верхняя граница в принципе не важна, но условно пусть будет 100; могут повторяться для разных uuid). Строк в .csv ~30 млн. Необходимо для каждой ссылки получить status code, title и response time и связать ссылку с uuid. Я выбрал следущую схему (БД выбрал Postgres): url из url_array будет pk в таблице urls, там же будут колонки status_code, title, response_time, вторая таблица pages будет содержать uuid (который из .csv), третья таблица ассоциативная — первое поле uuid, а второе url.
Задача, вроде бы, самая базовая — аля недоскрапинг, но почему-то с ней возникают проблемы в плане перформанса.
Я выбрал dramatiq (https://dramatiq.io/) (¯\_(ツ)_/¯), ибо с помощью него началось моё знакомство с обработкой фоновых задач. Там из коробки и Rabbitmq, и Redis, и Prometheus, почему бы и да. Даже на стартовой странице фреймворка показан пример count_words, который якобы +- повторяет то что мне надо, но нет (надеюсь). Оформил actor, подключил алхимию и сидел припеваючи ждал завершения работы. Но мы же все любим большой rps, почему бы и не увеличить. Принцип работы был +- такой: проверяем url в базе, если есть, то просто создаём связь uuid-url, если нет, то делаем request и записываем в базу все вышеперечисленные поля. Поднял ещё несколько машинок — ого, скорость х2 стала, ещё машинку — ого, скорость ещё выше, продолжаю поднимать новые тачки до того момента, пока скорость не стала 0 — "Привет, я ООМ в Postgres". База не выдержала новых подключений и решила прилечь. Мы народ не гордый, пошли пытаться искать в чём проблема — вышел на pgboucner и вроде как-то стало лучше (база не падала больше), но rps был непонятным для меня.
Мои мысли по поводу того, почему это работаем медленно.
0. Актёр принимает на вход только один url, а не пачкой
1. Зависит от Rabbitmq
2. Зависит от количество тредов, созданных dramatiq
3. Что-то с базой. От ООМ избавились, добавление в базу происходит за 0.5-0.6 sec, что, вроде бы, ок?
4. ¯\_(ツ)_/¯
Помогите расставить всё точки над i, как всё-таки следует решать такие кейсы.
Всякие uvicorn'ы, tornado и т.п. — это не про вот это всё? Была идея поднять api (fast-api, flask, etc), сделать там asyncio методы для обработки всего вышеперечисленного, но это так и осталось в мыслях, поскольку dramatiq все уши прожжужал
я не экспертный эксперт, просто для себя интересно интересный кейс. 0.5-0.6 - это что именно? база или вся апи?
Это commit одной транзакции, где в транзакции лежит uuid и url
мне кажется не норм. хотя опыта не так много чтоб быть уверенным. а отправлять пачками в базу юзкейс не позволяет?
Скорее нет, чем да. Но это видимо из-за того, как именно я начал решать. Вариантов естественно куча должна быть
судя по тому что количество воркеров ускоряет работу, балк инсерт ещё круче будет. можешь предварительно потестить в консольке
С bulk-insert прокатит тогда и только тогда, когда один актёр будет обрабатывать несколько url, не так ли? Сейчас спроектировано так, что воркер выполняет задачу для одной пары url-uuid
да, верно. но я думаю если после тестов окажется что это будет намного быстрее, можно и переписать код, как по мне
Звучит как будто ты коннекты не закрываешь
Коннекты к чему именно? Алихимия работает через сессии и в конце session.close(), requests через with работает Redis не используется
Коннекты к БД. Если они постепенно кончились, значит они текут. Либо ты сделал слишком много и в БД не хватило памяти, как ты предположил. Ограничь размер пула, чо
Обсуждают сегодня