Anyone that has written a little PHP knows what the flush()
family of functions do. The ideal usage scenario for using chunked transfer[0] is when we have something costly to render e.g. the first three most recent articles on a blog. Why ? one might ask.
Is rather simple: in a normal request where the server responds with a Content-Length
header the browser will wait until the whole page comes down the wire then it goes loading the assets et al.
Using the Transfer-Encoding: chunked
header, the server will send chunks of the rendered page back to the browser so in the case of Rails, it starts with the layout and sends out the <head>
part including assets like js and css.
It's clear how this helps the rendering of the page on the client side : get the first chunk containing the <head>
with assets, immediately start loading the assets while waiting for the rest of the response. Of course, browsers nowadays include lots of micro-optimizations that might already do something similar but still this remains a good practice.
Implementation wise, you just need to add to your controller methods something like :
class YourController < ApplicationController def index @articles = Article.most_recent render stream: true end # other controller logic end
The latest version of Unicorn (4.x) comes by default[1] with support for chunked response. You can always add to your unicorn_config.rb
something like:
# :tcp_nopush This prevents partial TCP frames from being sent out # :tcp_nodelay Disables Nagle’s algorithm on TCP sockets if true. port = ENV["PORT"].to_i || 3000 # the ENV["PORT"] is a Heroku environment variable listen port, tcp_nopush: false, tcp_nodelay: true
We also have some quirks when using streaming in Rails because of the inversion of template rendering order[2]:
When streaming, rendering happens top-down instead of inside-out. Rails starts with the layout, and the template is rendered later, when its yield is reached .
tl;dr: use provide
instead of content_for
when you have multiple calls to content_for
otherwise it will break the purpose of streaming and/or it will concatenate the values from content_for
.
There's also a “small” issue with NewRelic agent and Heroku: you need to disable browser instrumentation or else you'll get a blank page[3], thankfully the fix is rather trivial:
# config/newrelic.yml browser_monitoring: # By default the agent automatically injects # the monitoring JavaScript into web pages # Turn this to false auto_instrument: false
There's also ActionController::Live
that can be used to create a simple Rails 4 chat application[4][5].