Skip to content
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
14 changes: 11 additions & 3 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,6 @@ group :development do
gem "better_errors", "~> 2.5"
gem "binding_of_caller", "~> 0.8"
gem "brakeman", "~> 4.4", require: false
gem "bullet", "~> 5.9"
gem "bundler-audit", "~> 0.6"
gem "derailed_benchmarks", "~> 1.3"
gem "guard", "~> 2.15", require: false
Expand All @@ -117,7 +116,11 @@ group :development do
gem "web-console", "~> 3.7"
end

group :development, :test do
group :development, :profile do
gem "bullet", "~> 5.9"
end

group :development, :test, :profile do
gem "capybara", "~> 3.13"
gem "derailed", "~> 0.1"
gem "erb_lint", "~> 0.0", require: false
Expand Down Expand Up @@ -149,11 +152,16 @@ group :test do
gem "shoulda-matchers", "4.0.0.rc1", require: false
gem "simplecov", "~> 0.16", require: false
gem "sinatra", "~> 2.0"
gem "stackprof", "~> 0.2", require: false, platforms: :ruby
gem "stripe-ruby-mock", "~> 2.5", require: "stripe_mock"
gem "test-prof", "~> 0.7"
gem "timecop", "~> 0.9"
gem "webmock", "~> 3.5"
gem "zonebie", "~> 0.6.1"
end

gem "rack-mini-profiler", require: false
gem "stackprof", require: false
gem "newrelic_rpm"
gem "prometheus_exporter"

# rubocop:enable LineLength
13 changes: 12 additions & 1 deletion 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 @@ -612,6 +613,8 @@ 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)
mini_portile2 (~> 2.4.0)
Expand Down Expand Up @@ -651,6 +654,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 @@ -677,6 +682,8 @@ GEM
rack (2.0.6)
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 @@ -932,6 +939,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 @@ -1013,13 +1021,15 @@ DEPENDENCIES
liquid (~> 4.0)
memory_profiler (~> 0.9)
nakayoshi_fork
newrelic_rpm
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
pry (~> 0.12)
pry-byebug (~> 3.7)
pry-rails (~> 0.3)
Expand All @@ -1029,6 +1039,7 @@ DEPENDENCIES
pusher (~> 1.3)
pusher-push-notifications (~> 1.0)
rack-host-redirect (~> 1.3)
rack-mini-profiler
rack-timeout (~> 0.5)
rails (~> 5.1.6)
rails-assets-airbrake-js-client (~> 1.5)!
Expand Down Expand Up @@ -1063,7 +1074,7 @@ DEPENDENCIES
spring-commands-rspec (~> 1.0)
sprockets (~> 3.7)
staccato (~> 0.5)
stackprof (~> 0.2)
stackprof
storext (~> 2.2)
stripe (~> 4.8)
stripe-ruby-mock (~> 2.5)
Expand Down
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 -a prometheus/custom_collector.rb
4 changes: 4 additions & 0 deletions Procfile.lp
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
web: bin/rails s -p 3000 -e profile
webpacker: ./bin/webpack-dev-server
job: bin/rake jobs:work
prometheus_exporter: bundle exec prometheus_exporter -a prometheus/custom_collector.rb
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
9 changes: 8 additions & 1 deletion bin/startup
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,13 @@ def system!(*args)
end

chdir APP_ROOT do
env = ENV.fetch("RAILS_ENV", "development")
puts "== STARTING UP =="
system! "foreman start -f Procfile.dev"

if env == "profile"
system! "bundle exec rake assets:precompile"
system! "foreman start -f Procfile.lp"
else
system! "foreman start -f Procfile.dev"
end
end
99 changes: 99 additions & 0 deletions config/case_study.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
# Гипотеза

Все инструменты мониторинга показывают, что самой горячей точкой является главная страница, StoriesController#index.

В частности, заметное время занимает рендеринг partial-ов \_single_story.html.erb.

Рассмотрите гипотезу о том, что можно закешировать <%= render "articles/single_story", story: story %> в \_main_stories_feed.html.erb и это даст заметный эффект.

## Benchmarking до оптимизации

```
Benchmarking 127.0.0.1 (be patient).....done

Server Software:
Server Hostname: 127.0.0.1
Server Port: 3000

Document Path: /
Document Length: 137084 bytes

Concurrency Level: 1
Time taken for tests: 19.019 seconds
Complete requests: 100
Failed requests: 0
Total transferred: 13750700 bytes
HTML transferred: 13708400 bytes
Requests per second: 5.26 [#/sec] (mean)
Time per request: 190.187 [ms] (mean)
Time per request: 190.187 [ms] (mean, across all concurrent requests)
Transfer rate: 706.06 [Kbytes/sec] received

Connection Times (ms)
min mean[+/-sd] median max
Connect: 0 0 0.0 0 0
Processing: 134 190 61.3 176 398
Waiting: 134 190 61.3 176 398
Total: 134 190 61.3 176 398

Percentage of the requests served within a certain time (ms)
50% 176
66% 192
75% 199
80% 205
90% 260
95% 366
98% 392
99% 398
100% 398 (longest request)
```

## Проверка гипотезы

Добавила кэширование `story` в `app/views/stories/_main_stories_feed.html.erb`

## Benchmarking после оптимизации

