Skip to content

Commit a899f61

Browse files
committed
chore: further micro optimizations
1 parent a3337a8 commit a899f61

File tree

4 files changed

+77
-56
lines changed

4 files changed

+77
-56
lines changed

app/services/utils_service.rb

+31-46
Original file line numberDiff line numberDiff line change
@@ -9,18 +9,19 @@ def self.call(file_name)
99

1010
def initialize
1111
@cities = {}
12-
@buses = {}
13-
@buses_services = {}
14-
@services = {}
12+
@buses = Hash.new { |h, k| h[k] = {} }
13+
@services = Service::SERVICES.map.with_index(1).to_h
14+
@services_buses = @services.map { |_, index| [index, []] }.to_h
15+
@next_bus_id = 0
1516
end
1617

1718
def call(file_name)
1819
ActiveRecord::Base.transaction do
1920
truncate
21+
copy_services
2022
copy_trips(file_name)
2123
copy_cities
2224
copy_buses
23-
copy_services
2425
copy_buses_services
2526
end
2627
end
@@ -59,12 +60,12 @@ def copy_trips(file_name)
5960
trip = FastJsonparser.parse(str)
6061

6162
copy(
62-
fetch_city_id(trip, :from),
63-
fetch_city_id(trip, :to),
64-
trip[:start_time],
65-
trip[:duration_minutes],
66-
trip[:price_cents],
67-
fetch_bus_id(trip[:bus])
63+
fetch_city_id(trip[:from]).to_s << ';' <<
64+
fetch_city_id(trip[:to]).to_s << ';' <<
65+
trip[:start_time].to_s << ';' <<
66+
trip[:duration_minutes].to_s << ';' <<
67+
trip[:price_cents].to_s << ';' <<
68+
fetch_bus_id(trip[:bus]).to_s << "\n"
6869
)
6970

7071
str.clear
@@ -77,37 +78,25 @@ def copy_trips(file_name)
7778
end
7879
end
7980

80-
def fetch_city_id(trip, key)
81-
id = @cities[trip[key]]
81+
def fetch_city_id(key)
82+
id = @cities[key]
8283
if !id
8384
id = @cities.size + 1
84-
@cities[trip[key]] = id
85+
@cities[key] = id
8586
end
8687

8788
id
8889
end
8990

9091
def fetch_bus_id(bus)
91-
bus_key = [bus[:model], bus[:number]]
92-
bus_id = @buses[bus_key]
92+
bus_id = @buses[bus[:model]][bus[:number]]
9393

9494
if !bus_id
95-
bus_id = @buses.size + 1
96-
@buses[bus_key] = bus_id
95+
bus_id = @next_bus_id += 1
96+
@buses[bus[:model]][bus[:number]] = bus_id
9797

9898
bus[:services].each do |service|
99-
service_id = @services[service]
100-
101-
if !service_id
102-
service_id = @services.size + 1
103-
@services[service] ||= service_id
104-
end
105-
106-
buses_service_id = @buses_services[[bus_id, service_id]]
107-
if !buses_service_id
108-
buses_service_id = @buses_services.size + 1
109-
@buses_services[[bus_id, service_id]] = buses_service_id
110-
end
99+
@services_buses[@services[service]] << bus_id
111100
end
112101
end
113102

@@ -121,11 +110,9 @@ def copy_cities
121110

122111
ActiveRecord::Base.connection.raw_connection.copy_data(sql) do
123112
@cities.each do |name, id|
124-
copy(id, name)
113+
copy(id.to_s << ';' << name << "\n")
125114
end
126115
end
127-
128-
@cities.clear
129116
end
130117

131118
def copy_buses
@@ -134,12 +121,12 @@ def copy_buses
134121
SQL
135122

136123
ActiveRecord::Base.connection.raw_connection.copy_data(sql) do
137-
@buses.each do |(model, number), id|
138-
copy(id, model, number)
124+
@buses.each do |model, numbers|
125+
numbers.each do |number, id|
126+
copy(id.to_s << ';' << model << ';' << number << "\n")
127+
end
139128
end
140129
end
141-
142-
@buses.clear
143130
end
144131

