I'm trying to serve multiple requests concurrently in Rails 4, something I was able to do very easily with config.threadsafe!
and Puma
in Rails 3.
Say I have this controller
class ConcurrentController < ApplicationController def index sleep 10000 end def show end end
I used to be able to just start puma with puma -t 2:16 -p 3000
(for min 2 threads) and hit index
and then show
and still have show
render properly.
In Rails 4, if I attempt to do the same thing Puma now locks on the index
request and show
never gets rendered. When I hit Ctrl-C
for the server Puma gives me this error:
Rack app error: #<ThreadError: Attempt to unlock a mutex which is locked by another thread>
What am I missing here to get concurrency to work with Rails 4? config.threadsafe!
is supposed to not be needed (and doesn't make a difference even if I try)
Rails automatically allows various operations to be performed at the same time. When using a threaded web server, such as the default Puma, multiple HTTP requests will be served simultaneously, with each request provided its own controller instance.
The number of concurrent requests refers to the number of requests that the system can process simultaneously. When it comes to a website, concurrent requests refer to the requests from the visitors at the same time.
600 requests/second. 180 application instances (mongrel)
Rails as a framework is thread-safe. So, the answer is yes!
I invite you to read about the configuration options of config.threadsafe!
in this article Removing config.threadsafe! It will help you to understand better the options of config.threadsafe!
, in particular to allow concurrency.
In Rails 4 config.threadsafe!
is set by default.
In Rails 4 requests are wrapped around a Mutex by the Rack::Lock middleware in DEV environments by default.
If you want to enable concurrency you can set config.allow_concurrency=true
. This will disable the Rack::Lock middleware. I would not delete it as mentioned in another answer to your question; that looks like a hack to me.
Note: If you have
config.cache_classes=true
then an assignment toconfig.allow_concurrency
(Rack::Lock request mutex) won't take effect, concurrent requests are allowed by default. If you haveconfig.cache_classes=false
, then you can setconfig.allow_concurrency
to eithertrue
orfalse
. In DEV environment you would want to have it like thisconfig.cache_classes=false config.allow_concurrency=true
The statement: Which means that if config.cache_classes = false (which it is by default in dev env) we can't have concurrent requests. is not correct.
You can refer to this answer, which sets up an experiment testing concurrency using MRI and JRuby. The results are surprising. MRI was faster than JRuby.
The experiment with MRI concurrency is on GitHub. The experiment only tests concurrent request. There are no race conditions in the controller. However, I think it is not too difficult to implement example from the article above to test race conditions in a controller.
It seems that by default, in Rails 4, concurrent requests are not enabled in the Development environment.
I found this quote in the documentation.
Rack::Lock wraps the app in mutex so it can only be called by a single thread at a time. Only enabled when config.cache_classes is false.
Which means that if config.cache_classes = false
(which it is by default in dev env) we can't have concurrent requests.
Concurrent requests do work with my example in production environment.
One solution is to set config.cache_classes = true
in the development environment, but then the code doesn't reload on a change, which doesn't really work for development.
The second kind of hacky solution is to disable the Rack::Lock
middleware in development.
So if you were to add in development.rb
the following line:
config.middleware.delete Rack::Lock
you should be able to have concurrent requests in development environment with cache classes disabled.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With