Skip to content
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

igor-arkhipov/performance-improvement-4 #136

Open
wants to merge 6 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
16 changes: 16 additions & 0 deletions .eslintrc.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
env:
browser: true
node: true
es6: true
extends: 'eslint:recommended'
globals:
Atomics: readonly
SharedArrayBuffer: readonly
parserOptions:
ecmaFeatures:
jsx: true
ecmaVersion: 2018
sourceType: module
plugins:
- react
rules: {}
2 changes: 1 addition & 1 deletion .ruby-version
Original file line number Diff line number Diff line change
@@ -1 +1 @@
2.6.3
2.7.7
11 changes: 9 additions & 2 deletions Gemfile
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# rubocop:disable LineLength
source "https://rubygems.org"
ruby "2.6.3"
ruby "2.7.7"

# Enforce git to transmitted via https.
# workaround until bundler 2.0 is released.
Expand Down Expand Up @@ -59,19 +59,22 @@ gem "jquery-rails", "~> 4.3"
gem "kaminari", "~> 1.1"
gem "libhoney", "~> 1.11"
gem "liquid", "~> 4.0"
gem 'newrelic_rpm', "~> 9.6"
gem "nokogiri", "~> 1.10"
gem "octokit", "~> 4.13"
gem "omniauth", "~> 1.9"
gem "omniauth-github", "~> 1.3"
gem "omniauth-twitter", "~> 1.4"
gem "pg", "~> 1.1"
gem "prometheus_exporter", "~> 2.0"
gem "pry", "~> 0.12"
gem "pry-rails", "~> 0.3"
gem "puma", "~> 3.12"
gem "pundit", "~> 2.0"
gem "pusher", "~> 1.3"
gem "pusher-push-notifications", "~> 1.0"
gem "rack-host-redirect", "~> 1.3"
gem "rack-mini-profiler", "~> 3.1", require: false
gem "rack-timeout", "~> 0.5"
gem "rails", "~> 5.1.6"
gem "rails-assets-airbrake-js-client", "~> 1.5", source: "https://rails-assets.org"
Expand All @@ -89,7 +92,7 @@ gem "sdoc", "~> 1.0", group: :doc
gem "serviceworker-rails", "~> 0.5"
gem "share_meow_client", "~> 0.1"
gem "sitemap_generator", "~> 6.0"
gem "skylight", "~> 3.1"
gem "skylight", "~> 4.3"
gem "slack-notifier", "~> 2.3"
gem "sprockets", "~> 3.7"
gem "staccato", "~> 0.5"
Expand All @@ -103,6 +106,10 @@ gem "validate_url", "~> 1.0"
gem "webpacker", "~> 3.5"
gem "webpush", "~> 0.3"

group :localproduction, :development do
gem 'meta_request', "~> 0.7"
end

