Skip to content

Commit a3337a8

Browse files
committed
chore: 4th step case b
1 parent c315cda commit a3337a8

10 files changed

+29
-47
lines changed

app/controllers/trips_controller.rb

+1-2
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ class TripsController < ApplicationController
22
def index
33
@from = City.find_by_name!(params[:from])
44
@to = City.find_by_name!(params[:to])
5-
@services = Service.pluck(:id, :name).to_h
6-
@trips = Trip.where(from: @from, to: @to).preload(bus: :bus_services).order(:start_time)
5+
@trips = Trip.where(from: @from, to: @to).preload(bus: :services).order(:start_time)
76
end
87
end

app/models/bus.rb

+1-2
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,7 @@ class Bus < ApplicationRecord
2121
].freeze
2222

2323
has_many :trips
24-
has_many :bus_services, class_name: 'BusService'
25-
has_many :services, through: :bus_services
24+
has_and_belongs_to_many :services, join_table: :buses_services
2625

2726
validates :number, presence: true, uniqueness: true
2827
validates :model, inclusion: { in: MODELS }

app/models/bus_service.rb

-14
This file was deleted.

app/models/service.rb

+1-2
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,7 @@ class Service < ApplicationRecord
1919
'Можно не печатать билет',
2020
].freeze
2121

22-
has_many :service_buses
23-
has_many :buses, through: :service_buses
22+
has_and_belongs_to_many :buses, join_table: :buses_services
2423

2524
validates :name, presence: true
2625
validates :name, inclusion: { in: SERVICES }

app/views/trips/_service.html.erb

+1-1
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
<li><%= "#{@services[bus_service.service_id]}" %></li>
1+
<li><%= "#{service.name}" %></li>

app/views/trips/_services.html.erb

-4
This file was deleted.

app/views/trips/_trip.html.erb

+5-2
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,11 @@
44
<li><%= "В пути: #{trip.duration_minutes / 60}ч. #{trip.duration_minutes % 60}мин." %></li>
55
<li><%= "Цена: #{trip.price_cents / 100}р. #{trip.price_cents % 100}коп." %></li>
66
<li><%= "Автобус: #{trip.bus.model}#{trip.bus.number}" %></li>
7-
<% if trip.bus.bus_services.present? %>
8-
<%= render "services", bus_services: trip.bus.bus_services %>
7+
<% if trip.bus.services.present? %>
8+
<li>Сервисы в автобусе:</li>
9+
<ul>
10+
<%= render partial: "service", collection: trip.bus.services %>
11+
</ul>
912
<% end %>
1013
</ul>
1114

case-study-b.md

+11-19
Original file line numberDiff line numberDiff line change
@@ -23,46 +23,38 @@
2323
Вот как я построил `feedback_loop`: профилирование - изменение кода - тестирование – бенчмаркинг – откат при отсутствии разницы от оптимизации/сохранение результатов
2424

2525
## Вникаем в детали системы, чтобы найти главные точки роста
26-
Для того, чтобы найти "точки роста" для оптимизации я воспользовался rack mini profiler
26+
Для того, чтобы найти "точки роста" для оптимизации я воспользовался rack mini profiler, bullet
2727

2828
Вот какие проблемы удалось найти и решить
2929

3030
### Ваша находка №1
31-
- rack mini profiler показал, что N+1 проблему `SELECT "buses".* FROM "buses" WHERE "buses"."id" = $1 LIMIT $2; ` + `SELECT "services".* FROM "services" INNER JOIN "buses_services" ON "services"."id" = "buses_services"."service_id" WHERE "buses_services"."bus_id" = $1;` в `trips/_services.html.erb` (на 1004 рейса 690 sql buses + services)
31+
- bullet показал, что N+1 проблему `SELECT "buses".* FROM "buses" WHERE "buses"."id" = $1 LIMIT $2; ` + `SELECT "services".* FROM "services" INNER JOIN "buses_services" ON "services"."id" = "buses_services"."service_id" WHERE "buses_services"."bus_id" = $1;`
3232
- делаю `.preload(bus: :services)` для `trips`
3333
- метрика снизилась до 6 секунд
3434
- количество sql запросов для `trips/index.html.erb` сократилось до 12
3535