145132
def copy_services
@@ -149,29 +136,27 @@ def copy_services
149136

150137
ActiveRecord::Base.connection.raw_connection.copy_data(sql) do
151138
@services.each do |name, id|
152-
copy(id, name)
139+
copy(id.to_s << ';' << name << "\n")
153140
end
154141
end
155-
156-
@services.clear
157142
end
158143

159144
def copy_buses_services
160145
sql = <<~SQL
161-
copy buses_services (id, bus_id, service_id) from stdin with csv delimiter ';'
146+
copy buses_services (bus_id, service_id) from stdin with csv delimiter ';'
162147
SQL
163148

164149
ActiveRecord::Base.connection.raw_connection.copy_data(sql) do
165-
@buses_services.each do |(bus_id, service_id), id|
166-
copy(id, bus_id, service_id)
150+
@services_buses.each do |service_id, bus_ids|
151+
bus_ids.each do |bus_id|
152+
copy(bus_id.to_s << ';' << service_id.to_s << "\n")
153+
end
167154
end
168155
end
169-
170-
@buses_services.clear
171156
end
172157

173-
def copy(*values)
158+
def copy(values)
174159
# стримим подготовленный чанк данных в postgres
175-
ActiveRecord::Base.connection.raw_connection.put_copy_data(values.join(';') << "\n")
160+
ActiveRecord::Base.connection.raw_connection.put_copy_data(values)
176161
end
177162
end

case-study-a.md

+3-3
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525

2626
## Результаты
2727
В результате проделанной оптимизации наконец удалось обработать файл с данными.
28-
Удалось улучшить метрику системы с с 77 секунд до 1. для medium и уложиться в заданный бюджет.
29-
Файл large грузится за 7 секунд
30-
Файл 1м стал грузится за 57 секунд.
28+
Удалось улучшить метрику системы с с 77 секунд до 1. для medium и уложиться в заданный бюджет.
29+
Файл large грузится за 6.5 секунд
30+
Файл 1м стал грузится за 56 секунд.
3131

case-study-b.md

+1-6
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,7 @@
22

33
## Актуальная проблема
44
В нашем проекте возникла серьёзная проблема.
5-
6-
Необходимо было обработать файл с данными, чуть больше ста мегабайт.
7-
8-
У нас уже была программа на `ruby`, которая умела делать нужную обработку.
9-
10-
Она успешно работала на файлах размером пару мегабайт, но для большого файла она работала слишком долго, и не было понятно, закончит ли она вообще работу за какое-то разумное время.
5+
Время загрузки страницы `автобусы/Самара/Москва` при наличии уже 100к поездок в базе данных превышало любой уровень терпения.
116

127
Я решил исправить эту проблему, оптимизировав эту программу.
138

+42-1
Original file line numberDiff line numberDiff line change
@@ -1 +1,42 @@
1-
[{"id":1,"name":"Туалет"},{"id":2,"name":"WiFi"}]
1+
[
2+
{
3+
"id": 1,
4+
"name": "WiFi"
5+
},
6+
{
7+
"id": 2,
8+
"name": "Туалет"
9+
},
10+
{
11+
"id": 3,
12+
"name": "Работающий туалет"
13+
},
14+
{
15+
"id": 4,
16+
"name": "Ремни безопасности"
17+
},
18+
{
19+
"id": 5,
20+
"name": "Кондиционер общий"
21+
},
22+
{
23+
"id": 6,
24+
"name": "Кондиционер Индивидуальный"
25+
},
26+
{
27+
"id": 7,
28+
"name": "Телевизор общий"
29+
},
30+
{
31+
"id": 8,
32+
"name": "Телевизор индивидуальный"
33+
},
34+
{
35+
"id": 9,
36+
"name": "Стюардесса"
37+
},
38+
{
39+
"id": 10,
40+
"name": "Можно не печатать билет"
41+
}
42+
]

0 commit comments

Comments
 (0)