Martin Schurig
Application Engineer based in Barcelona, Spain.

Lately, I was looking for a way to show the user the output of an active job in rails.

The keyword for that is streaming.

You may want to read the keep in mind section before starting.

Setup the Webserver

As you might know, the default web server used for the development environment is WEBrick. Unfortunately, WEBrick doesn’t support streaming. That’s why we need some other Webserver that supports it. In my case I use unicorn as I also use it for my production environment.

# Gemfile
gem 'unicorn'

But our Webserver is not ready yet. We will need to tell unicorn to activate its streaming support.

For that I have created an unicorn config file and put in a line of code.

# config/unicorn/development.rb
listen 3000, tcp_nopush: false

If you also want to use it for production you have to add listen 3000, tcp_nopush: false into your unicorn production file. In my case it is sitting right here config/unicorn/production.rb.

And that’s how you start your Webserver:

$ unicorn_rails --config-file config/unicorn/development.rb

Controller setup

That’s what my controller looks like:

# app/controllers/stuffs_controller.rb
class StuffsController < ApplicationController
  include ActionController::Live

  def create
    response.headers['Content-Type'] = 'text/event-stream'
    StuffJob.perform_now(response)
  ensure
    response.stream.close
  end
end

Notice that we pass the response into the job. We need to do that so the job can communicate with the current stream.

Job

Now you can call response.stream.write 'Hello World' in your Job and it will appear in your browser.

# app/jobs/stuff_job.rb
class StuffJob < ActiveJob::Base
  queue_as :default

  def perform(response)
    10.times {
      logger.info 'Hello World'
      response.stream.write "Hello World\n"
      sleep 1
    }
  end
end

Keep in mind

  • Keep in mind that the job will stop working as soon you close the browser.

  • Also keep in mind that there is a request timeout. If you find yourself wondering why the stream keeps ending after a certain time it probably is the cause for it. This happens when your job takes longer than the time set as timeout.

To change the timeout setting you add timeout 300 into the unicorn config file. This will set the timeout to 300 seconds.

  • Furthermore keep in mind that all other requests could be blocked by your streaming request. This happens if you only use one unicorn worker.

You can change the worker count with adding a new configuration line worker_processes 3 into your unicorn config.


Closing words and question

Did you notice that I use the perform_now method when calling the job? This leads to the issue (mentioned above) that you can’t close your browser. Do you have a idea how to resolve this? When I use perform_later I can’t access the response any longer and end up with a RuntimeError can't modify frozen Hash error.

- Something to say? Feel free to tweet me @martinschurig!
Click here to read my other articles.