Skip to content

Optimization #121

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@
/tmp
/log
/public
/.idea
1 change: 1 addition & 0 deletions .rspec
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
--require spec_helper
2 changes: 1 addition & 1 deletion .ruby-version
Original file line number Diff line number Diff line change
@@ -1 +1 @@
3.4.1
3.3.1
13 changes: 12 additions & 1 deletion Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,17 @@ gem 'puma'
gem 'listen'
gem 'bootsnap'
gem 'rack-mini-profiler'

gem 'sprockets-rails', :require => 'sprockets/railtie'
# Windows does not include zoneinfo files, so bundle the tzinfo-data gem
gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw, :jruby]

group :development do
gem 'pghero'
gem "pg_query", ">= 2"
gem "ruby-prof"
gem "memory_profiler"
end

group :development, :test do
gem 'rspec-rails', '~> 7.0.0'
end
43 changes: 42 additions & 1 deletion Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -82,11 +82,15 @@ GEM
connection_pool (2.5.0)
crass (1.0.6)
date (3.4.1)
diff-lcs (1.6.0)
drb (2.2.1)
erubi (1.13.1)
ffi (1.17.1-arm64-darwin)
globalid (1.2.1)
activesupport (>= 6.1)
google-protobuf (4.29.3-arm64-darwin)
bigdecimal
rake (>= 13)
i18n (1.14.7)
concurrent-ruby (~> 1.0)
io-console (0.8.0)
Expand All @@ -107,6 +111,7 @@ GEM
net-pop
net-smtp
marcel (1.0.4)
memory_profiler (1.1.0)
mini_mime (1.1.5)
minitest (5.25.4)
msgpack (1.8.0)
Expand All @@ -123,6 +128,10 @@ GEM
nokogiri (1.18.2-arm64-darwin)
racc (~> 1.4)
pg (1.5.9)
pg_query (6.0.0)
google-protobuf (>= 3.25.3)
pghero (3.6.1)
activerecord (>= 6.1)
pp (0.6.2)
prettyprint
prettyprint (0.2.0)
Expand Down Expand Up @@ -179,7 +188,32 @@ GEM
psych (>= 4.0.0)
reline (0.6.0)
io-console (~> 0.5)
rspec-core (3.13.3)
rspec-support (~> 3.13.0)
rspec-expectations (3.13.3)
diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.13.0)
rspec-mocks (3.13.2)
diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.13.0)
rspec-rails (7.0.2)
actionpack (>= 7.0)
activesupport (>= 7.0)
railties (>= 7.0)
rspec-core (~> 3.13)
rspec-expectations (~> 3.13)
rspec-mocks (~> 3.13)
rspec-support (~> 3.13)
rspec-support (3.13.2)
ruby-prof (1.7.1)
securerandom (0.4.1)
sprockets (4.2.1)
concurrent-ruby (~> 1.0)
rack (>= 2.2.4, < 4)
sprockets-rails (3.5.2)
actionpack (>= 6.1)
activesupport (>= 6.1)
sprockets (>= 3.0.0)
stringio (3.1.2)
thor (1.3.2)
timeout (0.4.3)
Expand All @@ -194,19 +228,26 @@ GEM
zeitwerk (2.7.1)

PLATFORMS
arm64-darwin-23
arm64-darwin-24

DEPENDENCIES
bootsnap
listen
memory_profiler
pg
pg_query (>= 2)
pghero
puma
rack-mini-profiler
rails (~> 8.0.1)
rspec-rails (~> 7.0.0)
ruby-prof
sprockets-rails
tzinfo-data

RUBY VERSION
ruby 3.4.1p0
ruby 3.3.1p55

BUNDLED WITH
2.6.2
2 changes: 1 addition & 1 deletion Readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@

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

`rake reload_json[fixtures/large.json]`
`rake "reload_json[fixtures/large.json]"`
Copy link
Collaborator

Choose a reason for hiding this comment

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

👍


Для импорта этого объёма данных
- вам может помочь batch-import
Expand Down
2 changes: 1 addition & 1 deletion app/controllers/trips_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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.preload(:from, :to, bus: :services).where(from: @from, to: @to)
Copy link
Collaborator

Choose a reason for hiding this comment

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

а мы тут сортировку не потеряли?

end
end
141 changes: 141 additions & 0 deletions app/services/reloader.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
class Reloader
def self.call(file_name)
new(file_name).call
end

def initialize(file_name)
@file_name = file_name
@json = JSON.parse(File.read(file_name))
end

def call
ActiveRecord::Base.transaction do
clear_tables
collect_and_create_data
end
end

private

attr_reader :file_name, :json

def clear_tables
City.delete_all
Bus.delete_all
Service.delete_all
Trip.delete_all
ActiveRecord::Base.connection.execute('DELETE FROM buses_services')
end

