Flushing output stream(ob_flush() equivalent)

I’m trying to find a way to flush the output buffer. I have done this before in java and php but cannot find a method in ruby/rails? Is this possible?

-m

Rails doesn't do streaming for regular requests, but it does provide
an API for streaming responses.

http://api.rubyonrails.org/classes/ActionController/Streaming.html
http://datanoise.com/articles/2006/03/17/rails-and-response-streaming

Thanks for the input. I’ve tried send_data previously and it does not provide the function I need. I’ve also tried using $stdout.flush and it doesn’t seem to affect anything. Any other suggestions welcomed.

-m

Looking through the changelog I found this:

  • render_text now accepts a block for deferred rendering. Useful for
    streaming large files, displaying
    a “please wait” message during a complex search, etc. Streaming

example:

 render_text do |response|
   File.open(path, 'rb') do |file|
     while buf = file.read(1024)
       print buf
     end
   end
 end

[Jeremy Kemper]

Yet when I try this it does not work. I wonder if this has been removed in later releases?

-m

render :text => lambda { |response, out|
     File.open(path, 'rb') do |file|
       while buf = file.read(1024)
         out.print buf
       end
     end
}

Thanks Kent, but I’ve tried that and it doesn’t provide what I want. If I do :

render :text => lambda { |response, out|  
  out << "Sleeping"
  sleep(10)
  out << "Waking Up"   
}

What I’m expecting here is to see “Sleeping” then after the sleep duration, “Waking Up”. However it does not stream the response, it will process the whole block and then send the response. No different then how it normally renders.

You should use out.flush method

class TestController < ApplicationController
  def index
    render :text => lambda { |resp, out|
      out.puts 'start'
      out.flush
      10.times do
        out.puts '.'
        out.flush
        sleep 1
      end
      out.puts 'done'
    }
  end
end

Note it works only with fastcgi setup. It doesn't work with webrick.

I’ve tried of all this and I’m using lighttpd and still it does not flush the output buffer. I’ve tested this in production and development mode, I even tried mongrel just for kicks. Tested it on more then one machine with the help of a coworker here at work. I am not sure if maybe at work our configuration is somehow preventing this to function, but we are running a standard setup and I really don’t see how that can be. In the meantime I will have some of my other friends try this on their setups and see if they can get it to work. Any other suggestions would be truly appreciated.

regards,
m

If you are using a browser to test it, you won’t see the difference. Use curl for that. In my example,

$ curl http://localhost:3000/test

You will see the output printed after each flush method is being called.

I’ve been using mozilla and lynx to test and now I’ve just used curl and the same thing happens. It’s not streaming the output. I am wondering if its something within my setup thats preventing me from getting this to work. My co-worker has tried the same code on his development system and it doesn’t work for him either. I am wondering if I can replicate your rails/server setup if I could get it to work.

-m

The saga continues - I refuse to give up. I’ve determined that its not a server issue at this point, I’ve used lighttpd, apache with fcgid, mongrel and no luck still. Me and a coworker are working on a workaround for this since output streaming seems like a pipe dream at this point. In the meantime it would be nice to get the specs of a system setup that is known to work with streaming so I can implement it and see if I have better results. In the meanwhile I have a $20 prize over at Guruza http://guruza.com/question/358/reward-20.00 to anyone who can help answer this question.

Miguel,

I'm using lighttpd with fastcgi and it streams just fine. With my
example if I use curl to query my action URL, I can see the output
after each our.flush invocation.

Oh, I missed this, sorry but you can't stream from rails in Mongrel at
the least, probably in none of them. Mostly because Rails does some
buffering and Mongrel does some buffering and Rails likes to change its
mind mid-stream.

If there's a particular action that needs streaming and you can break it
out of rails then it is possible to write Mongrel handler that streams
fairly quickly. In fact Mongrel does this when sending a file.

Another option, is if you can pack up what Rails is supposed to send in
a streaming fashion, then you can write a simple Mongrel handler that
does the heavy lifting of streaming it out to the user.

Otherwise, having Rails do the actual streaming will stop the other
clients since while Rails is streaming you'll be locked.

Go ahead and give the $20 to a charity. :slight_smile:

Rails calls output.write on the IO the dispatcher hands it; Mongrel hands Rails a StringIO. The only exception I recall is that send_file used to call output.syswrite, if available, to bypass Ruby buffered IO.

jeremy

No, send_file uses output.write method. If Rails is deployed in the
fastcgi environment, your output object is FCGI::Stream which doesn't
provide sysread/syswrite methods. All buffering in this case is
provided by fastcgi library and 'flush' method is supported.