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