36-
### Ваша находка 2
36+
### Ваша находка 2
3737
- rack mini profiler (и логи веб сервера) показал, что основное время тратится на рендеринг шаблонов `trips/index.html.erb 2091.1
3838
4917.9`.
3939
- рендереринг всех коллекций через `render partial:`
40-
- метрика снизилась до 1 секунды
40+
- метрика снизилась до 600мс
4141
- `Rendering: trips/index.html.erb 716.2 975.7`
4242

43-
### Ваша находка 3
44-
- rack mini profiler показал, что основное время занимает запрос в сервисы (около 400мс).
45-
- вместо habtm делаю промежуточную таблицу, для нее и делаю eager loading вмсто services. в контроллере достаю весь и собираю хэш-справочник (id=>name) сервисов и использую его во вью.
46-
- метрика особенно не снизилась
47-
- запрос пропал из логов профилировщика (вместо него показывается запрос в сервисы, около 25мс, что 8 раз меньше)
48-
49-
### Ваша находка 4
43+
### Ваша находка № 3
5044
- rack mini profiler показал, что производится и запрос по count, и идентичный по выборке данных.
5145
- меняю count на length.
5246
- метрика особенно не снизилась
5347
- запрос count пропал из логов профилировщика
5448

55-
### Ваша находка № 5
56-
- rack mini profiler показал, что долго выполняются запросы `SELECT "buses_services".* FROM "buses_services" WHERE "buses_services"."bus_id" IN ($1, <...>` (делаю explain, он показывает `Seq Scan on buses_services` как основную точку роста),``
57-
- Добавляю индекс на `bus_id`
58-
- как изменилась метрика
59-
- как изменился отчёт профилировщика - исправленная проблема перестала быть главной точкой роста?
60-
61-
49+
### Ваша находка № 4
50+
- rack mini profiler показал, что долго выполняются запрос ` SELECT "trips".* FROM "trips" WHERE "trips"."from_id" = $1 AND "trips"."to_id" = $2 ORDER BY "trips"."start_time" ASC` (делаю explain, он показывает `Seq Scan on trips` как основную точку роста),
51+
- Добавляю индекс на связку `from_id`/`to_id`/`start_time`
52+
- метрика особенно не снизилась
53+
- время работы запроса снизилось с 18 до 2.5мс
6254

6355
## Результаты
6456
В результате проделанной оптимизации наконец удалось обработать файл с данными.
65-
Удалось улучшить метрику системы с *того, что у вас было в начале, до того, что получилось в конце* и уложиться в заданный бюджет.
57+
Удалось улучшить метрику системы с 13.3с до 0.6с.
6658

6759
## Защита от регрессии производительности
6860
Для защиты от потери достигнутого прогресса при дальнейших изменениях программы *о performance-тестах, которые вы написали*
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
class AddFromIdToIdIndexToTrips < ActiveRecord::Migration[5.2]
2+
disable_ddl_transaction!
3+
4+
def change
5+
add_index(:trips, %i[from_id to_id start_time], order: {start_time: :asc}, algorithm: :concurrently)
6+
end
7+
end

db/schema.rb

+2-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
#
1111
# It's strongly recommended that you check this file into your version control system.
1212

13-
ActiveRecord::Schema.define(version: 2019_03_30_193044) do
13+
ActiveRecord::Schema.define(version: 2024_05_15_205445) do
1414

1515
# These are extensions that must be enabled in order to support this database
1616
enable_extension "plpgsql"
@@ -40,6 +40,7 @@
4040
t.integer "duration_minutes"
4141
t.integer "price_cents"
4242
t.integer "bus_id"
43+
t.index ["from_id", "to_id", "start_time"], name: "index_trips_on_from_id_and_to_id_and_start_time"
4344
end
4445

4546
end

0 commit comments

Comments
 (0)