Первым шагом при выполнении задания была настройка запуска в докер и проверка корректности работы. После первого запуска скрипта на экспорт данных было заметно что он работает не очень быстро.
Скрипт решила оптимизировать первоначально по памяти, т.к. имеет место обработка файла + active record наверняка может создавать лишние объекты ну и хотелось в этом больше потренироваться. Как и ожидалось после запуска memoryprofiler больше всего памяти приходится на activerecord, поэтому так же как и во втором задании поэтапно начинаю оптимизацию.
- Время выполнения (fixtures/small.json): 6.97 сек
- Потребляемая память (fixtures/small.json): 119 MB
Далее перечислю основные моменты:
- Первое что решаю сделать это добавить frozen_string_literal, что дает небольшое улучшение.
- Далее, исходя из отчета самое большое количество памяти выделено на /activerecord-5.2.3/lib/active_record/log_subscriber.rb Заменила log_level на error для скрипта. Здесь же убрала bootsnap, т.к. периодически падал профилировщик. Точка роста сместилась
- Т.к далее основная нагрузка приходится на active record, решаю по максимуму убрать использование объектов active record. Для этого выношу код в отдельный класс, заменяю запросы удаления на truncate, подключаю гем activerecord-import и с помощью него уже строю импорт данных В результате время выполнения сократилась, но потребление памяти не очень. На первом месте остались объекты active record
- Далее решила в импорте не использовать recursive, а собрать все в массив и импортировать его и это уже принесло хороший результат:
-
Время выполнения (fixtures/small.json): 0.81 сек
-
Потребляемая память (fixtures/small.json): 113 MB
-
Время выполнения (fixtures/medium.json): 4.07 сек
-
Потребляемая память (fixtures/small.json): 112 MB
-
Время выполнения (fixtures/large.json): 34.86 сек
-
Потребляемая память (fixtures/small.json): 335 MB
В результате оптимизации active record перестал быть главной точкой роста.
- После профилирования по времени на первом месте оказался PG::Connection#async_exec, судя по записанным данным самое большое количество запросов приходится на добавление связи bus и service. Ради интереса решила попробовать сделать их запись в одном запросе, что привело к снижению времени обработки. После этого решила попробовать по аналогии записывать и данные по trip и в итоге получила значительное снижение по времени, т.о от гема activerecord-import отказалась. Результаты:
-
Время выполнения (fixtures/small.json): 0.39 сек
-
Потребляемая память (fixtures/small.json): 110 MB
-
Время выполнения (fixtures/medium.json): 1.5 сек
-
Потребляемая память (fixtures/medium.json): 116 MB
-
Время выполнения (fixtures/large.json): 13.28 сек
-
Потребляемая память (fixtures/large.json): 330 MB
-
Время выполнения (fixtures/1M.json): 137.6 сек
-
Потребляемая память (fixtures/1M.json): 1895 MB
По памяти много, но по времени кажется неплохо. На этом результате решила остановиться пока, потому что времени катастрофически не хватает. Потоковая обработка интересна, если получится как нибудь попробую предложенный пример записи в БД.
После импорта файла large.json страница загружалась примерно 24 секунды. Первым делом решила попробовать поставить pgHero, т.к. было интересно попробовать этот инструмент.
Сразу после первого прогона был найден медленный запрос:
SELECT "services".* FROM "services" INNER JOIN "buses_services" ON "services"."id" = "buses_services"."service_id" WHERE "buses_services"."bus_id" = $1Поставила bullet и rack-mini-profiler.
- Далее поправила запросы исходя из алертов и добавила .
- из профайлера видно что очень много запросов вида:
SELECT "services".* FROM "services" INNER JOIN "buses_services" ON "services"."id" = "buses_services"."service_id" WHERE "buses_services"."bus_id" = $1; то же показывает и pgHero, поэтому поправила этот момент тем то изменила выборку по trips - добавила в select только нужные для отображения аттрибуты и добавила joins.
- объединила рендеры в один для удобства поиска необходимых аттрибутов
- заменила поиск города по названию (влияет не сильно но тем не менее)
В результате время отображения на сократилось до 182ms. По pgHero и bullet проблем больше не выявлено.
Дальше я решила посмотреть анализ самого долгого запроса, который стал основным. По нему получается что самым долгим является часть джойна с buses_services, т.к там нет индексов. Такая же проблема на таблицах buses и trips. Добавляю индексы.
В результате время отображения на сократилось до 130ms (с включенным профилировщиком)
Очень понравилось использовать pgHero, действительно удобный и визуально приятный инструмент. С bullet и rack-mini-profiler немного уже работала, поэтому они остаются полезными)
P.S Прошу прощения за такую задержку с выполнением дз. Постараюсь наверстать все пропущенное до конца курса.