group :development do
gem "better_errors", "~> 2.5"
gem "binding_of_caller", "~> 0.8"
Expand Down
39 changes: 28 additions & 11 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,7 @@ GEM
descendants_tracker (~> 0.0.4)
ice_nine (~> 0.11.0)
thread_safe (~> 0.3, >= 0.3.1)
base64 (0.2.0)
bcrypt (3.1.12)
benchmark-ips (2.7.2)
better_errors (2.5.0)
Expand Down Expand Up @@ -212,7 +213,7 @@ GEM
coffee-script-source
execjs
coffee-script-source (1.12.2)
concurrent-ruby (1.1.5)
concurrent-ruby (1.2.2)
connection_pool (2.2.2)
counter_culture (2.1.2)
activerecord (>= 3.0.0)
Expand Down Expand Up @@ -317,7 +318,7 @@ GEM
faraday_middleware (>= 0.9)
loofah (>= 2.0)
sax-machine (>= 1.0)
ffi (1.9.25)
ffi (1.16.3)
figaro (1.1.1)
thor (~> 0.14)
fission (0.5.0)
Expand Down Expand Up @@ -536,7 +537,7 @@ GEM
mime-types (~> 3.0)
multi_xml (>= 0.5.2)
httpclient (2.8.3)
i18n (1.6.0)
i18n (1.14.1)
concurrent-ruby (~> 1.0)
ice_nine (0.11.2)
inflecto (0.0.2)
Expand Down Expand Up @@ -592,13 +593,16 @@ GEM
memoizable (0.4.2)
thread_safe (~> 0.3, >= 0.3.1)
memory_profiler (0.9.12)
meta_request (0.7.4)
rack-contrib (>= 1.1, < 3)
railties (>= 3.0.0, < 7.1)
method_source (0.9.2)
mime-types (3.2.2)
mime-types-data (~> 3.2015)
mime-types-data (3.2018.0812)
mini_mime (1.0.1)
mini_portile2 (2.4.0)
minitest (5.11.3)
minitest (5.20.0)
momentjs-rails (2.20.1)
railties (>= 3.1)
msgpack (1.2.4)
Expand All @@ -612,8 +616,10 @@ GEM
net-http-persistent (3.0.0)
connection_pool (~> 2.2)
netrc (0.11.0)
newrelic_rpm (9.6.0)
base64
nio4r (2.3.1)
nokogiri (1.10.1)
nokogiri (1.10.2)
mini_portile2 (~> 2.4.0)
notiffany (0.1.1)
nenv (~> 0.1)
Expand Down Expand Up @@ -651,6 +657,8 @@ GEM
ast (~> 2.4.0)
pg (1.1.4)
powerpack (0.1.2)
prometheus_exporter (2.0.8)
webrick
pry (0.12.2)
coderay (~> 1.1.0)
method_source (~> 0.9.0)
Expand All @@ -675,8 +683,12 @@ GEM
pusher-signature (0.1.8)
raabro (1.1.6)
rack (2.0.6)
rack-contrib (2.4.0)
rack (< 4)
rack-host-redirect (1.3.0)
rack
rack-mini-profiler (3.1.1)
rack (>= 1.2.0)
rack-protection (2.0.4)
rack
rack-proxy (0.6.5)
Expand Down Expand Up @@ -843,9 +855,9 @@ GEM
tilt (~> 2.0)
sitemap_generator (6.0.2)
builder (~> 3.0)
skylight (3.1.4)
skylight-core (= 3.1.4)
skylight-core (3.1.4)
skylight (4.3.2)
skylight-core (= 4.3.2)
skylight-core (4.3.2)
activesupport (>= 4.2.0)
slack-notifier (2.3.2)
smart_properties (1.13.1)
Expand Down Expand Up @@ -895,7 +907,7 @@ GEM
multipart-post (~> 2.0)
naught (~> 1.0)
simple_oauth (~> 0.3.0)
tzinfo (1.2.5)
tzinfo (1.2.11)
thread_safe (~> 0.1)
uber (0.1.0)
uglifier (4.1.20)
Expand Down Expand Up @@ -932,6 +944,7 @@ GEM
webpush (0.3.2)
hkdf (~> 0.2)
jwt
webrick (1.8.1)
websocket-driver (0.6.5)
websocket-extensions (>= 0.1.0)
websocket-extensions (0.1.3)
Expand Down Expand Up @@ -1012,14 +1025,17 @@ DEPENDENCIES
libhoney (~> 1.11)
liquid (~> 4.0)
memory_profiler (~> 0.9)
meta_request (~> 0.7)
nakayoshi_fork
newrelic_rpm (~> 9.6)
nokogiri (~> 1.10)
octokit (~> 4.13)
omniauth (~> 1.9)
omniauth-github (~> 1.3)
omniauth-twitter (~> 1.4)
parallel_tests (~> 2.27)
pg (~> 1.1)
prometheus_exporter (~> 2.0)
pry (~> 0.12)
pry-byebug (~> 3.7)
pry-rails (~> 0.3)
Expand All @@ -1029,6 +1045,7 @@ DEPENDENCIES
pusher (~> 1.3)
pusher-push-notifications (~> 1.0)
rack-host-redirect (~> 1.3)
rack-mini-profiler (~> 3.1)
rack-timeout (~> 0.5)
rails (~> 5.1.6)
rails-assets-airbrake-js-client (~> 1.5)!
Expand Down Expand Up @@ -1057,7 +1074,7 @@ DEPENDENCIES
simplecov (~> 0.16)
sinatra (~> 2.0)
sitemap_generator (~> 6.0)
skylight (~> 3.1)
skylight (~> 4.3)
slack-notifier (~> 2.3)
spring (~> 2.0)
spring-commands-rspec (~> 1.0)
Expand All @@ -1082,7 +1099,7 @@ DEPENDENCIES
zonebie (~> 0.6.1)

RUBY VERSION
ruby 2.6.3p62
ruby 2.7.7p221

BUNDLED WITH
1.17.3
1 change: 1 addition & 0 deletions Procfile.dev
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
web: bin/rails s -p 3000
webpacker: ./bin/webpack-dev-server
job: bin/rake jobs:work
prometheus_exporter: bundle exec prometheus_exporter
4 changes: 4 additions & 0 deletions Procfile.localproduction
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
web: bin/rails s -p 3000
webpacker: ./bin/webpack-dev-server
job: bin/rake jobs:work
prometheus_exporter: bundle exec prometheus_exporter
4 changes: 3 additions & 1 deletion app/views/stories/_main_stories_feed.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,9 @@
<% if !user_signed_in? && i == 4 %>
<%= render "stories/sign_in_invitation" %>
<% end %>
<%= render "articles/single_story", story: story %>
<% cache story do %>
<%= render "articles/single_story", story: story %>
<% end %>
<% end %>
<% end %>
<% if @stories.size > 1 %>
Expand Down
7 changes: 6 additions & 1 deletion bin/startup
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,10 @@ end

