I am having an issue with the following and that might be straightforward to someone with more knowledge or experience. Having spent the whole weekend trying to figure it out without success...extra help and insight would be very appreciated, thank you.
I have a class variable, @@clients
that keeps track of opened websocket connections. When I access that variable from within the on.message
block from the redis_sub.subscribe
block (in a new Thread) the array is empty. I made a test class variable @@test
that is incremented everytime a new websocket connection happens and log that variable, same, the log shows @@test
to be 0, its initial value.
class Shrimp
# Heroku has a 50 seconds idle connection time limit.
KEEPALIVE_TIME = 15 # in seconds
@@clients = []
@@test = 0
class << self
def load_session(client)
if !client.env["rack.session"].loaded?
client.env["rack.session"][:init] = true
end
end
def get_client(user_id)
# check if user is one of the ws clients
@@clients.each do |client|
# lazily load session
load_session(client)
# get user_id from the session
if(client.env["rack.session"]["warden.user.user.key"][0][0] == user_id)
return client
end
end
nil
end
end
def initialize(app)
@app = app
redis_uri = URI.parse(ENV["REDISCLOUD_URL"])
# work on a separte thread not to block current thread
Thread.new do
redis_sub = Redis.new(host: redis_uri.host, port: redis_uri.port, password: redis_uri.password)
redis_sub.subscribe(ENV["REDIS_CHANNEL"]) do |on| # thread blocking operation
p [:redis_suscribe]
p [:test, @@test]
on.message do |channel, msg|
data = JSON.parse(msg)
p [:redis_receive_message, data]
p [:test, @@test]
client = Shrimp.get_client(data["user_id"])
client.send(data["thumbnail_urls"].to_json) if client
end
end
end
end
def call(env)
env['rack.shrimp'] = self
if Faye::WebSocket.websocket?(env)
puts websocket_string
# Send every KEEPALIVE_TIME sec a ping for keeping the connection open.
ws = Faye::WebSocket.new(env, nil, { ping: KEEPALIVE_TIME })
ws.on :open do |event|
puts '***** WS OPEN *****'
@@clients << ws
@@test = @@test + 1
p [:test, @@test]
end
ws.on :message do |event|
puts '***** WS INCOMING MESSAGE *****'
p [:message, event.data]
p [:test, @@test]
@@clients.each { |client| client.send(event.data.to_json) }
end
ws.on :close do |event|
puts '***** WS CLOSE *****'
p [:close, ws.object_id, event.code, event.reason]
@@clients.delete(ws)
p [:test, @@test]
ws = nil
end
ws.on :error do |event|
puts '***** WS ERROR *****'
p [:close, ws.object_id, event.code, event.reason]
end
# Return async Rack response
ws.rack_response
else
@app.call(env)
end
end
end
we can see the @@test
class variable being 1
after a first connection opens, still 1
when the server gets a message from that one client, 0
when logged from within the on.message
block and 1
again when that websocket connection is closed.
I am obviously missing something but cannot figure it out despite all the research and readings.