def collect_and_create_data
collected_data = collect_data
created_records = create_main_records(collected_data)
create_associated_records(created_records)
end

def collect_data
{
cities: Set.new,
buses: [],
services: Set.new
}.tap do |data|
Copy link
Collaborator

Choose a reason for hiding this comment

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

кстати вот в контексте профилирования и оптимизации не очень удобно работать с tap

в отчётах профилировщиков это может выглядеть как - вся работа была в блоке tap (а что тормозило внутри этого блока, и какой именно это был tap в случае если их несколько - непонятно)

json.each do |trip|
data[:cities] << trip['from']
data[:cities] << trip['to']
data[:services].merge(trip['bus']['services'])
data[:buses] << {
number: trip['bus']['number'],
model: trip['bus']['model']
}
end
end
end

def create_main_records(collected_data)
{
cities: create_cities(collected_data[:cities]),
services: create_services(collected_data[:services]),
buses: create_buses(collected_data[:buses])
}
end

def create_cities(cities)
City.insert_all(
cities.map { |name| { name: name } },
returning: [:id, :name]
).index_by { |city| city['name'] }
end

def create_services(services)
Service.insert_all(
services.map { |name| { name: name } },
returning: [:id, :name]
).index_by { |service| service['name'] }
end

def create_buses(buses)
Bus.insert_all(
buses.uniq { |bus| bus[:number] },
returning: [:id, :number]
).index_by { |bus| bus['number'] }
end

def create_associated_records(created_records)
buses_services_data = []
trips_data = []

json.each do |trip|
bus_id = created_records[:buses][trip['bus']['number']]['id']

collect_buses_services_data(
buses_services_data,
bus_id,
trip['bus']['services'],
created_records[:services]
)

collect_trips_data(
trips_data,
trip,
bus_id,
created_records[:cities]
)
end

create_buses_services(buses_services_data)
create_trips(trips_data)
end

def collect_buses_services_data(data, bus_id, services, services_records)
services.each do |service_name|
data << {
bus_id: bus_id,
service_id: services_records[service_name]['id']
}
end
end

def collect_trips_data(data, trip, bus_id, cities)
data << {
from_id: cities[trip['from']]['id'],
to_id: cities[trip['to']]['id'],
bus_id: bus_id,
start_time: trip['start_time'],
duration_minutes: trip['duration_minutes'],
price_cents: trip['price_cents']
}
end

def create_buses_services(data)
return if data.empty?

values = data.uniq.map { |r| "(#{r[:bus_id]}, #{r[:service_id]})" }.join(', ')
ActiveRecord::Base.connection.execute(
"INSERT INTO buses_services (bus_id, service_id) VALUES #{values}"
)
end

def create_trips(data)
Trip.insert_all!(data)
end
end
16 changes: 11 additions & 5 deletions app/views/trips/_trip.html.erb
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
<li><%= "Отправление: #{trip.start_time}" %></li>
<li><%= "Прибытие: #{(Time.parse(trip.start_time) + trip.duration_minutes.minutes).strftime('%H:%M')}" %></li>
<li><%= "В пути: #{trip.duration_minutes / 60}ч. #{trip.duration_minutes % 60}мин." %></li>
<li><%= "Цена: #{trip.price_cents / 100}р. #{trip.price_cents % 100}коп." %></li>
<li><%= "Автобус: #{trip.bus.model} №#{trip.bus.number}" %></li>
<ul class="trip-details" data-bus-id="<%= trip.bus.id %>">
<li><%= "Отправление: #{trip.start_time}" %></li>
<li><%= "Прибытие: #{(Time.parse(trip.start_time) + trip.duration_minutes.minutes).strftime('%H:%M')}" %></li>
<li><%= "В пути: #{trip.duration_minutes / 60}ч. #{trip.duration_minutes % 60}мин." %></li>
<li><%= "Цена: #{trip.price_cents / 100}р. #{trip.price_cents % 100}коп." %></li>
<li><%= "Автобус: #{trip.bus.model} №#{trip.bus.number}" %></li>

<% if trip.bus.services.present? %>
<%= render "services", services: trip.bus.services %>
<% end %>
</ul>
12 changes: 3 additions & 9 deletions app/views/trips/index.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,6 @@
<%= "В расписании #{@trips.count} рейсов" %>
</h2>

<% @trips.each do |trip| %>
<ul>
<%= render "trip", trip: trip %>
<% if trip.bus.services.present? %>
<%= render "services", services: trip.bus.services %>
<% end %>
</ul>
<%= render "delimiter" %>
<% end %>
<div class="trips-container">
<%= render partial: "trip", collection: @trips, spacer_template: "delimiter" %>
Copy link
Collaborator

Choose a reason for hiding this comment

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

👍

</div>
Loading