chdir APP_ROOT do
puts "== STARTING UP =="
system! "foreman start -f Procfile.dev"
if ENV['RAILS_ENV'] == 'localproduction'
system! "bundle exec rake assets:precompile"
system! "foreman start -f Procfile.localproduction"
else
system! "foreman start -f Procfile.dev"
end
end
3 changes: 2 additions & 1 deletion bin/webpack-dev-server
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ CONFIG_FILE = File.join(APP_PATH, "config/webpacker.yml")
NODE_MODULES_PATH = File.join(APP_PATH, "node_modules")
WEBPACK_CONFIG = File.join(APP_PATH, "config/webpack/#{NODE_ENV}.js")

DEFAULT_LISTEN_HOST_ADDR = NODE_ENV == "development" ? "localhost" : "0.0.0.0"
LOCALHOST = NODE_ENV == "development" || NODE_ENV == "localproduction"
DEFAULT_LISTEN_HOST_ADDR = LOCALHOST ? "localhost" : "0.0.0.0"

def args(key)
index = ARGV.index(key)
Expand Down
105 changes: 105 additions & 0 deletions case-study.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
# Case-study оптимизации

## Актуальная проблема

Нужно оптимизировать механизм загрузки главной страницы проекта `StoriesController#index`.

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

## Формирование метрики

Для того, чтобы понимать, дают ли мои изменения положительный эффект на быстродействие программы я придумал использовать такую метрику: число секунд, требуемые для прохождения нагрузочного бенчмарка `ab` с 30 запросами к главной странице проекта.

## Вникаем в детали системы, чтобы найти главные точки роста

Для того, чтобы найти "точки роста" для оптимизации я воспользовался инструментами rack-mini-profiler, Prometheus, Grafana.

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

### Ваша находка №1

- какой отчёт показал главную точку роста

- rack-mini-profiler: заметное время занимает рендеринг `partial`-ов `_single_story.html.erb`, делаются 11 вызовов. Rendering: stories/\_main_stories_feed.html.erb duration 3.3ms, with children 57.1ms
- ```
Document Path: /
Document Length: 139178 bytes
Concurrency Level: 1
Time taken for tests: 2.373 seconds
Complete requests: 30
Failed requests: 0
Total transferred: 4206630 bytes
HTML transferred: 4175340 bytes
Requests per second: 12.64 [#/sec] (mean)
Time per request: 79.097 [ms] (mean)
Time per request: 79.097 [ms] (mean, across all concurrent requests)
Transfer rate: 1731.23 [Kbytes/sec] received
Connection Times (ms)
min mean[+/-sd] median max
Connect: 0 0 0.2 0 1
Processing: 69 79 18.0 74 164
Waiting: 69 79 18.0 74 164
Total: 69 79 18.2 75 166
Percentage of the requests served within a certain time (ms)
50% 75
66% 77
75% 79
80% 80
90% 89
95% 111
98% 166
99% 166
100% 166 (longest request)
```
Нашей точкой роста будет число вызовов отдельных шаблонов `articles/_single_story.html.erb`
- как вы решили её оптимизировать
- Применил кэширование в шаблоне при работе с коллекциями stories. Гипотеза состоит в том, что в случае не частых обновлений постов на главной странице кэширование снизит время изначальной загрузки за счет переиспользования данных об уже имеющихся постах.
- как изменилась метрика
- Метрика снизилась с 2.37 сек до 1.42 сек
```
Document Path: /
Document Length: Variable
Concurrency Level: 1
Time taken for tests: 1.423 seconds
Complete requests: 30
Failed requests: 0
Total transferred: 4208029 bytes
HTML transferred: 4176759 bytes
Requests per second: 21.08 [#/sec] (mean)
Time per request: 47.445 [ms] (mean)
Time per request: 47.445 [ms] (mean, across all concurrent requests)
Transfer rate: 2887.15 [Kbytes/sec] received
Connection Times (ms)
min mean[+/-sd] median max
Connect: 0 0 0.2 0 1
Processing: 39 47 15.7 44 120
Waiting: 39 47 15.7 43 119
Total: 39 47 15.9 44 121
Percentage of the requests served within a certain time (ms)
50% 44
66% 45
75% 45
80% 47
90% 49
95% 84
98% 121
99% 121
100% 121 (longest request)
```
- как изменился отчёт профилировщика
- Rendering: stories/\_main_stories_feed.html.erb duration 10.1ms, with children 10.2ms
## Результаты
В результате проделанной оптимизации удалось ускорить время рендеринга главной страницы более чем в полтора раза.
Copy link
Collaborator

Choose a reason for hiding this comment

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

👍

2 changes: 2 additions & 0 deletions config/application.rb
Original file line number Diff line number Diff line change
Expand Up @@ -53,5 +53,7 @@ class Application < Rails::Application
end
ReservedWords.all = [ReservedWords::BASE_WORDS + top_routes].flatten.compact.uniq
end

config.skylight.environments << 'localproduction' if Rails.env.localproduction?
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
Loading