1

How can I increase my rails app performance and use the allocated heroku resources to the max? For instance, half of the memory remains unused.

Should I have increase the worker process and reduce the timeout limit? H12 an H18 errors have increased after a small raise in traffic to the website.

And when should I buy a Heroku worker? Will it reduce the H18 and H12 errors?

Please share your insights to fine tune things to improve the performance and reduce the errors.

I have started learning rails very recently. Sorry if the question is very basic.

enter image description here

config/unicorn.rb

# config/unicorn.rb
worker_processes Integer(ENV["WEB_CONCURRENCY"] || 3)
timeout 65
preload_app true

before_fork do |server, worker|
  Signal.trap 'TERM' do
    puts 'Unicorn master intercepting TERM and sending myself QUIT instead'
    Process.kill 'QUIT', Process.pid
  end

  defined?(ActiveRecord::Base) and
    ActiveRecord::Base.connection.disconnect!
end

after_fork do |server, worker|
  Signal.trap 'TERM' do
    puts 'Unicorn worker intercepting TERM and doing nothing. Wait for master to send QUIT'
  end

  defined?(ActiveRecord::Base) and
    ActiveRecord::Base.establish_connection
end

Gemfile

source 'https://rubygems.org'
ruby   '2.1.4'
.
.
.
group :production do
  gem 'pg', '0.17.1'
  gem 'rails_12factor', '0.0.2'
  gem 'unicorn'
end

Some additional information. enter image description here

LovingRails
  • 1,565
  • 2
  • 16
  • 30

2 Answers2

2

The question is not actually basic, this stuff is tricky. It's not entirely clear to me why you'd be getting H12 (request timeout) and especially H18 (request interrupted) errors due to a small increase in traffic. It would be worth trying to figure out what's going on -- for the H12's, looking at logs (or setting up logging with new relic), to figure out how long individual requests are taking to process. If an individual request not under load is taking more than a few seconds (which is already kind of long), you're probably going to want to work on figuring out why and speeding that up -- by modifying your actual app code to avoid these slow responses. The H18's are even more mysterious to me.

But if individual requests are reasonably speedy, and your problem really is just not enough processing power to handle your traffic (which I wouldn't neccesarily assume from what you've told us) -- then there are things you can do to make the most of the processing power you have available. Or you can also increase the number of dynos, definitely. If that's really the issue.

To maximize usage of the resources you've got, I would use puma rather than unicorn -- puma is currently the heroku-recommended app server for Rails.

puma, as the heroku docs explain, allows you to both run multiple Rails processes, as well as have each process process requests concurrently using multiple threads. Although multiple threaded request handling requires your app to be thread-safe -- Rails itself is, but your app code, or possibly gems you are using, may not be. Thread-safety in Rails is basically just about avoiding shared global state (class or module variables; globals), except read-only state set up on program boot (or global state that is properly synchronized for multi-threaded access).

Contrary to some other answers, I'm pretty sure Unicorn doesn't use multi-threading at all, it only uses multiple processes. Puma can run multiple processes which also each use multiple threads.

For most typical Rails apps that spend non-trivial time waiting on I/O (typically the database), multi-threaded request handling is definitely the way to maximize throughput and minimize latency, within given hardware limits. Even with MRI ruby and it's "global interpreter lock." For even better performance, deploy on JRuby or rubinius, which don't have a GIL.

Either way, it's just a question of figuring out how many puma processes, with how many maximum threads each you can fit in your dyno without running out of memory. The heroku docs suggest that you can typically run 2-4 puma processes in a 1x dyno. You could probably have each one with a maximum thread configuration of 2 or even 10 threads.

The heroku puma docs are pretty good, and should serve to give you an overview of the considerations.

But this is all assuming, as your question did, that your issue with H12 and H18 errors from heroku is really that you need more processing power to handle your request traffic. I'm not certain that's really it, but it might be.

jrochkind
  • 22,799
  • 12
  • 59
  • 74
1

The default unicorn configuration probably assumes that you are running on 1x dynos.

Since you are using 2x dynos (as you should), you can increase the number of threads per dyno. Try setting WEB_CONCURRENCY to 5 and check the memory usage.

You should also profile your application using a service like NewRelic to see which method calls are slow.

By default, adding Heroku workers will not increase the speed of your application or eliminate errors. However, if you have slow operations in your application which could be run asynchronously (that are not required to generate the response -- for example, sending email), then you could use ActiveJob to queue them up for background processing, and you could use a Heroku Worker to perform the work.

Nick Urban
  • 3,568
  • 2
  • 22
  • 36