background_queue 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.document +5 -0
- data/.rspec +1 -0
- data/.rvmrc +48 -0
- data/Gemfile +19 -0
- data/LICENSE.txt +20 -0
- data/README.md +69 -0
- data/Rakefile +42 -0
- data/TODO +2 -0
- data/VERSION +1 -0
- data/background_queue.gemspec +158 -0
- data/bin/bg_queue +26 -0
- data/lib/background_queue.rb +8 -0
- data/lib/background_queue/client.rb +96 -0
- data/lib/background_queue/client_lib/command.rb +36 -0
- data/lib/background_queue/client_lib/config.rb +109 -0
- data/lib/background_queue/client_lib/connection.rb +105 -0
- data/lib/background_queue/client_lib/job_handle.rb +19 -0
- data/lib/background_queue/command.rb +49 -0
- data/lib/background_queue/config.rb +118 -0
- data/lib/background_queue/server_lib/balanced_queue.rb +108 -0
- data/lib/background_queue/server_lib/config.rb +339 -0
- data/lib/background_queue/server_lib/event_connection.rb +133 -0
- data/lib/background_queue/server_lib/event_server.rb +35 -0
- data/lib/background_queue/server_lib/job.rb +252 -0
- data/lib/background_queue/server_lib/job_registry.rb +30 -0
- data/lib/background_queue/server_lib/lru.rb +193 -0
- data/lib/background_queue/server_lib/owner.rb +54 -0
- data/lib/background_queue/server_lib/priority_queue.rb +156 -0
- data/lib/background_queue/server_lib/queue_registry.rb +123 -0
- data/lib/background_queue/server_lib/server.rb +314 -0
- data/lib/background_queue/server_lib/sorted_workers.rb +52 -0
- data/lib/background_queue/server_lib/task.rb +79 -0
- data/lib/background_queue/server_lib/task_registry.rb +51 -0
- data/lib/background_queue/server_lib/thread_manager.rb +121 -0
- data/lib/background_queue/server_lib/worker.rb +18 -0
- data/lib/background_queue/server_lib/worker_balancer.rb +97 -0
- data/lib/background_queue/server_lib/worker_client.rb +94 -0
- data/lib/background_queue/server_lib/worker_thread.rb +70 -0
- data/lib/background_queue/utils.rb +40 -0
- data/lib/background_queue/worker/base.rb +46 -0
- data/lib/background_queue/worker/calling.rb +59 -0
- data/lib/background_queue/worker/config.rb +35 -0
- data/lib/background_queue/worker/environment.rb +70 -0
- data/lib/background_queue/worker/worker_loader.rb +94 -0
- data/lib/background_queue_server.rb +21 -0
- data/lib/background_queue_worker.rb +5 -0
- data/spec/background_queue/client_lib/command_spec.rb +75 -0
- data/spec/background_queue/client_lib/config_spec.rb +156 -0
- data/spec/background_queue/client_lib/connection_spec.rb +170 -0
- data/spec/background_queue/client_spec.rb +95 -0
- data/spec/background_queue/command_spec.rb +34 -0
- data/spec/background_queue/config_spec.rb +134 -0
- data/spec/background_queue/server_lib/balanced_queue_spec.rb +122 -0
- data/spec/background_queue/server_lib/config_spec.rb +443 -0
- data/spec/background_queue/server_lib/event_connection_spec.rb +190 -0
- data/spec/background_queue/server_lib/event_server_spec.rb +48 -0
- data/spec/background_queue/server_lib/integration/full_test_spec.rb +247 -0
- data/spec/background_queue/server_lib/integration/queue_integration_spec.rb +98 -0
- data/spec/background_queue/server_lib/integration/serialize_spec.rb +127 -0
- data/spec/background_queue/server_lib/job_registry_spec.rb +65 -0
- data/spec/background_queue/server_lib/job_spec.rb +525 -0
- data/spec/background_queue/server_lib/owner_spec.rb +33 -0
- data/spec/background_queue/server_lib/priority_queue_spec.rb +182 -0
- data/spec/background_queue/server_lib/server_spec.rb +353 -0
- data/spec/background_queue/server_lib/sorted_workers_spec.rb +122 -0
- data/spec/background_queue/server_lib/task_registry_spec.rb +69 -0
- data/spec/background_queue/server_lib/task_spec.rb +20 -0
- data/spec/background_queue/server_lib/thread_manager_spec.rb +106 -0
- data/spec/background_queue/server_lib/worker_balancer_spec.rb +127 -0
- data/spec/background_queue/server_lib/worker_client_spec.rb +196 -0
- data/spec/background_queue/server_lib/worker_thread_spec.rb +125 -0
- data/spec/background_queue/utils_spec.rb +27 -0
- data/spec/background_queue/worker/base_spec.rb +35 -0
- data/spec/background_queue/worker/calling_spec.rb +103 -0
- data/spec/background_queue/worker/environment_spec.rb +67 -0
- data/spec/background_queue/worker/worker_loader_spec.rb +103 -0
- data/spec/background_queue_spec.rb +7 -0
- data/spec/resources/config-client.yml +7 -0
- data/spec/resources/config-serialize.yml +12 -0
- data/spec/resources/config.yml +12 -0
- data/spec/resources/example_worker.rb +4 -0
- data/spec/resources/example_worker_with_error.rb +4 -0
- data/spec/resources/test_worker.rb +8 -0
- data/spec/shared/queue_registry_shared.rb +216 -0
- data/spec/spec_helper.rb +15 -0
- data/spec/support/default_task.rb +9 -0
- data/spec/support/private.rb +23 -0
- data/spec/support/simple_server.rb +28 -0
- data/spec/support/simple_task.rb +58 -0
- data/spec/support/test_worker_server.rb +205 -0
- metadata +254 -0
@@ -0,0 +1,54 @@
|
|
1
|
+
module BackgroundQueue::ServerLib
|
2
|
+
|
3
|
+
class Owner < PriorityQueue
|
4
|
+
include BackgroundQueue::ServerLib::QueueRegistry
|
5
|
+
|
6
|
+
attr_accessor :id
|
7
|
+
|
8
|
+
def initialize(id, balanced_queues)
|
9
|
+
@id = id
|
10
|
+
@balanced_queues = balanced_queues
|
11
|
+
super()
|
12
|
+
end
|
13
|
+
|
14
|
+
|
15
|
+
def ==(other)
|
16
|
+
@id == other.id
|
17
|
+
end
|
18
|
+
|
19
|
+
def server
|
20
|
+
@balanced_queues.server
|
21
|
+
end
|
22
|
+
|
23
|
+
def inspect
|
24
|
+
"OwnerQueue:#{self.id} (#{self.object_id})"
|
25
|
+
end
|
26
|
+
|
27
|
+
def self.queue_class
|
28
|
+
BackgroundQueue::ServerLib::Job
|
29
|
+
end
|
30
|
+
|
31
|
+
def synchronous?
|
32
|
+
false
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
def create_queue(queue_id)
|
38
|
+
job = super(queue_id)
|
39
|
+
@balanced_queues.register_job(job)
|
40
|
+
job
|
41
|
+
end
|
42
|
+
|
43
|
+
def get_queue_id_from_item(item)
|
44
|
+
item.job_id
|
45
|
+
end
|
46
|
+
|
47
|
+
def add_item_to_queue(queue, item)
|
48
|
+
queue.add_item(item)
|
49
|
+
end
|
50
|
+
|
51
|
+
|
52
|
+
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,156 @@
|
|
1
|
+
module BackgroundQueue::ServerLib
|
2
|
+
#internally implemented using a list of queues
|
3
|
+
#this does not do any locking, subclasses should look after any locking
|
4
|
+
class PriorityQueue
|
5
|
+
|
6
|
+
def initialize
|
7
|
+
@queues = []
|
8
|
+
@items = {}
|
9
|
+
@stalled_items = {}
|
10
|
+
@stalled = false
|
11
|
+
end
|
12
|
+
|
13
|
+
def pop
|
14
|
+
q = get_next_queue
|
15
|
+
return nil if q.nil?
|
16
|
+
item = q.shift
|
17
|
+
if q.empty?
|
18
|
+
remove_queue(q)
|
19
|
+
end
|
20
|
+
item
|
21
|
+
end
|
22
|
+
|
23
|
+
def push(item)
|
24
|
+
q = get_queue_for_priority(item.priority, true)
|
25
|
+
q.push(item)
|
26
|
+
end
|
27
|
+
|
28
|
+
def remove(item, override_priority=nil)
|
29
|
+
override_priority = item.priority if override_priority.nil?
|
30
|
+
q = get_queue_for_priority(override_priority, false)
|
31
|
+
raise "unable to get queue at priority #{override_priority} when removing" if q.nil?
|
32
|
+
q.delete_if { |q_item| q_item.id == item.id }
|
33
|
+
if q.empty?
|
34
|
+
remove_queue(q)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
#the highest priority queue
|
39
|
+
def priority
|
40
|
+
return nil if @queues.empty?
|
41
|
+
get_next_queue.priority
|
42
|
+
end
|
43
|
+
|
44
|
+
def stalled?
|
45
|
+
@stalled
|
46
|
+
end
|
47
|
+
|
48
|
+
def stalled=(stall)
|
49
|
+
@stalled = stall
|
50
|
+
end
|
51
|
+
|
52
|
+
def empty?
|
53
|
+
@queues.empty?
|
54
|
+
end
|
55
|
+
|
56
|
+
def number_of_priorities
|
57
|
+
@queues.length
|
58
|
+
end
|
59
|
+
|
60
|
+
def number_if_items_at_priority(priority)
|
61
|
+
q = get_queue_for_priority(priority, false)
|
62
|
+
return 0 if q.nil?
|
63
|
+
q.length
|
64
|
+
end
|
65
|
+
|
66
|
+
def peek
|
67
|
+
q = get_next_queue
|
68
|
+
return nil if q.nil?
|
69
|
+
q.first
|
70
|
+
end
|
71
|
+
|
72
|
+
def each_item(&block)
|
73
|
+
for queue in @queues
|
74
|
+
for item in queue
|
75
|
+
block.call(item)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
private
|
81
|
+
|
82
|
+
def get_queue_for_priority(priority, create)
|
83
|
+
@queues.each_with_index do |q, idx|
|
84
|
+
return q if q.priority == priority
|
85
|
+
if q.priority > priority #passed it.. insert here...
|
86
|
+
return nil unless create
|
87
|
+
return insert_queue_at_index(priority, idx)
|
88
|
+
end
|
89
|
+
end
|
90
|
+
return insert_queue_at_index(priority, -1)
|
91
|
+
end
|
92
|
+
|
93
|
+
def insert_queue_at_index(priority, index)
|
94
|
+
q = PriorityArray.new(priority)
|
95
|
+
@queues.insert(index, q)
|
96
|
+
q
|
97
|
+
end
|
98
|
+
|
99
|
+
def get_next_queue
|
100
|
+
@queues.first
|
101
|
+
end
|
102
|
+
|
103
|
+
def remove_queue(queue)
|
104
|
+
@queues.delete(queue)
|
105
|
+
end
|
106
|
+
|
107
|
+
def get_queues
|
108
|
+
@queues
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
|
113
|
+
#this is an array with a priority attribute
|
114
|
+
class PriorityArray < Array
|
115
|
+
|
116
|
+
attr_accessor :priority
|
117
|
+
def initialize(priority)
|
118
|
+
@priority = priority
|
119
|
+
super(0)
|
120
|
+
end
|
121
|
+
|
122
|
+
def ==(other)
|
123
|
+
other.priority == self.priority
|
124
|
+
end
|
125
|
+
|
126
|
+
def inspect
|
127
|
+
"#{self.priority}:#{super}"
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
|
133
|
+
#balanced_queue => owners => jobs => items
|
134
|
+
#items can change owner/jobs
|
135
|
+
#balanced_queue, and owner are priority queues
|
136
|
+
#job is a normal queue
|
137
|
+
#as an item is popped from the queue, its priority is recalculated
|
138
|
+
|
139
|
+
#bq.next_task gets pops owner with highest priority. Calls owner.next_task(). If no tasks, will wait()
|
140
|
+
#owner.next_task pops next job with highest priority. Calls job.next_task
|
141
|
+
#job.next_task pops next task with highest priority
|
142
|
+
#job is added back to owner with current priority (unless empty?). Task is returned
|
143
|
+
#owner is added back to bq with current priority (unless empty?), Task is returned
|
144
|
+
|
145
|
+
#task is run()
|
146
|
+
#mark task as running
|
147
|
+
#call http to call the worker
|
148
|
+
#deregister task if run_version is the same (run_version is incremented each time the same task is reregistered)
|
149
|
+
|
150
|
+
|
151
|
+
#task registry keeps track of items so they can be found/re-inserted
|
152
|
+
#the queue keeps track of all items using hashes until item is removed (not popped)
|
153
|
+
#this allows a get(id) method and a pop() method
|
154
|
+
#get will add an instance to the hash if not found
|
155
|
+
|
156
|
+
|
@@ -0,0 +1,123 @@
|
|
1
|
+
module BackgroundQueue::ServerLib
|
2
|
+
module QueueRegistry
|
3
|
+
|
4
|
+
def add_item(item)
|
5
|
+
in_queue, queue = get_queue(get_queue_id_from_item(item), true)
|
6
|
+
priority_increased, original_priority = track_priority_when_adding_to_queue(queue, item)
|
7
|
+
if !queue.stalled? && (!in_queue || priority_increased)
|
8
|
+
if in_queue #remove from existing priority queue
|
9
|
+
remove(queue, original_priority)
|
10
|
+
end
|
11
|
+
push(queue)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def next_item
|
16
|
+
item = nil
|
17
|
+
queue = pop
|
18
|
+
unless queue.nil?
|
19
|
+
priority_decreased, original_priority, item = remove_item_from_queue(queue, :next)
|
20
|
+
|
21
|
+
# some items must run synchronously, so we dont want to add it back until the task is finished.
|
22
|
+
# if the queue it empty we still want to keep it there until the task is finished, incase the running task queues more tasks against the job.
|
23
|
+
if queue.empty? || queue.synchronous?
|
24
|
+
@items.delete(queue.id)
|
25
|
+
stall_queue(queue)
|
26
|
+
else
|
27
|
+
push(queue)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
item
|
31
|
+
end
|
32
|
+
|
33
|
+
def remove_item(item)
|
34
|
+
in_queue, queue = get_queue(get_queue_id_from_item(item), false)
|
35
|
+
raise "Unable to remove task #{item.id} at priority #{item.priority} (no queue at that priority)" if queue.nil?
|
36
|
+
priority_decreased, original_priority, item = remove_item_from_queue(queue, item)
|
37
|
+
if queue.empty?
|
38
|
+
remove(queue, original_priority)
|
39
|
+
@items.delete(queue.id)
|
40
|
+
elsif priority_decreased
|
41
|
+
remove(queue, original_priority)
|
42
|
+
push(queue)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def finish_item(item)
|
47
|
+
#puts "#{self.class.name}:finish item: #{item.inspect}"
|
48
|
+
in_queue, queue = get_queue(get_queue_id_from_item(item), false)
|
49
|
+
raise "Queue #{get_queue_id_from_item(item)} unavailble when finishing item" if queue.nil?
|
50
|
+
queue.finish_item(item)
|
51
|
+
resume_queue(queue)
|
52
|
+
end
|
53
|
+
|
54
|
+
private
|
55
|
+
|
56
|
+
def create_queue(queue_id)
|
57
|
+
self.class.queue_class.new(queue_id, self)
|
58
|
+
end
|
59
|
+
|
60
|
+
def track_priority_when_adding_to_queue(queue, item)
|
61
|
+
original_priority = queue.priority
|
62
|
+
add_item_to_queue(queue, item) #queue.add_item(item)
|
63
|
+
|
64
|
+
if original_priority.nil? || original_priority > queue.priority
|
65
|
+
return [true, original_priority]
|
66
|
+
end
|
67
|
+
[false, original_priority]
|
68
|
+
end
|
69
|
+
|
70
|
+
def remove_item_from_queue(queue, target_item)
|
71
|
+
original_priority = queue.priority
|
72
|
+
|
73
|
+
if target_item == :next
|
74
|
+
item = queue.next_item
|
75
|
+
else
|
76
|
+
item = queue.remove_item(target_item)
|
77
|
+
end
|
78
|
+
|
79
|
+
if queue.priority.nil? || original_priority < queue.priority
|
80
|
+
return [true, original_priority, item]
|
81
|
+
end
|
82
|
+
[false, original_priority, item]
|
83
|
+
end
|
84
|
+
|
85
|
+
def get_queue(queue_id, create)
|
86
|
+
queue = @items[queue_id]
|
87
|
+
queue = @stalled_items[queue_id] if queue.nil?
|
88
|
+
return [true, queue] unless queue.nil?
|
89
|
+
return [false, nil] unless create
|
90
|
+
queue = create_queue(queue_id) #BackgroundQueue::ServerLib::Owner.new(owner_id)
|
91
|
+
@items[queue_id] = queue
|
92
|
+
return [false, queue]
|
93
|
+
end
|
94
|
+
|
95
|
+
def stall_queue(queue)
|
96
|
+
queue.stalled = true
|
97
|
+
#puts "stalling queue #{queue.inspect}"
|
98
|
+
@stalled_items[queue.id] = queue
|
99
|
+
end
|
100
|
+
|
101
|
+
def resume_queue(queue)
|
102
|
+
if queue.stalled?
|
103
|
+
@stalled_items.delete(queue.id)
|
104
|
+
unless queue.empty?
|
105
|
+
queue.stalled = false
|
106
|
+
push(queue)
|
107
|
+
@items[queue.id] = queue
|
108
|
+
#puts "returned q: #{queue.inspect}"
|
109
|
+
else
|
110
|
+
@items.delete(queue.id)
|
111
|
+
#puts "q empty"
|
112
|
+
end
|
113
|
+
#else
|
114
|
+
# puts "q not stalled"
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
def stalled_items
|
119
|
+
@stalled_items
|
120
|
+
end
|
121
|
+
|
122
|
+
end
|
123
|
+
end
|
@@ -0,0 +1,314 @@
|
|
1
|
+
require 'optparse'
|
2
|
+
require 'logger'
|
3
|
+
|
4
|
+
module BackgroundQueue::ServerLib
|
5
|
+
class Server
|
6
|
+
|
7
|
+
attr_accessor :config
|
8
|
+
attr_accessor :thread_manager
|
9
|
+
attr_accessor :task_queue
|
10
|
+
attr_accessor :event_server
|
11
|
+
attr_accessor :workers
|
12
|
+
attr_accessor :jobs
|
13
|
+
attr_accessor :logger
|
14
|
+
|
15
|
+
def initialize
|
16
|
+
@running = false
|
17
|
+
@stat_mutex = Mutex.new
|
18
|
+
@stats = {
|
19
|
+
:tasks=>0,
|
20
|
+
:run_tasks=>0,
|
21
|
+
:running=>0
|
22
|
+
}
|
23
|
+
end
|
24
|
+
|
25
|
+
def process_args(argv)
|
26
|
+
argv = argv.clone
|
27
|
+
cmd = argv.shift
|
28
|
+
|
29
|
+
if cmd.nil?
|
30
|
+
raise BackgroundQueue::ServerLib::InitError, "Usage: server command [options]"
|
31
|
+
end
|
32
|
+
|
33
|
+
options = {:command=>cmd.nil? ? nil : cmd.downcase.intern}
|
34
|
+
|
35
|
+
env_to_load = "development"
|
36
|
+
|
37
|
+
OptionParser.new do |opts|
|
38
|
+
opts.banner = "Usage: server command [options]"
|
39
|
+
case options[:command]
|
40
|
+
when :start, :test
|
41
|
+
opts.on("-c", "--config PATH", "Configuration Path") do |cp|
|
42
|
+
options[:config] = cp
|
43
|
+
end
|
44
|
+
when :stop
|
45
|
+
|
46
|
+
when nil
|
47
|
+
|
48
|
+
else
|
49
|
+
raise "Invalid Command: #{cmd}"
|
50
|
+
end
|
51
|
+
opts.on("-l", "--logfile [PATH]", "Logfile Path") do |lf|
|
52
|
+
options[:log_file] = lf
|
53
|
+
end
|
54
|
+
opts.on("-v", "--loglevel [LEVEL]", "Log Level") do |ll|
|
55
|
+
options[:log_level] = ll
|
56
|
+
end
|
57
|
+
opts.on("-p", "--pidfile [PATH]", "Pid file Path (/var/run/background_queue.pid)") do |pf|
|
58
|
+
options[:pid_file] = pf
|
59
|
+
end
|
60
|
+
opts.on("-e", "--environment [RAILS_ENV]", "testing/development/production (development)") do |env|
|
61
|
+
env_to_load = env
|
62
|
+
end
|
63
|
+
end.parse!(argv)
|
64
|
+
|
65
|
+
ENV['RAILS_ENV']=env_to_load
|
66
|
+
|
67
|
+
raise BackgroundQueue::ServerLib::InitError, "Missing config argument (-c)" if options[:config].nil? && ([:test, :start].include?(options[:command]) )
|
68
|
+
|
69
|
+
options
|
70
|
+
end
|
71
|
+
|
72
|
+
def load_configuration(path)
|
73
|
+
@config = BackgroundQueue::ServerLib::Config.load_file(path)
|
74
|
+
true
|
75
|
+
end
|
76
|
+
|
77
|
+
def resolve_logging_path(path)
|
78
|
+
File.expand_path(path)
|
79
|
+
end
|
80
|
+
|
81
|
+
def set_logging_level(log, level)
|
82
|
+
if level.nil? || level.strip.length == 0
|
83
|
+
level = "warn"
|
84
|
+
else
|
85
|
+
level = level.to_s.downcase
|
86
|
+
end
|
87
|
+
case level
|
88
|
+
when 'debug'
|
89
|
+
log.level = Logger::DEBUG
|
90
|
+
when 'info'
|
91
|
+
log.level = Logger::INFO
|
92
|
+
when 'warn'
|
93
|
+
log.level = Logger::WARN
|
94
|
+
when 'error'
|
95
|
+
log.level = Logger::ERROR
|
96
|
+
when 'fatal'
|
97
|
+
log.level = Logger::FATAL
|
98
|
+
else
|
99
|
+
raise BackgroundQueue::ServerLib::InitError, "Unknown logging level: #{level}"
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
def init_logging(path, level)
|
104
|
+
unless path.nil? || path.to_s.strip.length == 0
|
105
|
+
path = resolve_logging_path(path)
|
106
|
+
begin
|
107
|
+
@logger = Logger.new(path, "daily")
|
108
|
+
set_logging_level(@logger, level)
|
109
|
+
rescue Exception=>e
|
110
|
+
raise BackgroundQueue::ServerLib::InitError, "Error initializing log file #{path}: #{e.message}"
|
111
|
+
end
|
112
|
+
end
|
113
|
+
if @logger.nil?
|
114
|
+
#just make a fallback logger...
|
115
|
+
@logger = Logger.new($stderr)
|
116
|
+
set_logging_level(@logger, "fatal")
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
def get_pid_path(options)
|
121
|
+
if options[:pid_file]
|
122
|
+
options[:pid_file]
|
123
|
+
else
|
124
|
+
"/var/run/background_queue.pid"
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
def get_pid(options)
|
129
|
+
sPid = nil
|
130
|
+
begin
|
131
|
+
sPid = File.open(get_pid_path(options)) { |f|
|
132
|
+
f.read
|
133
|
+
}
|
134
|
+
rescue
|
135
|
+
return nil
|
136
|
+
end
|
137
|
+
return nil if sPid.nil? || sPid.to_i == 0
|
138
|
+
nPid = sPid.to_i
|
139
|
+
begin
|
140
|
+
Process.kill(0, nPid)
|
141
|
+
return nPid
|
142
|
+
rescue
|
143
|
+
return nil
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
def check_not_running(options)
|
148
|
+
proc_id = get_pid(options)
|
149
|
+
raise BackgroundQueue::ServerLib::InitError, "Process #{proc_id} already running" unless proc_id.nil?
|
150
|
+
nil
|
151
|
+
end
|
152
|
+
|
153
|
+
def stop_pid(options)
|
154
|
+
proc_id = get_pid(options)
|
155
|
+
unless proc_id.nil?
|
156
|
+
begin
|
157
|
+
Process.kill(15, proc_id)
|
158
|
+
rescue
|
159
|
+
#dont care... the process may have died already?
|
160
|
+
end
|
161
|
+
count = 0
|
162
|
+
while get_pid(options) && count < 10
|
163
|
+
puts "Waiting..."
|
164
|
+
sleep(1)
|
165
|
+
end
|
166
|
+
kill_pid(options) #make sure
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
170
|
+
def kill_pid(options)
|
171
|
+
proc_id = get_pid(options)
|
172
|
+
begin
|
173
|
+
Process.kill(9, proc_id) unless proc_id.nil?
|
174
|
+
rescue
|
175
|
+
#dont care... the process may have died already?
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
def write_pid(options)
|
180
|
+
proc_id = Process.pid
|
181
|
+
begin
|
182
|
+
File.open(get_pid_path(options), "w") { |f|
|
183
|
+
f.write(proc_id.to_s)
|
184
|
+
}
|
185
|
+
rescue Exception=>e
|
186
|
+
raise BackgroundQueue::ServerLib::InitError, "Unable to write to pid file #{get_pid_path(options)}: #{e.message}"
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
190
|
+
def remove_pid(options)
|
191
|
+
begin
|
192
|
+
File.delete(get_pid_path(options))
|
193
|
+
rescue
|
194
|
+
end
|
195
|
+
end
|
196
|
+
|
197
|
+
def trap_signals
|
198
|
+
Signal.trap("TERM") do
|
199
|
+
puts "Terminating..."
|
200
|
+
self.stop()
|
201
|
+
end
|
202
|
+
end
|
203
|
+
|
204
|
+
|
205
|
+
def daemonize(options)
|
206
|
+
fork{
|
207
|
+
stdin = open '/dev/null', 'r'
|
208
|
+
stdout = open '/dev/null', 'w'
|
209
|
+
stderr = open '/dev/null', 'w'
|
210
|
+
STDIN.reopen stdin
|
211
|
+
STDOUT.reopen stdout
|
212
|
+
STDERR.reopen stderr
|
213
|
+
fork{
|
214
|
+
write_pid(options) unless options[:skip_pid]
|
215
|
+
run(options)
|
216
|
+
} and exit!
|
217
|
+
}
|
218
|
+
end
|
219
|
+
|
220
|
+
def start(options)
|
221
|
+
begin
|
222
|
+
load_configuration(options[:config])
|
223
|
+
init_logging(options[:log_file], options[:log_level])
|
224
|
+
check_not_running(options) unless options[:skip_pid]
|
225
|
+
write_pid(options) unless options[:skip_pid] #this will make sure we can write the pid file... the daemon will write it again
|
226
|
+
if options[:command] == :start
|
227
|
+
daemonize(options)
|
228
|
+
elsif options[:command] == :run
|
229
|
+
run(options)
|
230
|
+
else
|
231
|
+
raise BackgroundQueue::ServerLib::InitError, "Unknown Command: #{options[:command]}"
|
232
|
+
end
|
233
|
+
rescue BackgroundQueue::ServerLib::InitError=>ie
|
234
|
+
STDERR.puts ie.message
|
235
|
+
rescue Exception=>e
|
236
|
+
STDERR.puts e.message
|
237
|
+
STDERR.puts e.backtrace.join("\n")
|
238
|
+
end
|
239
|
+
end
|
240
|
+
|
241
|
+
def running?
|
242
|
+
@running
|
243
|
+
end
|
244
|
+
|
245
|
+
def run(options)
|
246
|
+
trap_signals
|
247
|
+
@running = true
|
248
|
+
@thread_manager = BackgroundQueue::ServerLib::ThreadManager.new(self, self.config.connections_per_worker)
|
249
|
+
|
250
|
+
@workers = BackgroundQueue::ServerLib::WorkerBalancer.new(self)
|
251
|
+
@task_queue = BackgroundQueue::ServerLib::BalancedQueue.new(self)
|
252
|
+
|
253
|
+
@thread_manager.start(BackgroundQueue::ServerLib::WorkerThread)
|
254
|
+
|
255
|
+
@event_server = BackgroundQueue::ServerLib::EventServer.new(self)
|
256
|
+
|
257
|
+
@jobs = BackgroundQueue::ServerLib::JobRegistry.new
|
258
|
+
|
259
|
+
load_tasks(config.task_file)
|
260
|
+
|
261
|
+
@event_server.start
|
262
|
+
end
|
263
|
+
|
264
|
+
def stop(timeout_secs=10)
|
265
|
+
@running = false
|
266
|
+
@event_server.stop
|
267
|
+
@thread_manager.wait(timeout_secs)
|
268
|
+
save_tasks(config.task_file)
|
269
|
+
end
|
270
|
+
|
271
|
+
def change_stat(stat, delta)
|
272
|
+
@stat_mutex.synchronize {
|
273
|
+
@stats[stat] += delta
|
274
|
+
}
|
275
|
+
end
|
276
|
+
|
277
|
+
def get_stats
|
278
|
+
@stat_mutex.synchronize {
|
279
|
+
@stats.clone
|
280
|
+
}
|
281
|
+
end
|
282
|
+
|
283
|
+
def load_tasks(path)
|
284
|
+
return if path.nil?
|
285
|
+
if File.exist?(path)
|
286
|
+
begin
|
287
|
+
File.open(path, 'r') { |io|
|
288
|
+
task_queue.load_from_file(io)
|
289
|
+
}
|
290
|
+
rescue Exception=>e
|
291
|
+
logger.error("Error loading tasks from #{path}: #{e.message}")
|
292
|
+
logger.debug(e.backtrace.join("\n"))
|
293
|
+
end
|
294
|
+
end
|
295
|
+
end
|
296
|
+
|
297
|
+
def save_tasks(path)
|
298
|
+
return if path.nil?
|
299
|
+
|
300
|
+
begin
|
301
|
+
File.open(path, 'w') { |io|
|
302
|
+
task_queue.save_to_file(io)
|
303
|
+
}
|
304
|
+
rescue Exception=>e
|
305
|
+
logger.error("Error saving tasks to #{path}: #{e.message}")
|
306
|
+
logger.debug(e.backtrace.join("\n"))
|
307
|
+
end
|
308
|
+
end
|
309
|
+
end
|
310
|
+
|
311
|
+
class InitError < Exception
|
312
|
+
|
313
|
+
end
|
314
|
+
end
|