Help understanding the request dispatch workflow for live streaming

Hello, I’m trying to understand why we’re experiencing some issues
regarding Live streaming in our servers (it works in our development
machines).

So, I'm taking a look at ActionPack source-code to understand how

dispatching works…

It seems, the entry point is the #dispatch method in

ActionController::Metal.

Then it calls #process(name) and finally #to_a, which seems to

return the Rack response.

First, I couldn't understand how "response" is defined. Will it

always be nil unless I’m using a responds_to block? Who fills in the
@responses hash in MimeType?

Since we include ActionController::Live and we're using HTTP 1.1 for

the nginx proxy, we expect this #process to take place:

    def process(name)

      # ...

      # This processes the action in a child thread. It lets us

return the

      # response code and headers back up the rack stack, and still

process

      # the body in parallel with sending data to the client

      Thread.new {

        #...

        begin

          super(name)

        rescue => e

          # ...

        ensure

          @_response.commit!

        end

      }

      @_response.await_commit

    end

It seems the response is only committed on response.stream.close

(Live::Buffer), which calls @response.commit!

So, I don't quite follow the comment in the code. It says it starts

a new thread so that it returns the response body to the rack stack,
but to me it seems that it only happens in #to_a, but #to_a would
only be called after #process returns, right? But to me it seems
like @_response.await_commit will block until close is called in the
buffer, right?

Could someone please explain me how Rails interacts with Rack. In

other words, how does Rails send Rack the expected [status, headers,
body] array?

http://rack.rubyforge.org/doc/SPEC.html

"
A Rack application is a Ruby object (not a class) that responds to

call. It takes exactly one argument, the environment and returns an
Array of exactly three values: The status, the headers, and the
body."

this is the implementation of to_a:

    def to_a #:nodoc:

      response ? response.to_a : [status, headers, response_body]

    end

How do I know if response is set? And in the case it's set, what

class would it be? How is this related to ActionDispatch::Response
or its inherited ActionController::Live::Response?

Any help is welcomed.

Thanks in advance,

Rodrigo.

Ok, forget about this. I just realized that I shouldn’t be looking
for a “def response”. It’s defined as attr_internal, which seems to
use the @_response object. Now it gets easier for me to find out the
real response, but I’m still curious about the comment in the thread
below…

Nevermind, I’m back to investigation now and I noticed that Live::Response#write calls super, which calls commit!.

Ok, I’m done with regards to this investigation. In case someone is curious, it seems the problem only happen with unicorn set to preload the application and with the following Rails config options enabled:

config.cache_classes = true

config.eager_load = true

Any of those options enabled was enough for the streaming to fail. Unicorn was configured with tcp_nopush: false by the way, in case you were curious.

I still don’t know exactly why the application wasn’t working with streaming (we set the http version to 1.1 in nginx) but I won’t continue this investigation because even if we could get unicorn to work with streaming it wouldn’t be a good thing to do in our case, since our streaming actions can be really slow and we don’t want to take the risk of the “export to excel” feature to use all available Unicorn workers, so we’ll redirect those actions to another multi-thread server or we’ll deploy the full application with Puma, maybe.

But maybe this could help others that might face the same problem concerning live streaming and unicorn/nginx.

Regards,

Rodrigo.