diff --git a/.gitignore b/.gitignore
index 59c74047..263ef72b 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,4 +1,13 @@
-/.bundle
-/tmp
-/log
-/public
+log/
+tmp/
+.generators
+.idea/
+data*.txt
+result.json
+ruby_prof_reports/
+.ruby-version
+.rubocop.yml
+/docker-valgrind-massif/
+/stackprof_reports/
+1M.json
+.byebug_history
diff --git a/Gemfile b/Gemfile
index e20b1260..12dc5c95 100644
--- a/Gemfile
+++ b/Gemfile
@@ -7,20 +7,24 @@ gem 'rails', '~> 5.2.3'
gem 'pg', '>= 0.18', '< 2.0'
gem 'puma', '~> 3.11'
gem 'bootsnap', '>= 1.1.0', require: false
-
+gem 'pghero'
+gem 'oj'
+gem 'activerecord-import'
group :development, :test do
# Call 'byebug' anywhere in the code to stop execution and get a debugger console
gem 'byebug', platforms: [:mri, :mingw, :x64_mingw]
end
group :development do
+ gem 'rack-mini-profiler'
# Access an interactive console on exception pages or by calling 'console' anywhere in the code.
gem 'web-console', '>= 3.3.0'
gem 'listen', '>= 3.0.5', '< 3.2'
end
group :test do
+ gem 'minitest'
end
# Windows does not include zoneinfo files, so bundle the tzinfo-data gem
-gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw, :jruby]
+# gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw, :jruby]
diff --git a/Gemfile.lock b/Gemfile.lock
index fccf6f5f..47d997e4 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -33,6 +33,8 @@ GEM
activemodel (= 5.2.3)
activesupport (= 5.2.3)
arel (>= 9.0)
+ activerecord-import (1.0.4)
+ activerecord (>= 3.2)
activestorage (5.2.3)
actionpack (= 5.2.3)
activerecord (= 5.2.3)
@@ -76,9 +78,14 @@ GEM
nio4r (2.3.1)
nokogiri (1.10.2)
mini_portile2 (~> 2.4.0)
+ oj (3.10.2)
pg (1.1.4)
+ pghero (2.4.1)
+ activerecord (>= 5)
puma (3.12.1)
rack (2.0.6)
+ rack-mini-profiler (1.1.6)
+ rack (>= 1.2.0)
rack-test (1.1.0)
rack (>= 1.0, < 3)
rails (5.2.3)
@@ -134,17 +141,21 @@ PLATFORMS
ruby
DEPENDENCIES
+ activerecord-import
bootsnap (>= 1.1.0)
byebug
listen (>= 3.0.5, < 3.2)
+ minitest
+ oj
pg (>= 0.18, < 2.0)
+ pghero
puma (~> 3.11)
+ rack-mini-profiler
rails (~> 5.2.3)
- tzinfo-data
web-console (>= 3.3.0)
RUBY VERSION
ruby 2.6.3p62
BUNDLED WITH
- 2.0.2
+ 2.1.4
diff --git a/app/controllers/trips_controller.rb b/app/controllers/trips_controller.rb
index acb38be2..b2b1fa06 100644
--- a/app/controllers/trips_controller.rb
+++ b/app/controllers/trips_controller.rb
@@ -2,6 +2,6 @@ class TripsController < ApplicationController
def index
@from = City.find_by_name!(params[:from])
@to = City.find_by_name!(params[:to])
- @trips = Trip.where(from: @from, to: @to).order(:start_time)
+ @trips = Trip.eager_load(bus: [:services]).where(from: @from, to: @to).order(:start_time).load
end
end
diff --git a/app/models/bus.rb b/app/models/bus.rb
index 1dcc54cb..1918e7d5 100644
--- a/app/models/bus.rb
+++ b/app/models/bus.rb
@@ -17,4 +17,15 @@ class Bus < ApplicationRecord
validates :number, presence: true, uniqueness: true
validates :model, inclusion: { in: MODELS }
+
+ def self.find_cached_or_create(bus_data)
+ @all_buses ||= Bus.all.map{ |bus| [bus.number, bus]}.to_h
+ unless @all_buses[bus_data['number']]
+ @all_buses[bus_data['number']] ||= Bus.create!(
+ model: bus_data['model'],
+ services: Service.find_dumped(bus_data['services']),
+ number: bus_data['number'])
+ end
+ @all_buses[bus_data['number']]
+ end
end
diff --git a/app/models/city.rb b/app/models/city.rb
index 19ec7f36..d17ab288 100644
--- a/app/models/city.rb
+++ b/app/models/city.rb
@@ -5,4 +5,10 @@ class City < ApplicationRecord
def name_has_no_spaces
errors.add(:name, "has spaces") if name.include?(' ')
end
+
+ def self.find_cached_or_create(name)
+ #name = name.gsub(' ','')
+ @all_cities ||= City.all.map{|city| [city.name, city]}.to_h
+ @all_cities[name] ||= City.create!(name:name)
+ end
end
diff --git a/app/models/service.rb b/app/models/service.rb
index 9cbb2a32..d88c8d64 100644
--- a/app/models/service.rb
+++ b/app/models/service.rb
@@ -16,4 +16,18 @@ class Service < ApplicationRecord
validates :name, presence: true
validates :name, inclusion: { in: SERVICES }
+
+ def self.find_dumped(names)
+ @all_dumped ||= dump_all_to_db
+ @all_dumped.slice(*names).values
+ end
+
+ private
+
+ def self.dump_all_to_db
+ SERVICES.sort.reverse.each do |name|
+ Service.find_or_create_by!(name: name)
+ end
+ Service.all.map{ |s| [s.name, s]}.to_h
+ end
end
diff --git a/app/models/trip.rb b/app/models/trip.rb
index 9d63dfff..d7bed72a 100644
--- a/app/models/trip.rb
+++ b/app/models/trip.rb
@@ -29,4 +29,19 @@ def to_h
},
}
end
+
+ def to_h_old
+ {
+ 'from' => from.name,
+ 'to' => to.name,
+ 'start_time' => start_time,
+ 'duration_minutes' => duration_minutes,
+ 'price_cents' => price_cents,
+ 'bus' => {
+ 'number' => bus.number,
+ 'model' => bus.model,
+ 'services' => bus.services.map(&:name),
+ },
+ }
+ end
end
diff --git a/app/services/trips_load.rb b/app/services/trips_load.rb
new file mode 100644
index 00000000..79a93250
--- /dev/null
+++ b/app/services/trips_load.rb
@@ -0,0 +1,36 @@
+class TripsLoad
+
+ def self.perform(file_name)
+ json = Oj.load(File.read(file_name))
+ ActiveRecord::Base.transaction do
+ City.delete_all
+ Bus.delete_all
+ Service.delete_all
+ Trip.delete_all
+ ActiveRecord::Base.connection.execute('delete from buses_services;')
+ trips_array = []
+
+ json.each_with_index do |trip, index|
+
+ from = City.find_cached_or_create(trip['from'])
+ to = City.find_cached_or_create(trip['to'])
+ bus = Bus.find_cached_or_create(trip['bus'])
+
+ trip_hash = {
+ from_id: from.id,
+ to_id: to.id,
+ bus_id: bus.id,
+ start_time: trip['start_time'],
+ duration_minutes: trip['duration_minutes'],
+ price_cents: trip['price_cents']
+ }
+ trips_array << trip_hash
+ if index%1000 == 999
+ Trip.import(trips_array, validate: true, validate_uniqueness: true)
+ trips_array =[]
+ end
+ end
+ Trip.import(trips_array, validate: true, validate_uniqueness: true)
+ end
+ end
+end
\ No newline at end of file
diff --git a/app/views/trips/_services.html.erb b/app/views/trips/_services.html.erb
index 2de639fc..0be503e1 100644
--- a/app/views/trips/_services.html.erb
+++ b/app/views/trips/_services.html.erb
@@ -1,6 +1,6 @@
Сервисы в автобусе:
<% services.each do |service| %>
- <%= render "service", service: service %>
+ - <%= "#{service.name}" %>
<% end %>
diff --git a/app/views/trips/index.html.erb b/app/views/trips/index.html.erb
index a60bce41..019edb52 100644
--- a/app/views/trips/index.html.erb
+++ b/app/views/trips/index.html.erb
@@ -2,15 +2,24 @@
<%= "Автобусы #{@from.name} – #{@to.name}" %>
- <%= "В расписании #{@trips.count} рейсов" %>
+ <%= "В расписании #{@trips.size} рейсов" %>
<% @trips.each do |trip| %>
- <%= render "trip", trip: trip %>
+ - <%= "Отправление: #{trip.start_time}" %>
+ - <%= "Прибытие: #{(Time.parse(trip.start_time) + trip.duration_minutes.minutes).strftime('%H:%M')}" %>
+ - <%= "В пути: #{trip.duration_minutes / 60}ч. #{trip.duration_minutes % 60}мин." %>
+ - <%= "Цена: #{trip.price_cents / 100}р. #{trip.price_cents % 100}коп." %>
+ - <%= "Автобус: #{trip.bus.model} №#{trip.bus.number}" %>
<% if trip.bus.services.present? %>
- <%= render "services", services: trip.bus.services %>
+ - Сервисы в автобусе:
+
+ <% trip.bus.services.each do |service| %>
+ - <%= "#{service.name}" %>
+ <% end %>
+
<% end %>
- <%= render "delimiter" %>
+ ====================================================
<% end %>
diff --git a/case-study.db.md b/case-study.db.md
new file mode 100644
index 00000000..a577a71f
--- /dev/null
+++ b/case-study.db.md
@@ -0,0 +1,102 @@
+# Case-study оптимизации загрузки данных
+
+## Актуальная проблема
+
+Необходимо импортировать файл с данными fixtures/large.json с 100_000 трипов менее чем за 1 минуту.
+
+У нас уже была rake задача, которая умела делать нужную обработку.
+
+Но она недостаточно производительна
+Так:
+ small.json с 1K трипов обрабатывается за 9 секунд
+ medium.json с 10K трипов обрабатывается за 80 секунд
+
+По грубым оценкам large.json с 100K трипов будет обрабатываться не менее 800 секунд.
+
+## Формирование метрики
+Для анализа влияния изменений на скорость работы возьмем время обработки файла small.json - 9 секунд
+
+## Гарантия корректности работы оптимизированной программы
+Для проверки корректности работы обновленной программы был написан тест, который заполнял БД данными из файла example.json, потом выгружал БД в json и сравнивал с исходным.
+
+## Feedback-Loop
+
+1. Проверка корректности работы, замер метрики, сбор отчета
+2. Изучение отчетов профайлеров
+
+Например
+ rails test test/system/load_test.rb && rake reload_json"[fixtures/small.json]" && rake pghero:capture_query_stats
+
+## Вникаем в детали системы, чтобы найти главные точки роста
+
+Для того, чтобы найти "точки роста" для оптимизации я воспользовался
+- pg_hero
+
+Вот какие проблемы удалось найти и решить
+
+### Находка №1
+- pg_hero показал что запросы
+ ```SELECT "services".* FROM "services" INNER JOIN "buses_services" ON "services"."id" = "buses_services"."service_id" WHERE "buses_services"."bus_id" = $1```
+ занимают 25% времени. explain показал последовательное чтение.
+- добавить индекс ```:buses_services, [:bus_id, :service_id] ```
+- значимого изменения метрики нет
+- в отчете pg_hero запрос переместился с верхней строчки вниз
+
+### Находка №2
+- pg_hero показал что запросы ```SELECT ? AS one FROM "buses" WHERE "buses"."number" = $1 AND "buses"."id" != $2 LIMIT $3``` занимают 23% времени
+- добавить индекс ```:buses, :number```
+- метрика изменилась незначительно на 0.3 секунды
+- в отчете pg_hero запрос переместился с верхней строчки вниз
+
+### Находка №3
+- pg_hero показал что запросы ```SELECT "services".* FROM "services" WHERE "services"."name" = $1 LIMIT $2``` занимают 18% времени
+- принято решение заполнить все возможные сервисы предварительно и собрать в хеш и не запрашивать бд
+- метрика снизилась примерно на 1,5 секунды до 7,2 секунд
+- запрос исчез из отчета
+
+### Находка №4
+- pg_hero показал что запросы ```SELECT "cities".* FROM "cities" WHERE "cities"."name" = $1 LIMIT $2``` занимают 12% времени
+- принято решение кешировать города в хеш
+- метрика снизилась до 6 секунд
+- запросы стал занимать менее 0.1% времени
+
+### Находка №5
+- pg_hero показал что запросы
+ ```SELECT "services".* FROM "services" INNER JOIN "buses_services" ON "services"."id" = "buses_services"."service_id" WHERE "buses_services"."bus_id" = $1```
+ занимают 47% времени.
+- принято решение переписать поиск и заполнение автобуса
+- метрика уменьшилась до 4,8 секунды
+- запрос исчез из отчета
+
+### Протеситирована метрика время обработки medium.json - 21 секунда. Переходим на эту метрику
+
+### Находка №6
+- pg_hero показал что запросы
+ ```SELECT "buses".* FROM "buses" WHERE "buses"."number" = $1 LIMIT $2``` занимают 33% времени
+- принято решение кешировать автобусы аналогично городам
+- метрика снизилась до 14 секунд
+- запрос исчез из отчета
+
+### Находка №7
+- pg_hero показал что запросы вставки в таблицу trip занимают 40% времени
+- вставлять записи пачками
+- ничего не изменилось, потому что activerecord запрограммирован вставлять по одной
+
+### Находка №8
+- Существует гем activerecord-import, который позволяет вставлять записи пачками
+- применить гем
+- метрика уменьшилась до 7 секунд
+- запросы вставки в таблицу trips самые емкие по времени, но с этим ничего не поделать
+
+## Результаты
+В результате проделанной оптимизации удалось обработать файл с данными за заданое время. Сейчас оно составляет 22 секунды.
+
+###Какими ещё результами можете поделиться
+1. Также можно применить activerecord-import на остальные модели.
+1. В rails 6 в классе ActiveRecord есть метод insert_all.
+1. При необходимости быстрой работы с бд надо организовывать ее на низком уровне.
+
+## Защита от регрессии производительности
+
+Для защиты от потери достигнутого прогресса при дальнейших изменениях программы был написан тест с проверкой загрузки файла medium.json менее чем за 8 секунд.
+
diff --git a/case-study.view.md b/case-study.view.md
new file mode 100644
index 00000000..2e070c58
--- /dev/null
+++ b/case-study.view.md
@@ -0,0 +1,72 @@
+# Case-study оптимизации отображения
+## Актуальная проблема
+
+При большом объеме данных страница расписания показывается слишком медленно.
+
+http://localhost:3000/%D0%B0%D0%B2%D1%82%D0%BE%D0%B1%D1%83%D1%81%D1%8B/%D0%A1%D0%B0%D0%BC%D0%B0%D1%80%D0%B0/%D0%9C%D0%BE%D1%81%D0%BA%D0%B2%D0%B0
+
+Для уточнения задачи были загружены данные файла fixtures/large.json
+Сейчас на странице 1004 рейсов
+Сейчас время отклика составляет 11_006 миллисекунд.
+
+Бюджет не задан четко, но время загрузки должно быть "адекватно"
+
+Идеальным временем зарузки страницы считаетсчя 50-100 миллисекунд.
+
+## Формирование метрики
+
+Метрикой будет время ответа сервера, записаное в его логах. Сейчас это 11 секунд.
+
+## Гарантия корректности работы оптимизированной программы
+
+Так как работа по оптимизации не должна воздействовать на содержимое страницы был написан тест сверяющий страницу до внесенных измений с новой.
+
+## Вникаем в детали системы, чтобы найти главные точки роста
+
+Для того, чтобы найти "точки роста" для оптимизации я воспользовался
+- gem 'rack-mini-profiler'
+
+Вот какие проблемы удалось найти и решить
+
+### Находка №0
+- Страница загружается 11 секунд только первый раз, потом она загружается 4,5 секунд
+
+### Находка №1
+- rack-mini-profiler показал, что запросы для каждого trip делается дополнительный запрос по buses
+- добавил eager_load для bus
+- метрика именьшилась до 3,3 секунды
+- Лишние запросы исчезли
+
+### Находка №2
+- rack-mini-profiler показал что запросы для каждого trip делается дополнительный запрос по services
+- добавил eager_load для services
+- метрика именьшилась до 2,4 секунды
+- Лишние запросы исчезли. Осталось 4 sql запроса, из время выполнения 113 миллискунд
+
+### Находка №3
+- rack-mini-profiler показал что основное время занимает больше число вызово рендеринга trips/_services, который каждый раз рендерит trips/_service
+- надо ускорить/упростить вызов
+- метрика именьшилась до 1,1 секунды
+- Лишние вызовы исчезли.
+
+### Находка №4
+- в процессе просмотр кода обнаружено, что для указания количества рейсов используется метод count, который делает дополнительный запрос
+- загружать полностью выборку и использовать size
+- метрика не изменилась
+- Лишний запрос исчез.
+
+### Находка №5
+- rack-mini-profiler показал что основное время занимает рендеринг индекса
+- просмотреть, что можно оптимизировать там.
+- решено собрать всё в один файл. Новый файл занимает 25 и остается читабельным
+- метрика уменьшилась первоначально до 0,6 секунды при повторных вызовах до 0,2 секунды.
+
+## Результаты
+В результате проделанной оптимизации удалось получить страницу c 1000 рейсами менее чем за 0,6 секунды, что считааю достаточным показателем.
+
+## Защита от регрессии производительности
+Для защиты от потери достигнутого прогресса при дальнейших изменениях программы был написан тест с проверкой загрузки индексной страницы на 100 поездках (файле medium.json) менее чем за 0.3 секунд. Ограничение пришлось огрубить,так как загрузка большего объема сильно замедлит тестирование.
+
+## Чем еще поделиться
+1. Все ускорилось и без индекса по trips, хотя он очень просился. Эксперимент показал, что время контретно этого запроса уменьшается с 20 до 9 ms. Что для единичного запроса незначительно.
+
diff --git a/config/routes.rb b/config/routes.rb
index a2da6a7b..92916b51 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -2,4 +2,6 @@
# For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html
get "/" => "statistics#index"
get "автобусы/:from/:to" => "trips#index"
+
+ mount PgHero::Engine, at: "pghero"
end
diff --git a/db/migrate/20200306143220_create_pghero_query_stats.rb b/db/migrate/20200306143220_create_pghero_query_stats.rb
new file mode 100644
index 00000000..fbf41263
--- /dev/null
+++ b/db/migrate/20200306143220_create_pghero_query_stats.rb
@@ -0,0 +1,15 @@
+class CreatePgheroQueryStats < ActiveRecord::Migration[5.2]
+ def change
+ create_table :pghero_query_stats do |t|
+ t.text :database
+ t.text :user
+ t.text :query
+ t.integer :query_hash, limit: 8
+ t.float :total_time
+ t.integer :calls, limit: 8
+ t.timestamp :captured_at
+ end
+
+ add_index :pghero_query_stats, [:database, :captured_at]
+ end
+end
diff --git a/db/migrate/20200307171251_add_index_to_bus_services.rb b/db/migrate/20200307171251_add_index_to_bus_services.rb
new file mode 100644
index 00000000..61d6d5fa
--- /dev/null
+++ b/db/migrate/20200307171251_add_index_to_bus_services.rb
@@ -0,0 +1,5 @@
+class AddIndexToBusServices < ActiveRecord::Migration[5.2]
+ def change
+ add_index :buses_services, [:service_id, :bus_id]
+ end
+end
diff --git a/db/migrate/20200307174443_add_index_number_to_buses.rb b/db/migrate/20200307174443_add_index_number_to_buses.rb
new file mode 100644
index 00000000..04b54561
--- /dev/null
+++ b/db/migrate/20200307174443_add_index_number_to_buses.rb
@@ -0,0 +1,5 @@
+class AddIndexNumberToBuses < ActiveRecord::Migration[5.2]
+ def change
+ add_index :buses, :number
+ end
+end
diff --git a/db/migrate/20200311194043_add_from_to_index_to_trips.rb b/db/migrate/20200311194043_add_from_to_index_to_trips.rb
new file mode 100644
index 00000000..6bf571ef
--- /dev/null
+++ b/db/migrate/20200311194043_add_from_to_index_to_trips.rb
@@ -0,0 +1,7 @@
+class AddFromToIndexToTrips < ActiveRecord::Migration[5.2]
+ disable_ddl_transaction!
+
+ def change
+ add_index :trips, [:from_id, :to_id], algorithm: :concurrently
+ end
+end
diff --git a/db/schema.rb b/db/schema.rb
index f6921e45..df42c848 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -10,25 +10,39 @@
#
# It's strongly recommended that you check this file into your version control system.
-ActiveRecord::Schema.define(version: 2019_03_30_193044) do
+ActiveRecord::Schema.define(version: 2020_03_11_194043) do
# These are extensions that must be enabled in order to support this database
+ enable_extension "pg_stat_statements"
enable_extension "plpgsql"
create_table "buses", force: :cascade do |t|
t.string "number"
t.string "model"
+ t.index ["number"], name: "index_buses_on_number"
end
create_table "buses_services", force: :cascade do |t|
t.integer "bus_id"
t.integer "service_id"
+ t.index ["service_id", "bus_id"], name: "index_buses_services_on_service_id_and_bus_id"
end
create_table "cities", force: :cascade do |t|
t.string "name"
end
+ create_table "pghero_query_stats", force: :cascade do |t|
+ t.text "database"
+ t.text "user"
+ t.text "query"
+ t.bigint "query_hash"
+ t.float "total_time"
+ t.bigint "calls"
+ t.datetime "captured_at"
+ t.index ["database", "captured_at"], name: "index_pghero_query_stats_on_database_and_captured_at"
+ end
+
create_table "services", force: :cascade do |t|
t.string "name"
end
@@ -40,6 +54,7 @@
t.integer "duration_minutes"
t.integer "price_cents"
t.integer "bus_id"
+ t.index ["from_id", "to_id"], name: "index_trips_on_from_id_and_to_id"
end
end
diff --git a/lib/tasks/pghero.rake b/lib/tasks/pghero.rake
new file mode 100644
index 00000000..a8c133f6
--- /dev/null
+++ b/lib/tasks/pghero.rake
@@ -0,0 +1,5 @@
+desc 'Очистка запросов в pg_hero'
+task :empty_pghero_query_stats => :environment do
+ # puts ActiveRecord::Base.connection.execute('delete from pg_statements').inspect
+ puts ActiveRecord::Base.connection.execute('delete from pghero_query_stats').inspect
+end
\ No newline at end of file
diff --git a/lib/tasks/utils.rake b/lib/tasks/utils.rake
index 540fe871..4932de94 100644
--- a/lib/tasks/utils.rake
+++ b/lib/tasks/utils.rake
@@ -1,34 +1,9 @@
# Наивная загрузка данных из json-файла в БД
# rake reload_json[fixtures/small.json]
-task :reload_json, [:file_name] => :environment do |_task, args|
- json = JSON.parse(File.read(args.file_name))
-
- ActiveRecord::Base.transaction do
- City.delete_all
- Bus.delete_all
- Service.delete_all
- Trip.delete_all
- ActiveRecord::Base.connection.execute('delete from buses_services;')
- json.each do |trip|
- from = City.find_or_create_by(name: trip['from'])
- to = City.find_or_create_by(name: trip['to'])
- services = []
- trip['bus']['services'].each do |service|
- s = Service.find_or_create_by(name: service)
- services << s
- end
- bus = Bus.find_or_create_by(number: trip['bus']['number'])
- bus.update(model: trip['bus']['model'], services: services)
-
- Trip.create!(
- from: from,
- to: to,
- bus: bus,
- start_time: trip['start_time'],
- duration_minutes: trip['duration_minutes'],
- price_cents: trip['price_cents'],
- )
- end
- end
+desc 'Загрузка данных из файла'
+task :reload_json, [:file_name] => :environment do |_task, args|
+ time1 = Time.now
+ TripsLoad.perform(args.file_name)
+ puts "Imported in #{Time.now - time1}"
end
diff --git a/test/integration/trips_index_test.rb b/test/integration/trips_index_test.rb
new file mode 100644
index 00000000..dac6a6ac
--- /dev/null
+++ b/test/integration/trips_index_test.rb
@@ -0,0 +1,9 @@
+require 'test_helper'
+
+class TripsIndexTest < ActionDispatch::IntegrationTest
+ test "Index for 100 trips in 0.3 second" do
+ TripsLoad.perform('fixtures/medium.json')
+ total = Benchmark.measure{ get URI.escape('/автобусы/Самара/Москва') }.total
+ assert(total < 0.3)
+ end
+end
diff --git a/test/system/load_test.rb b/test/system/load_test.rb
new file mode 100644
index 00000000..6e2fbed4
--- /dev/null
+++ b/test/system/load_test.rb
@@ -0,0 +1,16 @@
+require 'test_helper'
+require 'json'
+
+class TestLoad < ActiveSupport::TestCase
+ def test_correct_load
+ source_json = JSON.load(File.read('fixtures/example.json'))
+ system 'bin/rake reload_json[fixtures/example.json]'
+ result_json = Trip.all.order(:id).map(&:to_h_old)
+ assert_equal source_json, result_json
+ end
+
+ def test_perfomance_load
+ total = Benchmark.measure{ system 'bin/rake reload_json[fixtures/medium.json]'}.total
+ assert(total < 8)
+ end
+end
\ No newline at end of file