I am trying to understand how each web request works in rails application. To understand my logic I have written this dummy controller.
class TestsController < ApplicationController
def index
puts 'Going to Sleep'
sleep 30.seconds
render json: { message: 'ok' }
end
end
I use puma as my application server. I ran rails server in production mode and visited locahost:3000/tests
in 5 different tabs in browser. My understanding was since puma is concurrent server, each request will run under different thread, that's why request blocked by first request won't block request for second request because that request will be handled by seperate thread.
But when I look into server log in terminal I see message Going to Sleep
appear on first request and then ruby goes to sleep for 30 second. For second request and other requests I don't see message Going to Sleep
in log file for 30 seconds (until first request have finished sleeping). This is confusing me a-lot, I thought for each 5 requests on different tabs on browser I will see message Going to Sleep
immediately and non of request will block each other. But looks like I am wrong.
Can some one please explain it to me how it works ? My first request is blocking other request. Isn't that problem? In real world application if I make api call to 3rd party application and if it takes longer to get response back than it will block request for other users ? Or do I need to make http request from different ip addresses for concurrency to work? Please explain.
UPDATA
Below is my puma configuration
max_threads_count = ENV.fetch("RAILS_MAX_THREADS") { 5 }
min_threads_count = ENV.fetch("RAILS_MIN_THREADS") { max_threads_count }
threads min_threads_count, max_threads_count
CodePudding user response:
The answer to this condurum is that Rails is not blocking, but your browser is.
Browsers often reuse existing socket connections in order to reduce network traffic. In this case opening a new tab to exactly the same URL will try to reuse the existing socket connection to the server.
The browser is essentially trying to pipeline the request to the server through the already open connection. Since this socket connection is busy waiting for the index
of your Tests
controller, the browser will block.
To see that Rails will indeed serve the same action from the same controller concurrently, you have to open new requests from another browser, or you can also test it from the command line with a tool like wget
:
> wget -O - http://locahost:3000/tests &
> wget -O - http://locahost:3000/tests &
You will see both requests will go through fine, since they each make a new unique connection to the server.
Conclusion:
Rails is working fine. Concurrency and threading is working as expected. It's just that your browser is blocked because it's trying to reuse the already opened socket.