2021-06-01 12:20:20 +00:00
|
|
|
# Copyright (C) 2012-2021 Zammad Foundation, http://zammad-foundation.org/
|
|
|
|
|
2013-08-21 18:35:22 +00:00
|
|
|
module Sessions
|
2013-01-24 01:01:47 +00:00
|
|
|
|
2021-06-30 08:24:58 +00:00
|
|
|
@store = case Rails.application.config.websocket_session_store
|
|
|
|
when :redis then Sessions::Store::Redis.new
|
|
|
|
else Sessions::Store::File.new
|
|
|
|
end
|
2013-01-24 01:01:47 +00:00
|
|
|
|
|
|
|
# create global vars for threads
|
2015-05-08 13:47:27 +00:00
|
|
|
@@client_threads = {} # rubocop:disable Style/ClassVars
|
2012-07-23 22:22:23 +00:00
|
|
|
|
2014-06-27 06:43:37 +00:00
|
|
|
=begin
|
|
|
|
|
|
|
|
start new session
|
|
|
|
|
2016-05-16 17:49:06 +00:00
|
|
|
Sessions.create(client_id, session_data, { type: 'websocket' })
|
2014-06-27 06:43:37 +00:00
|
|
|
|
|
|
|
returns
|
|
|
|
|
|
|
|
true|false
|
|
|
|
|
|
|
|
=end
|
|
|
|
|
2015-12-08 13:22:13 +00:00
|
|
|
def self.create(client_id, session, meta)
|
2015-05-03 09:22:48 +00:00
|
|
|
# collect session data
|
2015-05-10 20:53:15 +00:00
|
|
|
meta[:last_ping] = Time.now.utc.to_i
|
2015-05-03 08:40:10 +00:00
|
|
|
data = {
|
|
|
|
user: session,
|
|
|
|
meta: meta,
|
|
|
|
}
|
2015-05-03 09:22:48 +00:00
|
|
|
content = data.to_json
|
|
|
|
|
2021-06-30 08:24:58 +00:00
|
|
|
@store.create(client_id, content)
|
2015-05-03 09:22:48 +00:00
|
|
|
|
2012-11-02 16:10:22 +00:00
|
|
|
# send update to browser
|
2017-11-23 08:09:44 +00:00
|
|
|
return if !session || session['id'].blank?
|
2018-10-09 06:17:41 +00:00
|
|
|
|
2017-11-23 08:09:44 +00:00
|
|
|
send(
|
|
|
|
client_id,
|
|
|
|
{
|
|
|
|
event: 'ws:login',
|
2018-12-19 17:31:51 +00:00
|
|
|
data: { success: true },
|
2017-11-23 08:09:44 +00:00
|
|
|
}
|
|
|
|
)
|
2012-07-23 22:22:23 +00:00
|
|
|
end
|
|
|
|
|
2014-06-27 06:43:37 +00:00
|
|
|
=begin
|
|
|
|
|
|
|
|
list of all session
|
|
|
|
|
|
|
|
client_ids = Sessions.sessions
|
|
|
|
|
|
|
|
returns
|
|
|
|
|
|
|
|
['4711', '4712']
|
|
|
|
|
|
|
|
=end
|
|
|
|
|
|
|
|
def self.sessions
|
2021-06-30 08:24:58 +00:00
|
|
|
@store.sessions
|
2014-06-27 06:43:37 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
=begin
|
|
|
|
|
|
|
|
list of all session
|
|
|
|
|
|
|
|
Sessions.session_exists?(client_id)
|
|
|
|
|
|
|
|
returns
|
|
|
|
|
|
|
|
true|false
|
|
|
|
|
|
|
|
=end
|
|
|
|
|
|
|
|
def self.session_exists?(client_id)
|
2021-06-30 08:24:58 +00:00
|
|
|
@store.session_exists?(client_id)
|
2014-06-27 06:43:37 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
=begin
|
|
|
|
|
|
|
|
list of all session with data
|
|
|
|
|
|
|
|
client_ids_with_data = Sessions.list
|
|
|
|
|
|
|
|
returns
|
|
|
|
|
|
|
|
{
|
|
|
|
'4711' => {
|
2015-12-08 13:22:13 +00:00
|
|
|
user: {
|
2014-10-05 12:38:30 +00:00
|
|
|
'id' => 123,
|
2014-06-27 06:43:37 +00:00
|
|
|
},
|
2015-12-08 13:22:13 +00:00
|
|
|
meta: {
|
|
|
|
type: 'websocket',
|
|
|
|
last_ping: time_of_last_ping,
|
2014-06-27 06:43:37 +00:00
|
|
|
}
|
|
|
|
},
|
|
|
|
'4712' => {
|
2015-12-08 13:22:13 +00:00
|
|
|
user: {
|
2014-10-05 12:38:30 +00:00
|
|
|
'id' => 124,
|
2014-06-27 06:43:37 +00:00
|
|
|
},
|
2015-12-08 13:22:13 +00:00
|
|
|
meta: {
|
|
|
|
type: 'ajax',
|
|
|
|
last_ping: time_of_last_ping,
|
2014-06-27 06:43:37 +00:00
|
|
|
}
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
=end
|
|
|
|
|
|
|
|
def self.list
|
2015-05-07 12:10:38 +00:00
|
|
|
client_ids = sessions
|
2014-06-27 06:43:37 +00:00
|
|
|
session_list = {}
|
2017-10-01 12:25:52 +00:00
|
|
|
client_ids.each do |client_id|
|
2015-05-07 12:10:38 +00:00
|
|
|
data = get(client_id)
|
2014-06-27 06:43:37 +00:00
|
|
|
next if !data
|
2018-10-09 06:17:41 +00:00
|
|
|
|
2014-06-27 06:43:37 +00:00
|
|
|
session_list[client_id] = data
|
2017-10-01 12:25:52 +00:00
|
|
|
end
|
2014-06-27 06:43:37 +00:00
|
|
|
session_list
|
|
|
|
end
|
|
|
|
|
|
|
|
=begin
|
|
|
|
|
|
|
|
destroy session
|
|
|
|
|
2016-11-30 10:30:03 +00:00
|
|
|
Sessions.destroy(client_id)
|
2014-06-27 06:43:37 +00:00
|
|
|
|
|
|
|
returns
|
|
|
|
|
|
|
|
true|false
|
|
|
|
|
|
|
|
=end
|
|
|
|
|
2016-11-30 10:30:03 +00:00
|
|
|
def self.destroy(client_id)
|
2021-06-30 08:24:58 +00:00
|
|
|
@store.destroy(client_id)
|
2014-06-27 06:43:37 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
=begin
|
|
|
|
|
|
|
|
destroy idle session
|
|
|
|
|
2016-11-30 10:30:03 +00:00
|
|
|
list_of_client_ids = Sessions.destroy_idle_sessions
|
2014-06-27 06:43:37 +00:00
|
|
|
|
|
|
|
returns
|
|
|
|
|
|
|
|
['4711', '4712']
|
|
|
|
|
|
|
|
=end
|
|
|
|
|
2016-11-30 10:30:03 +00:00
|
|
|
def self.destroy_idle_sessions(idle_time_in_sec = 240)
|
2014-06-27 06:43:37 +00:00
|
|
|
list_of_closed_sessions = []
|
2015-05-03 09:22:48 +00:00
|
|
|
clients = Sessions.list
|
2017-10-01 12:25:52 +00:00
|
|
|
clients.each do |client_id, client|
|
2021-07-16 13:38:01 +00:00
|
|
|
if !client[:meta] || !client[:meta][:last_ping] || (client[:meta][:last_ping].to_i + idle_time_in_sec) < Time.now.utc.to_i
|
2014-06-27 06:43:37 +00:00
|
|
|
list_of_closed_sessions.push client_id
|
2016-11-30 10:30:03 +00:00
|
|
|
Sessions.destroy(client_id)
|
2014-06-27 06:43:37 +00:00
|
|
|
end
|
2017-10-01 12:25:52 +00:00
|
|
|
end
|
2014-06-27 06:43:37 +00:00
|
|
|
list_of_closed_sessions
|
|
|
|
end
|
|
|
|
|
|
|
|
=begin
|
|
|
|
|
|
|
|
touch session
|
|
|
|
|
|
|
|
Sessions.touch(client_id)
|
|
|
|
|
|
|
|
returns
|
|
|
|
|
|
|
|
true|false
|
|
|
|
|
|
|
|
=end
|
|
|
|
|
2015-12-08 13:22:13 +00:00
|
|
|
def self.touch(client_id)
|
2015-05-07 12:10:38 +00:00
|
|
|
data = get(client_id)
|
2014-06-27 06:43:37 +00:00
|
|
|
return false if !data
|
2018-10-09 06:17:41 +00:00
|
|
|
|
2015-05-10 20:53:15 +00:00
|
|
|
data[:meta][:last_ping] = Time.now.utc.to_i
|
2021-06-30 08:24:58 +00:00
|
|
|
@store.set(client_id, data)
|
2014-06-27 06:43:37 +00:00
|
|
|
true
|
|
|
|
end
|
|
|
|
|
|
|
|
=begin
|
|
|
|
|
|
|
|
get session data
|
|
|
|
|
|
|
|
data = Sessions.get(client_id)
|
|
|
|
|
|
|
|
returns
|
|
|
|
|
|
|
|
{
|
2015-12-08 13:22:13 +00:00
|
|
|
user: {
|
2014-10-05 12:38:30 +00:00
|
|
|
'id' => 123,
|
2014-06-27 06:43:37 +00:00
|
|
|
},
|
2015-12-08 13:22:13 +00:00
|
|
|
meta: {
|
|
|
|
type: 'websocket',
|
|
|
|
last_ping: time_of_last_ping,
|
2014-06-27 06:43:37 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
=end
|
|
|
|
|
2015-12-08 13:22:13 +00:00
|
|
|
def self.get(client_id)
|
2021-06-30 08:24:58 +00:00
|
|
|
@store.get client_id
|
2014-06-27 06:43:37 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
=begin
|
|
|
|
|
|
|
|
send message to client
|
|
|
|
|
|
|
|
Sessions.send(client_id_of_recipient, data)
|
|
|
|
|
|
|
|
returns
|
|
|
|
|
|
|
|
true|false
|
|
|
|
|
|
|
|
=end
|
|
|
|
|
2015-12-08 13:22:13 +00:00
|
|
|
def self.send(client_id, data)
|
2021-06-30 08:24:58 +00:00
|
|
|
@store.send_data(client_id, data)
|
2014-06-27 06:43:37 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
=begin
|
|
|
|
|
2014-08-25 15:44:57 +00:00
|
|
|
send message to recipient client
|
|
|
|
|
|
|
|
Sessions.send_to(user_id, data)
|
|
|
|
|
2018-11-02 17:42:57 +00:00
|
|
|
e. g.
|
|
|
|
|
|
|
|
Sessions.send_to(user_id, {
|
|
|
|
event: 'session:takeover',
|
|
|
|
data: {
|
|
|
|
taskbar_id: 12312
|
|
|
|
},
|
|
|
|
})
|
|
|
|
|
2014-08-25 15:44:57 +00:00
|
|
|
returns
|
|
|
|
|
|
|
|
true|false
|
|
|
|
|
|
|
|
=end
|
|
|
|
|
2015-12-08 13:22:13 +00:00
|
|
|
def self.send_to(user_id, data)
|
2014-08-25 15:44:57 +00:00
|
|
|
|
|
|
|
# list all current clients
|
2015-05-07 12:10:38 +00:00
|
|
|
client_list = sessions
|
2017-10-01 12:25:52 +00:00
|
|
|
client_list.each do |client_id|
|
2014-08-25 15:44:57 +00:00
|
|
|
session = Sessions.get(client_id)
|
|
|
|
next if !session
|
|
|
|
next if !session[:user]
|
2014-10-05 12:38:30 +00:00
|
|
|
next if !session[:user]['id']
|
|
|
|
next if session[:user]['id'].to_i != user_id.to_i
|
2018-10-09 06:17:41 +00:00
|
|
|
|
2015-12-08 13:22:13 +00:00
|
|
|
Sessions.send(client_id, data)
|
2017-10-01 12:25:52 +00:00
|
|
|
end
|
2014-08-25 15:44:57 +00:00
|
|
|
true
|
|
|
|
end
|
|
|
|
|
|
|
|
=begin
|
|
|
|
|
2015-12-08 13:22:13 +00:00
|
|
|
send message to all authenticated client
|
2014-06-27 06:43:37 +00:00
|
|
|
|
|
|
|
Sessions.broadcast(data)
|
|
|
|
|
|
|
|
returns
|
|
|
|
|
2016-05-26 08:14:51 +00:00
|
|
|
[array_with_client_ids_of_recipients]
|
2014-06-27 06:43:37 +00:00
|
|
|
|
2016-05-25 07:19:45 +00:00
|
|
|
broadcase also to not authenticated client
|
|
|
|
|
2016-05-26 21:40:10 +00:00
|
|
|
Sessions.broadcast(data, 'public') # public|authenticated
|
2016-05-25 07:19:45 +00:00
|
|
|
|
|
|
|
broadcase also not to sender
|
|
|
|
|
|
|
|
Sessions.broadcast(data, 'public', sender_user_id)
|
|
|
|
|
2014-06-27 06:43:37 +00:00
|
|
|
=end
|
|
|
|
|
2016-05-26 21:40:10 +00:00
|
|
|
def self.broadcast(data, recipient = 'authenticated', sender_user_id = nil)
|
2014-06-27 06:43:37 +00:00
|
|
|
|
|
|
|
# list all current clients
|
2016-05-26 08:14:51 +00:00
|
|
|
recipients = []
|
2015-05-07 12:10:38 +00:00
|
|
|
client_list = sessions
|
2017-10-01 12:25:52 +00:00
|
|
|
client_list.each do |client_id|
|
2015-12-08 13:22:13 +00:00
|
|
|
session = Sessions.get(client_id)
|
|
|
|
next if !session
|
2016-05-25 07:19:45 +00:00
|
|
|
|
|
|
|
if recipient != 'public'
|
2017-07-26 18:46:31 +00:00
|
|
|
next if session[:user].blank?
|
|
|
|
next if session[:user]['id'].blank?
|
2016-05-25 07:19:45 +00:00
|
|
|
end
|
|
|
|
|
2020-09-30 09:07:01 +00:00
|
|
|
next if sender_user_id && session[:user] && session[:user]['id'] && session[:user]['id'].to_i == sender_user_id.to_i
|
|
|
|
|
2015-12-08 13:22:13 +00:00
|
|
|
Sessions.send(client_id, data)
|
2016-05-26 08:14:51 +00:00
|
|
|
recipients.push client_id
|
2017-10-01 12:25:52 +00:00
|
|
|
end
|
2016-05-26 08:14:51 +00:00
|
|
|
recipients
|
2014-06-27 06:43:37 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
=begin
|
|
|
|
|
|
|
|
get messages for client
|
|
|
|
|
|
|
|
messages = Sessions.queue(client_id_of_recipient)
|
|
|
|
|
|
|
|
returns
|
|
|
|
|
|
|
|
[
|
|
|
|
{
|
|
|
|
key1 => 'some data of message 1',
|
|
|
|
key2 => 'some data of message 1',
|
|
|
|
},
|
|
|
|
{
|
|
|
|
key1 => 'some data of message 2',
|
|
|
|
key2 => 'some data of message 2',
|
|
|
|
},
|
|
|
|
]
|
|
|
|
|
|
|
|
=end
|
|
|
|
|
2015-12-08 13:22:13 +00:00
|
|
|
def self.queue(client_id)
|
2021-06-30 08:24:58 +00:00
|
|
|
@store.queue(client_id)
|
2014-06-27 06:43:37 +00:00
|
|
|
end
|
|
|
|
|
2017-05-02 17:16:46 +00:00
|
|
|
=begin
|
|
|
|
|
|
|
|
remove all session and spool messages
|
|
|
|
|
|
|
|
Sessions.cleanup
|
|
|
|
|
|
|
|
=end
|
|
|
|
|
2015-05-03 09:22:48 +00:00
|
|
|
def self.cleanup
|
2021-06-30 08:24:58 +00:00
|
|
|
@store.cleanup
|
2014-06-29 19:30:55 +00:00
|
|
|
end
|
|
|
|
|
2018-11-02 17:42:57 +00:00
|
|
|
=begin
|
|
|
|
|
|
|
|
create spool messages
|
|
|
|
|
|
|
|
Sessions.spool_create(some: 'data')
|
|
|
|
|
|
|
|
=end
|
|
|
|
|
2015-12-09 13:09:37 +00:00
|
|
|
def self.spool_create(data)
|
|
|
|
msg = JSON.generate(data)
|
2017-06-23 14:39:40 +00:00
|
|
|
data = {
|
2018-12-19 17:31:51 +00:00
|
|
|
msg: msg,
|
2017-06-23 14:39:40 +00:00
|
|
|
timestamp: Time.now.utc.to_i,
|
|
|
|
}
|
2021-06-30 08:24:58 +00:00
|
|
|
@store.add_to_spool(data)
|
2013-06-10 07:01:37 +00:00
|
|
|
end
|
|
|
|
|
2018-11-02 17:42:57 +00:00
|
|
|
=begin
|
|
|
|
|
|
|
|
get spool messages
|
|
|
|
|
|
|
|
Sessions.spool_list(junger_then, for_user_id)
|
|
|
|
|
|
|
|
=end
|
|
|
|
|
2015-12-08 13:22:13 +00:00
|
|
|
def self.spool_list(timestamp, current_user_id)
|
2015-05-03 09:22:48 +00:00
|
|
|
data = []
|
2013-06-10 07:01:37 +00:00
|
|
|
to_delete = []
|
2021-07-16 09:16:04 +00:00
|
|
|
@store.each_spool do |message, entry|
|
2021-06-30 08:24:58 +00:00
|
|
|
message_parsed = {}
|
|
|
|
begin
|
|
|
|
spool = JSON.parse(message)
|
|
|
|
message_parsed = JSON.parse(spool['msg'])
|
|
|
|
rescue => e
|
|
|
|
log('error', "can't parse spool message: #{message}, #{e.inspect}")
|
2021-07-16 09:16:04 +00:00
|
|
|
to_delete.push [message, entry]
|
2021-06-30 08:24:58 +00:00
|
|
|
next
|
|
|
|
end
|
2013-06-10 07:01:37 +00:00
|
|
|
|
2021-06-30 08:24:58 +00:00
|
|
|
# ignore message older then 48h
|
|
|
|
if spool['timestamp'] + (2 * 86_400) < Time.now.utc.to_i
|
2021-07-16 09:16:04 +00:00
|
|
|
to_delete.push [message, entry]
|
2021-06-30 08:24:58 +00:00
|
|
|
next
|
|
|
|
end
|
2013-06-10 07:01:37 +00:00
|
|
|
|
2021-06-30 08:24:58 +00:00
|
|
|
# add spool attribute to push spool info to clients
|
|
|
|
message_parsed['spool'] = true
|
2015-05-07 09:04:40 +00:00
|
|
|
|
2021-06-30 08:24:58 +00:00
|
|
|
# only send not already older messages
|
|
|
|
if !timestamp || timestamp < spool['timestamp']
|
2015-05-07 09:04:40 +00:00
|
|
|
|
2021-06-30 08:24:58 +00:00
|
|
|
# spool to recipient list
|
|
|
|
if message_parsed['recipient'] && message_parsed['recipient']['user_id']
|
2015-05-07 09:04:40 +00:00
|
|
|
|
2021-06-30 08:24:58 +00:00
|
|
|
message_parsed['recipient']['user_id'].each do |user_id|
|
2015-12-09 13:09:37 +00:00
|
|
|
|
2021-06-30 08:24:58 +00:00
|
|
|
next if current_user_id != user_id
|
2013-06-10 07:01:37 +00:00
|
|
|
|
2015-12-09 13:09:37 +00:00
|
|
|
message = message_parsed
|
|
|
|
if message_parsed['event'] == 'broadcast'
|
|
|
|
message = message_parsed['data']
|
|
|
|
end
|
2021-06-30 08:24:58 +00:00
|
|
|
|
2013-06-10 07:01:37 +00:00
|
|
|
item = {
|
2021-06-30 08:24:58 +00:00
|
|
|
type: 'direct',
|
2015-12-09 13:09:37 +00:00
|
|
|
message: message,
|
2013-06-10 07:01:37 +00:00
|
|
|
}
|
|
|
|
data.push item
|
|
|
|
end
|
2021-06-30 08:24:58 +00:00
|
|
|
|
|
|
|
# spool to every client
|
|
|
|
else
|
|
|
|
message = message_parsed
|
|
|
|
if message_parsed['event'] == 'broadcast'
|
|
|
|
message = message_parsed['data']
|
|
|
|
end
|
|
|
|
item = {
|
|
|
|
type: 'broadcast',
|
|
|
|
message: message,
|
|
|
|
}
|
|
|
|
data.push item
|
2013-06-10 07:01:37 +00:00
|
|
|
end
|
2017-10-01 12:25:52 +00:00
|
|
|
end
|
|
|
|
end
|
2021-07-16 09:16:04 +00:00
|
|
|
to_delete.each do |item|
|
|
|
|
@store.remove_from_spool(*item)
|
2017-10-01 12:25:52 +00:00
|
|
|
end
|
2015-04-30 17:20:27 +00:00
|
|
|
data
|
2013-06-10 07:01:37 +00:00
|
|
|
end
|
|
|
|
|
2018-11-02 17:42:57 +00:00
|
|
|
=begin
|
|
|
|
|
|
|
|
delete spool messages
|
|
|
|
|
|
|
|
Sessions.spool_delete
|
|
|
|
|
|
|
|
=end
|
|
|
|
|
|
|
|
def self.spool_delete
|
2021-06-30 08:24:58 +00:00
|
|
|
@store.clear_spool
|
2018-11-02 17:42:57 +00:00
|
|
|
end
|
|
|
|
|
2017-12-04 07:00:00 +00:00
|
|
|
def self.jobs(node_id = nil)
|
2013-01-15 06:41:37 +00:00
|
|
|
|
2017-12-04 07:00:00 +00:00
|
|
|
# dispatch sessions
|
2020-03-20 05:11:50 +00:00
|
|
|
if node_id.blank? && ENV['ZAMMAD_SESSION_JOBS_CONCURRENT'].to_i.positive?
|
|
|
|
|
2020-11-27 08:38:55 +00:00
|
|
|
previous_nodes_sessions = Sessions::Node.stats
|
|
|
|
if previous_nodes_sessions.present?
|
|
|
|
log('info', "Cleaning up previous Sessions::Node sessions: #{previous_nodes_sessions}")
|
|
|
|
Sessions::Node.cleanup
|
|
|
|
end
|
|
|
|
|
2020-03-20 05:11:50 +00:00
|
|
|
dispatcher_pid = Process.pid
|
|
|
|
node_count = ENV['ZAMMAD_SESSION_JOBS_CONCURRENT'].to_i
|
|
|
|
node_pids = []
|
|
|
|
(1..node_count).each do |worker_node_id|
|
|
|
|
node_pids << fork do
|
|
|
|
title = "Zammad Session Jobs Node ##{worker_node_id}: dispatch_pid:#{dispatcher_pid} -> worker_pid:#{Process.pid}"
|
|
|
|
$PROGRAM_NAME = title
|
|
|
|
|
|
|
|
log('info', "#{title} started.")
|
|
|
|
|
|
|
|
::Sessions.jobs(worker_node_id)
|
|
|
|
sleep node_count
|
2020-09-30 09:07:01 +00:00
|
|
|
rescue Interrupt
|
2020-03-20 05:11:50 +00:00
|
|
|
nil
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
Signal.trap 'SIGTERM' do
|
|
|
|
|
|
|
|
node_pids.each do |node_pid|
|
|
|
|
Process.kill 'TERM', node_pid
|
|
|
|
end
|
|
|
|
|
|
|
|
Process.waitall
|
|
|
|
|
|
|
|
raise SignalException, 'SIGTERM'
|
|
|
|
end
|
|
|
|
|
2020-11-27 08:38:55 +00:00
|
|
|
# dispatch client_ids to nodes
|
2017-12-04 07:00:00 +00:00
|
|
|
loop do
|
|
|
|
|
|
|
|
# nodes
|
|
|
|
nodes_stats = Sessions::Node.stats
|
|
|
|
|
|
|
|
client_ids = sessions
|
|
|
|
client_ids.each do |client_id|
|
|
|
|
|
|
|
|
# ask nodes for nodes
|
|
|
|
next if nodes_stats[client_id]
|
|
|
|
|
2020-11-27 08:38:55 +00:00
|
|
|
# assign to node
|
2017-12-04 07:00:00 +00:00
|
|
|
Sessions::Node.session_assigne(client_id)
|
2018-01-24 12:55:15 +00:00
|
|
|
sleep 1
|
2017-12-04 07:00:00 +00:00
|
|
|
end
|
|
|
|
sleep 1
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2012-08-03 22:46:05 +00:00
|
|
|
Thread.abort_on_exception = true
|
2015-05-05 14:10:06 +00:00
|
|
|
loop do
|
2017-12-04 07:00:00 +00:00
|
|
|
|
|
|
|
if node_id
|
|
|
|
|
|
|
|
# register node
|
|
|
|
Sessions::Node.register(node_id)
|
|
|
|
|
|
|
|
# watch for assigned sessions
|
|
|
|
client_ids = Sessions::Node.sessions_by(node_id)
|
|
|
|
else
|
|
|
|
client_ids = sessions
|
|
|
|
end
|
|
|
|
|
2017-10-01 12:25:52 +00:00
|
|
|
client_ids.each do |client_id|
|
2012-07-23 22:22:23 +00:00
|
|
|
|
2014-06-29 19:30:55 +00:00
|
|
|
# connection already open, ignore
|
2012-08-07 21:53:43 +00:00
|
|
|
next if @@client_threads[client_id]
|
|
|
|
|
2013-06-10 07:01:37 +00:00
|
|
|
# get current user
|
2015-12-08 13:22:13 +00:00
|
|
|
session_data = Sessions.get(client_id)
|
2018-01-24 12:55:15 +00:00
|
|
|
next if session_data.blank?
|
|
|
|
next if session_data[:user].blank?
|
|
|
|
next if session_data[:user]['id'].blank?
|
2018-10-09 06:17:41 +00:00
|
|
|
|
2017-11-06 01:32:47 +00:00
|
|
|
user = User.lookup(id: session_data[:user]['id'])
|
2018-01-24 12:55:15 +00:00
|
|
|
next if user.blank?
|
2012-08-03 22:46:05 +00:00
|
|
|
|
|
|
|
# start client thread
|
2018-01-24 12:55:15 +00:00
|
|
|
next if @@client_threads[client_id].present?
|
2015-05-07 09:04:40 +00:00
|
|
|
|
|
|
|
@@client_threads[client_id] = true
|
2017-10-01 12:25:52 +00:00
|
|
|
@@client_threads[client_id] = Thread.new do
|
2017-12-04 07:00:00 +00:00
|
|
|
thread_client(client_id, 0, Time.now.utc, node_id)
|
2015-05-07 09:04:40 +00:00
|
|
|
@@client_threads[client_id] = nil
|
2015-05-13 13:27:25 +00:00
|
|
|
log('debug', "close client (#{client_id}) thread")
|
2017-11-06 01:32:47 +00:00
|
|
|
if ActiveRecord::Base.connection.owner == Thread.current
|
|
|
|
ActiveRecord::Base.connection.close
|
|
|
|
end
|
2017-10-01 12:25:52 +00:00
|
|
|
end
|
2018-01-24 12:55:15 +00:00
|
|
|
sleep 1
|
2017-10-01 12:25:52 +00:00
|
|
|
end
|
2012-08-07 06:43:15 +00:00
|
|
|
|
2018-01-24 12:55:15 +00:00
|
|
|
sleep 1
|
2012-07-23 22:22:23 +00:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2014-06-29 19:30:55 +00:00
|
|
|
=begin
|
|
|
|
|
|
|
|
check if thread for client_id is running
|
|
|
|
|
|
|
|
Sessions.thread_client_exists?(client_id)
|
|
|
|
|
|
|
|
returns
|
|
|
|
|
|
|
|
thread
|
|
|
|
|
|
|
|
=end
|
|
|
|
|
|
|
|
def self.thread_client_exists?(client_id)
|
|
|
|
@@client_threads[client_id]
|
2013-12-01 11:55:21 +00:00
|
|
|
end
|
|
|
|
|
2014-06-29 19:30:55 +00:00
|
|
|
=begin
|
|
|
|
|
|
|
|
start client for browser
|
|
|
|
|
|
|
|
Sessions.thread_client(client_id)
|
|
|
|
|
|
|
|
returns
|
|
|
|
|
|
|
|
thread
|
|
|
|
|
|
|
|
=end
|
|
|
|
|
2017-12-04 07:00:00 +00:00
|
|
|
def self.thread_client(client_id, try_count = 0, try_run_time = Time.now.utc, node_id)
|
|
|
|
log('debug', "LOOP #{node_id}.#{client_id} - #{try_count}")
|
2014-06-29 19:30:55 +00:00
|
|
|
begin
|
2017-12-04 07:00:00 +00:00
|
|
|
Sessions::Client.new(client_id, node_id)
|
2014-06-29 19:30:55 +00:00
|
|
|
rescue => e
|
2015-07-03 15:18:01 +00:00
|
|
|
log('error', "thread_client #{client_id} exited with error #{e.inspect}")
|
2021-07-16 13:38:01 +00:00
|
|
|
log('error', e.backtrace.join("\n "))
|
2014-06-29 19:30:55 +00:00
|
|
|
sleep 10
|
2013-12-01 11:55:21 +00:00
|
|
|
begin
|
2014-06-29 19:30:55 +00:00
|
|
|
ActiveRecord::Base.connection_pool.release_connection
|
2013-12-01 11:55:21 +00:00
|
|
|
rescue => e
|
2015-07-03 15:18:01 +00:00
|
|
|
log('error', "Can't reconnect to database #{e.inspect}")
|
2014-06-29 19:30:55 +00:00
|
|
|
end
|
2014-04-24 05:49:30 +00:00
|
|
|
|
2014-06-29 19:30:55 +00:00
|
|
|
try_run_max = 10
|
|
|
|
try_count += 1
|
2014-04-24 05:49:30 +00:00
|
|
|
|
2014-06-29 19:30:55 +00:00
|
|
|
# reset error counter if to old
|
2021-07-16 13:38:01 +00:00
|
|
|
if try_run_time + (60 * 5) < Time.now.utc
|
2014-06-29 19:30:55 +00:00
|
|
|
try_count = 0
|
|
|
|
end
|
2015-05-10 19:47:17 +00:00
|
|
|
try_run_time = Time.now.utc
|
2014-04-24 05:49:30 +00:00
|
|
|
|
2014-06-29 19:30:55 +00:00
|
|
|
# restart job again
|
|
|
|
if try_run_max > try_count
|
2017-12-04 07:00:00 +00:00
|
|
|
thread_client(client_id, try_count, try_run_time, node_id)
|
2013-12-01 11:55:21 +00:00
|
|
|
end
|
2017-12-04 07:00:00 +00:00
|
|
|
raise "STOP thread_client for client #{node_id}.#{client_id} after #{try_run_max} tries"
|
2014-06-29 19:30:55 +00:00
|
|
|
end
|
2017-12-04 07:00:00 +00:00
|
|
|
log('debug', "/LOOP #{node_id}.#{client_id} - #{try_count}")
|
2013-12-01 11:55:21 +00:00
|
|
|
end
|
|
|
|
|
2015-01-13 14:53:15 +00:00
|
|
|
def self.symbolize_keys(hash)
|
2017-10-01 12:25:52 +00:00
|
|
|
hash.each_with_object({}) do |(key, value), result|
|
2015-01-13 14:53:15 +00:00
|
|
|
new_key = case key
|
|
|
|
when String then key.to_sym
|
|
|
|
else key
|
|
|
|
end
|
|
|
|
new_value = case value
|
|
|
|
when Hash then symbolize_keys(value)
|
|
|
|
else value
|
|
|
|
end
|
|
|
|
result[new_key] = new_value
|
2017-10-01 12:25:52 +00:00
|
|
|
end
|
2015-01-13 14:53:15 +00:00
|
|
|
end
|
|
|
|
|
2015-05-13 13:27:25 +00:00
|
|
|
# we use it in rails and non rails context
|
|
|
|
def self.log(level, message)
|
|
|
|
if defined?(Rails)
|
2020-07-13 12:46:08 +00:00
|
|
|
case level
|
|
|
|
when 'debug'
|
2018-03-20 17:47:49 +00:00
|
|
|
Rails.logger.debug { message }
|
2020-11-27 09:49:36 +00:00
|
|
|
when 'info'
|
|
|
|
Rails.logger.info message
|
2015-05-13 13:27:25 +00:00
|
|
|
else
|
|
|
|
Rails.logger.error message
|
|
|
|
end
|
|
|
|
return
|
|
|
|
end
|
|
|
|
puts "#{Time.now.utc.iso8601}:#{level} #{message}" # rubocop:disable Rails/Output
|
|
|
|
end
|
2015-04-27 14:15:29 +00:00
|
|
|
end
|