How rails resolve multi-requests at the same time?
Asked Answered
B

3

24

Prepare for the test: sleep 10 in a action

Test: Open two tabs in the browser to visit the action

Result: When the second request is running, the first request finished and began rendering the view, but the view is still blank. After the second request finished too, the two requests finished rendering the view at the same time.

Conclusion: Rails is just one single instance. One request can only enter the action after the previous requests finish. But how to explain the response part? Why the multi-requests finish rendering the views at the same time?

Befuddle answered 25/12, 2012 at 2:14 Comment(0)
S
4

Are you using a WEBrick server? That must be because your server is a single threaded server and is capable of fulfilling one request at a time (because of the single worker thread). Now in case of multiple requests, it runs the action part of the request and before running the view renderer it checks to see if there are any pending requests. Now if 10 requests are lined up, it would first complete all of them before actually rendering the views. When all of these requests are completed, the views would now be rendered sequentially.

You can switch to Passenger or Unicorn server if you want multi-threaded environment.

Hope that makes sense.

Stebbins answered 25/12, 2012 at 5:8 Comment(4)
thanks, you make me less confused. I test with more requests at the same time, it seems that after less than 10 requests are lined up, webrick will render the views and then resolve other requests.Befuddle
I think webrick is not a single threaded server, you can set 'config.threadsafe!' in production.rb, then it will resolve multi-requests at the same time.Befuddle
config.threadsafe! won't automatically make WEBrick multi-threaded. See my answer for details.Pinion
This answer isn't entirely correct. Instead of doing the check before just rendering the view, Rails puts a lock around the whole request which includes processing the request and generating the response, thanks to Rack::Lock. Also Passenger or Unicorn won't spawn threads. They spawn workers which run as separate processes.Coven
P
25

WEBrick is multi-threaded but Rails developers hard-coded a mutex, so it can handle just one request at a time. You can monkey-patch Rails::Server and you are free to run a multi-threaded WEBrick.

Just note that WEBrick will be multithreaded only when config config.cache_classes = true and config.eager_load = true, which is typical to RAILS_ENV=production. This is because class reloading in development is not thread safe.

To get WEBrick fully multi-threaded in Rails 4.0, just add this to config/initializers/multithreaded_webrick.rb:

# Remove Rack::Lock so WEBrick can be fully multi-threaded.
require 'rails/commands/server'

class Rails::Server
  def middleware
    middlewares = []
    middlewares << [Rails::Rack::Debugger] if options[:debugger]
    middlewares << [::Rack::ContentLength]

    Hash.new middlewares
  end
end

The offending code in rails/commands/server.rb that we got rid of is:

# FIXME: add Rack::Lock in the case people are using webrick.
# This is to remain backwards compatible for those who are
# running webrick in production. We should consider removing this
# in development.
if server.name == 'Rack::Handler::WEBrick'
  middlewares << [::Rack::Lock]
end

It's not needed on Rails 4.2. It's concurrent out-of-the-box.

Pinion answered 6/1, 2014 at 0:12 Comment(10)
Added, but still adds Rack::Lock to middlewareUraemia
@geekazoid, Rails 4.1 I assume? I tested it on Rails 4.0 only. I will see how to do it in 4.1.Pinion
Is it solved adding config.allow_concurrency=true?Uraemia
Rails 4.2 is concurrent in RAILS_ENV=production out of the box.Pinion
Hi @Nowaker, what is required for 4.1? Does it also work out of the box or should we use the workaround above?Perla
@Matt, I don't have any Rails 4.1 app by hand. Can you please check yourself and let me know?Pinion
Or simply add config.middleware.delete 'Rack::Lock' in your application.rbSolley
Nice, the initializer resolves the issue in Rails 4.2, I wrapped it in "unless Rails.env.production?" condition to execute only in test and development. Other suggestions like 'config.middleware.delete ...' or 'config.allow_concurrency...' DID NOT helpHessenassau
Ok but Rack::Lock was also included as a middleware in Rails 3. But Rails 3's WEBrick was able to handle multiple requests at one time. See what's in Rails 3: Rails.application.config.middleware => #<ActionDispatch::MiddlewareStack:0x007fb759b1b238 @middlewares= [ActionDispatch::Static, Rack::Lock,...Hessenassau
just adding the initializer on its own saved me, thanks!Wasting
S
4

Are you using a WEBrick server? That must be because your server is a single threaded server and is capable of fulfilling one request at a time (because of the single worker thread). Now in case of multiple requests, it runs the action part of the request and before running the view renderer it checks to see if there are any pending requests. Now if 10 requests are lined up, it would first complete all of them before actually rendering the views. When all of these requests are completed, the views would now be rendered sequentially.

You can switch to Passenger or Unicorn server if you want multi-threaded environment.

Hope that makes sense.

Stebbins answered 25/12, 2012 at 5:8 Comment(4)
thanks, you make me less confused. I test with more requests at the same time, it seems that after less than 10 requests are lined up, webrick will render the views and then resolve other requests.Befuddle
I think webrick is not a single threaded server, you can set 'config.threadsafe!' in production.rb, then it will resolve multi-requests at the same time.Befuddle
config.threadsafe! won't automatically make WEBrick multi-threaded. See my answer for details.Pinion
This answer isn't entirely correct. Instead of doing the check before just rendering the view, Rails puts a lock around the whole request which includes processing the request and generating the response, thanks to Rack::Lock. Also Passenger or Unicorn won't spawn threads. They spawn workers which run as separate processes.Coven
V
0

under your env setup config/environments/development.rb (or in config/application.rb)

add this line :

  #Enable threaded mode
  config.threadsafe!
Vtarj answered 3/8, 2016 at 18:41 Comment(1)
This option is no longer needed / supported (at least since Rails 4). For details see tenderlovemaking.com/2012/06/18/removing-config-threadsafe.htmlBarrington

© 2022 - 2024 — McMap. All rights reserved.