```
Benchmarking 127.0.0.1 (be patient).....done

Server Software:
Server Hostname: 127.0.0.1
Server Port: 3000

Document Path: /
Document Length: 137084 bytes

Concurrency Level: 1
Time taken for tests: 5.803 seconds
Complete requests: 100
Failed requests: 0
Total transferred: 13750700 bytes
HTML transferred: 13708400 bytes
Requests per second: 17.23 [#/sec] (mean)
Time per request: 58.035 [ms] (mean)
Time per request: 58.035 [ms] (mean, across all concurrent requests)
Transfer rate: 2313.85 [Kbytes/sec] received

Connection Times (ms)
min mean[+/-sd] median max
Connect: 0 0 0.0 0 0
Processing: 37 58 24.2 48 147
Waiting: 37 58 24.1 48 146
Total: 37 58 24.2 48 147

Percentage of the requests served within a certain time (ms)
50% 48
66% 53
75% 58
80% 72
90% 84
95% 128
98% 141
99% 147
100% 147 (longest request)
```

## Выводы

Время загрузки страницы ускорилось в 3,2 раза

Copy link
Copy Markdown

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/environments/development.rb
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,8 @@ def yarn_integrity_enabled?
Bullet.enable = true
Bullet.console = true
end

config.webpacker.check_yarn_integrity = false
end

# rubocop:enable Metrics/BlockLength
102 changes: 102 additions & 0 deletions config/environments/profile.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
# rubocop:disable Metrics/BlockLength
#
def yarn_integrity_enabled?
ENV.fetch("YARN_INTEGRITY_ENABLED", "false") == "true"
end

Rails.application.configure do
# Verifies that versions and hashed value of the package contents in the project's package.json
config.webpacker.check_yarn_integrity = yarn_integrity_enabled?

# Settings specified here will take precedence over those in config/application.rb.

# In the development environment your application's code is reloaded on
# every request. This slows down response time but is perfect for development
# since you don't have to restart the web server when you make code changes.
config.cache_classes = true

# Do not eager load code on boot.
config.eager_load = true

# Show full error reports and disable caching.
config.consider_all_requests_local = true

# Enable/disable caching. By default caching is disabled.
if Rails.root.join("tmp/caching-dev.txt").exist?
config.action_controller.perform_caching = true

config.cache_store = :memory_store
config.public_file_server.headers = {
"Cache-Control" => "public, max-age=172800"
}
else
config.action_controller.perform_caching = false

config.cache_store = :null_store
end

# Don't care if the mailer can't send.
config.action_mailer.raise_delivery_errors = false

# Print deprecation notices to the Rails logger.
config.active_support.deprecation = :log

# Raise an error on page load if there are pending migrations.
config.active_record.migration_error = :page_load

# Debug mode disables concatenation and preprocessing of assets.
# This option may cause significant delays in view rendering with a large
# number of complex assets.
config.assets.debug = false

# Asset digests allow you to set far-future HTTP expiration dates on all assets,
# yet still be able to expire them through the digest params.
config.assets.digest = false

# Supress logger output for asset requests.
config.assets.quiet = true

# Do not fallback to assets pipeline if a precompiled asset is missed.
config.assets.compile = true

# Adds additional error checking when serving assets at runtime.
# Checks for improperly declared sprockets dependencies.
# Raises helpful error messages.
config.assets.raise_runtime_errors = true

config.action_mailer.perform_caching = false

config.app_domain = "localhost:3000"

config.action_mailer.default_url_options = { host: "localhost:3000" }
config.action_mailer.delivery_method = :smtp
config.action_mailer.perform_deliveries = true
config.action_mailer.default_url_options = { host: config.app_domain }
config.action_mailer.smtp_settings = {
address: "smtp.gmail.com",
port: "587",
enable_starttls_auto: true,
user_name: '<%= ENV["DEVELOPMENT_EMAIL_USERNAME"] %>',
password: '<%= ENV["DEVELOPMENT_EMAIL_PASSWORD"] %>',
authentication: :plain,
domain: "localhost:3000"
}

config.action_mailer.preview_path = "#{Rails.root}/spec/mailers/previews"

# Raises error for missing translations
# config.action_view.raise_on_missing_translations = true

config.public_file_server.enabled = true

config.file_watcher = ActiveSupport::EventedFileUpdateChecker

# Install the Timber.io logger
send_logs_to_timber = ENV["SEND_LOGS_TO_TIMBER"] || "false" # <---- set to false to stop sending dev logs to Timber.io
log_device = send_logs_to_timber == "true" ? Timber::LogDevices::HTTP.new(ENV["TIMBER"]) : STDOUT
logger = Timber::Logger.new(log_device)
logger.level = config.log_level
config.logger = ActiveSupport::TaggedLogging.new(logger)
end

# rubocop:enable Metrics/BlockLength
3 changes: 2 additions & 1 deletion config/initializers/airbrake.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#
# Configuration details:
# https://github.com/airbrake/airbrake-ruby#configuration

Airbrake.configure do |c|
# You must set both project_id & project_key. To find your project_id and
# project_key navigate to your project's General Settings and copy the values
Expand Down Expand Up @@ -41,7 +42,7 @@
# environments.
# NOTE: This option *does not* work if you don't set the 'environment' option.
# https://github.com/airbrake/airbrake-ruby#ignore_environments
c.ignore_environments = %w[test development]
c.ignore_environments = %w[test development profile]

# A list of parameters that should be filtered out of what is sent to
# Airbrake. By default, all "password" attributes will have their contents
Expand Down
6 changes: 6 additions & 0 deletions config/initializers/prometheus.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
if Rails.env != "test"
require "prometheus_exporter/middleware"

# This reports stats per request like HTTP status and timings
Rails.application.middleware.unshift PrometheusExporter::Middleware
end
8 changes: 8 additions & 0 deletions config/initializers/rack_mini_profiler.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# frozen_string_literal: true

if Rails.env.development? || Rails.env.profile?
require "rack-mini-profiler"

# The initializer was required late, so initialize it manually.
Rack::MiniProfilerRails.initialize!(Rails.application)
end
Loading