Skip to content

Optimization#7

Open
foxy-eyed wants to merge 7 commits intospajic:masterfrom
foxy-eyed:optimization
Open

Optimization#7
foxy-eyed wants to merge 7 commits intospajic:masterfrom
foxy-eyed:optimization

Conversation

@foxy-eyed
Copy link

@foxy-eyed foxy-eyed commented Apr 13, 2019

1 Импорт данных

Цель

Оптимизировать механизм перезагрузки расписания из файла так, чтобы он обрабатывал файл large.json в пределах минуты.

Процесс

  • Механизм импорта реализован в виде rake-задачи, причем вся логика находится непосредственно внутри самой задачи. Кажется разумной практикой держать rake-задачи максимально примитивными, а логику уносить в сервисы, которые легко покрыть тестами. Поэтому на первом шаге перенесла весь код "как есть" в сервис DbImporter.

  • Для сервиса DbImporter добавила тесты, которые проверяют полноту и корректность загрузки записей в БД.

  • Для получения обратной связи об эффекте оптимизации добавила ещё одну rake-задачу, которая замеряет время выполнения задачи импорта. Запускаю её на небольшом файле small.json

    bundle exec rake reload_json_benchmark[fixtures/small.json]
    

Оптимизации

  • Вставку записей сделала не поштучной, а массовой (для этого использовала gem activercord-import). Инстансы моделей я аккумулирую в хэшах. Это позволяет быстро обращаться к нужному инстансу по ключу, а также избегать дублирования записей. В итоге весь импорт делается за четыре вставки.
  • Я избавилась от всех запросов не чтение, воспользовавшись опцией :synchronize: она позволяет иметь в наших промежуточных хэшах уже persisted записи после выполнения импорта.
  • Для парсинга json использовала Oj gem.

Результат

Screen Shot 2019-04-14 at 12 59 26

2 Отображение расписаний

Цель

Оптимизировать страницы расписаний, т.к. они формируются неэффективно и при росте объёмов начинают сильно тормозить.

Процесс

  • Для начала добавила тест, который проверяет ответ от проблемного эндпоинта, чтобы не допустить изменения логики.
  • Для оценки эффективности изменений использовала AB (100 запросов с concurrency 10). Замеры делала на базе, которая была импортирована из файла small.json, чтобы иметь быструю ОС.
  • Для поиска узких мест использовала
    • Newrelic
    • рельсовую консоль
    • http://tatiyants.com для визуализации планов запросов

Оптимизации

  • Самым затратным по времени оказался рендеринг страницы, это хорошо видно по графику:

    Screen Shot 2019-04-14 at 15 19 26

    Во-первых, убрала излишнее дробление на partial'ы, т.к. оно не имеет никакой ценности, но при этом замедляет генерацию документа.
    Во-вторых, заменила рендеринг partial'ов в цикле на рендеринг коллекции.

  • Избавление от N + 1.
    Проблема хорошо видна и в отчете newrelic, и невооруженным глазом в консоли: для каждого маршрута из вьюхи мы запрашиваем ассоциации bus и services.

    Screen Shot 2019-04-14 at 15 24 13

    Используем eager_load и достаем все связанные записи одним запросом.

  • Теперь, когда для каждого маршрута мы делаем один запрос, но более сложный, включающий объединение таблиц, имеет смысл обратить внимание на индексы. Ниже сравнение планов до и после добавления индексов (добавила на внешние ключи и на поле start_time у маршрутов):

Screen Shot 2019-04-14 at 19 32 00

Результаты

Вот так выглядит график для эндпоинта после всех проведенных оптимизаций:

Screen Shot 2019-04-14 at 19 39 26

Результаты выполнения ab -c 10 -n 100 http://localhost:3000/автобусы/Самара/Москва (до/после) на базе, импортированной из small.json:

Screen Shot 2019-04-14 at 19 45 49

Для базы, импортированной из файла large.json, эндпоинт отвечает в среднем за 310 ms

@foxy-eyed foxy-eyed changed the title [WIP] Optimization Optimization Apr 14, 2019
@foxy-eyed foxy-eyed marked this pull request as ready for review April 14, 2019 15:50
Copy link
Owner

@spajic spajic left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Топчик

gem 'dotenv-rails'
gem 'newrelic_rpm'
gem 'oj'
gem 'pghero'
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

gem 'newrelic_rpm'
gem 'oj'
gem 'pghero'
gem 'strong_migrations'
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

@@ -0,0 +1,80 @@
require 'oj'

class DbImporter
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

def change
add_index :buses_services, [:bus_id, :service_id], unique: true, algorithm: :concurrently

add_foreign_key :trips, :buses
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍 Плюсик за ключи

disable_ddl_transaction!

def change
add_index :buses_services, [:bus_id, :service_id], unique: true, algorithm: :concurrently
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍 Плюсик за concurrently

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants