Redis + ActionController::Live threads not dying Redis + ActionController::Live threads not dying multithreading multithreading

Redis + ActionController::Live threads not dying


A solution I just did (borrowing a lot from @teeg) which seems to work okay (haven't failure tested it, tho)

config/initializers/redis.rb

$redis = Redis.new(:host => "xxxx.com", :port => 6379)heartbeat_thread = Thread.new do  while true    $redis.publish("heartbeat","thump")    sleep 30.seconds  endendat_exit do  # not sure this is needed, but just in case  heartbeat_thread.kill  $redis.quitend

And then in my controller:

def events    response.headers["Content-Type"] = "text/event-stream"    redis = Redis.new(:host => "xxxxxxx.com", :port => 6379)    logger.info "New stream starting, connecting to redis"    redis.subscribe(['parse.new','heartbeat']) do |on|      on.message do |event, data|        if event == 'parse.new'          response.stream.write("event: parse\ndata: #{data}\n\n")        elsif event == 'heartbeat'          response.stream.write("event: heartbeat\ndata: heartbeat\n\n")        end      end    end  rescue IOError    logger.info "Stream closed"  ensure    logger.info "Stopping stream thread"    redis.quit    response.stream.close  end


I'm currently making an app that revolves around ActionController:Live, EventSource and Puma and for those that are encountering problems closing streams and such, instead of rescuing an IOError, in Rails 4.2 you need to rescue ClientDisconnected. Example:

def stream  #Begin is not required  twitter_client = Twitter::Streaming::Client.new(config_params) do |obj|    # Do something  endrescue ClientDisconnected  # Do something when disconnectedensure  # Do something else to ensure the stream is closedend

I found this handy tip from this forum post (all the way at the bottom): http://railscasts.com/episodes/401-actioncontroller-live?view=comments


Building on @James Boutcher, I used the following in clustered Puma with 2 workers, so that I have only 1 thread created for the heartbeat in config/initializers/redis.rb:

config/puma.rb

on_worker_boot do |index|  puts "worker nb #{index.to_s} booting"  create_heartbeat if index.to_i==0enddef create_heartbeat  puts "creating heartbeat"  $redis||=Redis.new  heartbeat = Thread.new do    ActiveRecord::Base.connection_pool.release_connection    begin      while true        hash={event: "heartbeat",data: "heartbeat"}        $redis.publish("heartbeat",hash.to_json)        sleep 20.seconds      end    ensure      #no db connection anyway    end  endend