Added option to use multiple session instances.
This commit is contained in:
parent
a08d5f1b51
commit
486802770b
4 changed files with 223 additions and 19 deletions
|
@ -956,7 +956,6 @@ class Table extends App.Controller
|
||||||
ticketListShow = []
|
ticketListShow = []
|
||||||
for ticket in tickets
|
for ticket in tickets
|
||||||
ticketListShow.push App.Ticket.find(ticket.id)
|
ticketListShow.push App.Ticket.find(ticket.id)
|
||||||
console.log('overview', overview)
|
|
||||||
@overview = App.Overview.find(overview.id)
|
@overview = App.Overview.find(overview.id)
|
||||||
@table.update(
|
@table.update(
|
||||||
overviewAttributes: @overview.view.s
|
overviewAttributes: @overview.view.s
|
||||||
|
|
|
@ -109,8 +109,11 @@ returns
|
||||||
=end
|
=end
|
||||||
|
|
||||||
def self.session_exists?(client_id)
|
def self.session_exists?(client_id)
|
||||||
client_ids = sessions
|
session_dir = "#{@path}/#{client_id}"
|
||||||
client_ids.include? client_id.to_s
|
return false if !File.exist?(session_dir)
|
||||||
|
session_file = "#{session_dir}/session"
|
||||||
|
return false if !File.exist?(session_file)
|
||||||
|
true
|
||||||
end
|
end
|
||||||
|
|
||||||
=begin
|
=begin
|
||||||
|
@ -247,14 +250,14 @@ returns
|
||||||
data = nil
|
data = nil
|
||||||
|
|
||||||
# if no session dir exists, session got destoried
|
# if no session dir exists, session got destoried
|
||||||
if !File.exist? session_dir
|
if !File.exist?(session_dir)
|
||||||
destroy(client_id)
|
destroy(client_id)
|
||||||
log('debug', "missing session directory for '#{client_id}', remove session.")
|
log('debug', "missing session directory #{session_dir} for '#{client_id}', remove session.")
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
# if only session file is missing, then it's an error behavior
|
# if only session file is missing, then it's an error behavior
|
||||||
if !File.exist? session_file
|
if !File.exist?(session_file)
|
||||||
destroy(client_id)
|
destroy(client_id)
|
||||||
log('error', "missing session file for '#{client_id}', remove session.")
|
log('error', "missing session file for '#{client_id}', remove session.")
|
||||||
return
|
return
|
||||||
|
@ -558,16 +561,47 @@ remove all session and spool messages
|
||||||
data
|
data
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.jobs
|
def self.jobs(node_id = nil)
|
||||||
|
|
||||||
# just make sure that spool path exists
|
# just make sure that spool path exists
|
||||||
if !File.exist?(@path)
|
if !File.exist?(@path)
|
||||||
FileUtils.mkpath @path
|
FileUtils.mkpath @path
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# dispatch sessions
|
||||||
|
if node_id && node_id.zero?
|
||||||
|
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]
|
||||||
|
|
||||||
|
# assigne to node
|
||||||
|
Sessions::Node.session_assigne(client_id)
|
||||||
|
end
|
||||||
|
sleep 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
Thread.abort_on_exception = true
|
Thread.abort_on_exception = true
|
||||||
loop do
|
loop do
|
||||||
client_ids = sessions
|
|
||||||
|
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
|
||||||
|
|
||||||
client_ids.each do |client_id|
|
client_ids.each do |client_id|
|
||||||
|
|
||||||
# connection already open, ignore
|
# connection already open, ignore
|
||||||
|
@ -586,7 +620,7 @@ remove all session and spool messages
|
||||||
|
|
||||||
@@client_threads[client_id] = true
|
@@client_threads[client_id] = true
|
||||||
@@client_threads[client_id] = Thread.new do
|
@@client_threads[client_id] = Thread.new do
|
||||||
thread_client(client_id)
|
thread_client(client_id, 0, Time.now.utc, node_id)
|
||||||
@@client_threads[client_id] = nil
|
@@client_threads[client_id] = nil
|
||||||
log('debug', "close client (#{client_id}) thread")
|
log('debug', "close client (#{client_id}) thread")
|
||||||
if ActiveRecord::Base.connection.owner == Thread.current
|
if ActiveRecord::Base.connection.owner == Thread.current
|
||||||
|
@ -629,10 +663,10 @@ returns
|
||||||
|
|
||||||
=end
|
=end
|
||||||
|
|
||||||
def self.thread_client(client_id, try_count = 0, try_run_time = Time.now.utc)
|
def self.thread_client(client_id, try_count = 0, try_run_time = Time.now.utc, node_id)
|
||||||
log('debug', "LOOP #{client_id} - #{try_count}")
|
log('debug', "LOOP #{node_id}.#{client_id} - #{try_count}")
|
||||||
begin
|
begin
|
||||||
Sessions::Client.new(client_id)
|
Sessions::Client.new(client_id, node_id)
|
||||||
rescue => e
|
rescue => e
|
||||||
log('error', "thread_client #{client_id} exited with error #{e.inspect}")
|
log('error', "thread_client #{client_id} exited with error #{e.inspect}")
|
||||||
log('error', e.backtrace.join("\n ") )
|
log('error', e.backtrace.join("\n ") )
|
||||||
|
@ -654,13 +688,11 @@ returns
|
||||||
|
|
||||||
# restart job again
|
# restart job again
|
||||||
if try_run_max > try_count
|
if try_run_max > try_count
|
||||||
thread_client(client_id, try_count, try_run_time)
|
thread_client(client_id, try_count, try_run_time, node_id)
|
||||||
return
|
|
||||||
end
|
end
|
||||||
|
raise "STOP thread_client for client #{node_id}.#{client_id} after #{try_run_max} tries"
|
||||||
raise "STOP thread_client for client #{client_id} after #{try_run_max} tries"
|
|
||||||
end
|
end
|
||||||
log('debug', "/LOOP #{client_id} - #{try_count}")
|
log('debug', "/LOOP #{node_id}.#{client_id} - #{try_count}")
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.symbolize_keys(hash)
|
def self.symbolize_keys(hash)
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
class Sessions::Client
|
class Sessions::Client
|
||||||
|
|
||||||
def initialize(client_id)
|
def initialize(client_id, node_id)
|
||||||
@client_id = client_id
|
@client_id = client_id
|
||||||
|
@node_id = node_id
|
||||||
log '---client start ws connection---'
|
log '---client start ws connection---'
|
||||||
fetch
|
fetch
|
||||||
log '---client exiting ws connection---'
|
log '---client exiting ws connection---'
|
||||||
|
@ -22,6 +23,9 @@ class Sessions::Client
|
||||||
loop_count = 0
|
loop_count = 0
|
||||||
loop do
|
loop do
|
||||||
|
|
||||||
|
# check if session still exists
|
||||||
|
return if !Sessions.session_exists?(@client_id)
|
||||||
|
|
||||||
# get connection user
|
# get connection user
|
||||||
session_data = Sessions.get(@client_id)
|
session_data = Sessions.get(@client_id)
|
||||||
return if !session_data
|
return if !session_data
|
||||||
|
@ -71,6 +75,6 @@ class Sessions::Client
|
||||||
end
|
end
|
||||||
|
|
||||||
def log(msg)
|
def log(msg)
|
||||||
Rails.logger.debug "client(#{@client_id}) #{msg}"
|
Rails.logger.debug "client(#{@node_id}.#{@client_id}) #{msg}"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
169
lib/sessions/node.rb
Normal file
169
lib/sessions/node.rb
Normal file
|
@ -0,0 +1,169 @@
|
||||||
|
module Sessions::Node
|
||||||
|
|
||||||
|
# get application root directory
|
||||||
|
@root = Dir.pwd.to_s
|
||||||
|
if @root.blank? || @root == '/'
|
||||||
|
@root = Rails.root
|
||||||
|
end
|
||||||
|
|
||||||
|
# get working directories
|
||||||
|
@path = "#{@root}/tmp/session_node_#{Rails.env}"
|
||||||
|
|
||||||
|
def self.session_assigne(client_id, force = false)
|
||||||
|
|
||||||
|
# get available nodes
|
||||||
|
nodes = Sessions::Node.registered
|
||||||
|
session_count = {}
|
||||||
|
|
||||||
|
nodes.each do |node|
|
||||||
|
count = Sessions::Node.sessions_by(node['node_id'], force).count
|
||||||
|
session_count[node['node_id']] = count
|
||||||
|
end
|
||||||
|
|
||||||
|
# search for lowest session count
|
||||||
|
node_id = nil
|
||||||
|
node_count = nil
|
||||||
|
session_count.each do |local_node_id, count|
|
||||||
|
next if !node_count.nil? && count > node_count
|
||||||
|
node_count = count
|
||||||
|
node_id = local_node_id
|
||||||
|
end
|
||||||
|
|
||||||
|
# assigne session
|
||||||
|
Rails.logger.info "Assigne session to node #{node_id} (#{client_id})"
|
||||||
|
Sessions::Node.sessions_for(node_id, client_id)
|
||||||
|
|
||||||
|
# write node status file
|
||||||
|
node_id
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.cleanup
|
||||||
|
FileUtils.rm_rf @path
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.registered
|
||||||
|
path = "#{@path}/*.status"
|
||||||
|
nodes = []
|
||||||
|
files = Dir.glob(path)
|
||||||
|
files.each do |filename|
|
||||||
|
File.open(filename, 'rb') do |file|
|
||||||
|
file.flock(File::LOCK_SH)
|
||||||
|
content = file.read
|
||||||
|
file.flock(File::LOCK_UN)
|
||||||
|
begin
|
||||||
|
data = JSON.parse(content)
|
||||||
|
nodes.push data
|
||||||
|
rescue => e
|
||||||
|
Rails.logger.error "can't parse status file #{filename}, #{e.inspect}"
|
||||||
|
#to_delete.push "#{path}/#{entry}"
|
||||||
|
#next
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
nodes
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.register(node_id)
|
||||||
|
if !File.exist?(@path)
|
||||||
|
FileUtils.mkpath @path
|
||||||
|
end
|
||||||
|
|
||||||
|
status_file = "#{@path}/#{node_id}.status"
|
||||||
|
|
||||||
|
# write node status file
|
||||||
|
data = {
|
||||||
|
updated_at_human: Time.now.utc,
|
||||||
|
updated_at: Time.now.utc.to_i,
|
||||||
|
node_id: node_id.to_s,
|
||||||
|
pid: $PROCESS_ID,
|
||||||
|
}
|
||||||
|
content = data.to_json
|
||||||
|
|
||||||
|
# store session data in session file
|
||||||
|
File.open(status_file, 'wb') do |file|
|
||||||
|
file.write content
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.stats
|
||||||
|
# read node sessions
|
||||||
|
path = "#{@path}/*.session"
|
||||||
|
|
||||||
|
sessions = {}
|
||||||
|
files = Dir.glob(path)
|
||||||
|
files.each do |filename|
|
||||||
|
File.open(filename, 'rb') do |file|
|
||||||
|
file.flock(File::LOCK_SH)
|
||||||
|
content = file.read
|
||||||
|
file.flock(File::LOCK_UN)
|
||||||
|
begin
|
||||||
|
next if content.blank?
|
||||||
|
data = JSON.parse(content)
|
||||||
|
next if data.blank?
|
||||||
|
next if data['client_id'].blank?
|
||||||
|
sessions[data['client_id']] = data['node_id']
|
||||||
|
rescue => e
|
||||||
|
Rails.logger.error "can't parse session file #{filename}, #{e.inspect}"
|
||||||
|
#to_delete.push "#{path}/#{entry}"
|
||||||
|
#next
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
sessions
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.sessions_for(node_id, client_id)
|
||||||
|
if !File.exist?(@path)
|
||||||
|
FileUtils.mkpath @path
|
||||||
|
end
|
||||||
|
|
||||||
|
status_file = "#{@path}/#{node_id}.#{client_id}.session"
|
||||||
|
|
||||||
|
# write node status file
|
||||||
|
data = {
|
||||||
|
updated_at_human: Time.now.utc,
|
||||||
|
updated_at: Time.now.utc.to_i,
|
||||||
|
node_id: node_id.to_s,
|
||||||
|
client_id: client_id.to_s,
|
||||||
|
pid: $PROCESS_ID,
|
||||||
|
}
|
||||||
|
content = data.to_json
|
||||||
|
|
||||||
|
# store session data in session file
|
||||||
|
File.open(status_file, 'wb') do |file|
|
||||||
|
file.write content
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.sessions_by(node_id, force = false)
|
||||||
|
|
||||||
|
# read node sessions
|
||||||
|
path = "#{@path}/#{node_id}.*.session"
|
||||||
|
|
||||||
|
sessions = []
|
||||||
|
files = Dir.glob(path)
|
||||||
|
files.each do |filename|
|
||||||
|
File.open(filename, 'rb') do |file|
|
||||||
|
file.flock(File::LOCK_SH)
|
||||||
|
content = file.read
|
||||||
|
file.flock(File::LOCK_UN)
|
||||||
|
begin
|
||||||
|
next if content.blank?
|
||||||
|
data = JSON.parse(content)
|
||||||
|
next if data.blank?
|
||||||
|
next if data['client_id'].blank?
|
||||||
|
next if !Sessions.session_exists?(data['client_id']) && force == false
|
||||||
|
sessions.push data['client_id']
|
||||||
|
rescue => e
|
||||||
|
Rails.logger.error "can't parse session file #{filename}, #{e.inspect}"
|
||||||
|
#to_delete.push "#{path}/#{entry}"
|
||||||
|
#next
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
sessions
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
Loading…
Reference in a new issue