background_queue 0.2.0 → 0.3.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/Gemfile CHANGED
@@ -6,6 +6,8 @@ gem "json"
6
6
  gem "rufus-scheduler"
7
7
  gem "eventmachine", "~>0.12.10"
8
8
  gem "ipaddress", "~> 0.8.0"
9
+ gem "ipaddress", "~> 0.8.0"
10
+ gem "algorithms", "~> 0.5.0"
9
11
 
10
12
  # Add dependencies to develop your gem here.
11
13
  # Include everything needed to run rake, tests, features, etc.
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.2.0
1
+ 0.3.0
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = "background_queue"
8
- s.version = "0.2.0"
8
+ s.version = "0.3.0"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["MarkPent"]
12
- s.date = "2012-10-09"
12
+ s.date = "2012-10-17"
13
13
  s.description = "Organise background tasks so they will not overload the machine(s) running the tasks, while still giving a fair, balanced allocation of running time to members in the queue"
14
14
  s.email = "mark.pent@gmail.com"
15
15
  s.executables = ["bg_queue"]
@@ -40,6 +40,7 @@ Gem::Specification.new do |s|
40
40
  "lib/background_queue/config.rb",
41
41
  "lib/background_queue/server_lib/balanced_queue.rb",
42
42
  "lib/background_queue/server_lib/config.rb",
43
+ "lib/background_queue/server_lib/error_task_list.rb",
43
44
  "lib/background_queue/server_lib/event_connection.rb",
44
45
  "lib/background_queue/server_lib/event_server.rb",
45
46
  "lib/background_queue/server_lib/job.rb",
@@ -73,6 +74,7 @@ Gem::Specification.new do |s|
73
74
  "spec/background_queue/config_spec.rb",
74
75
  "spec/background_queue/server_lib/balanced_queue_spec.rb",
75
76
  "spec/background_queue/server_lib/config_spec.rb",
77
+ "spec/background_queue/server_lib/error_task_list_spec.rb",
76
78
  "spec/background_queue/server_lib/event_connection_spec.rb",
77
79
  "spec/background_queue/server_lib/event_server_spec.rb",
78
80
  "spec/background_queue/server_lib/integration/full_test_spec.rb",
@@ -95,7 +97,6 @@ Gem::Specification.new do |s|
95
97
  "spec/background_queue/worker/calling_spec.rb",
96
98
  "spec/background_queue/worker/environment_spec.rb",
97
99
  "spec/background_queue/worker/worker_loader_spec.rb",
98
- "spec/background_queue_spec.rb",
99
100
  "spec/resources/config-client.yml",
100
101
  "spec/resources/config-serialize.yml",
101
102
  "spec/resources/config.yml",
@@ -124,6 +125,8 @@ Gem::Specification.new do |s|
124
125
  s.add_runtime_dependency(%q<rufus-scheduler>, [">= 0"])
125
126
  s.add_runtime_dependency(%q<eventmachine>, ["~> 0.12.10"])
126
127
  s.add_runtime_dependency(%q<ipaddress>, ["~> 0.8.0"])
128
+ s.add_runtime_dependency(%q<ipaddress>, ["~> 0.8.0"])
129
+ s.add_runtime_dependency(%q<algorithms>, ["~> 0.5.0"])
127
130
  s.add_development_dependency(%q<rspec>, [">= 2.9.0"])
128
131
  s.add_development_dependency(%q<jeweler>, ["~> 1.8.3"])
129
132
  s.add_development_dependency(%q<yard>, ["~> 0.7"])
@@ -135,6 +138,8 @@ Gem::Specification.new do |s|
135
138
  s.add_dependency(%q<rufus-scheduler>, [">= 0"])
136
139
  s.add_dependency(%q<eventmachine>, ["~> 0.12.10"])
137
140
  s.add_dependency(%q<ipaddress>, ["~> 0.8.0"])
141
+ s.add_dependency(%q<ipaddress>, ["~> 0.8.0"])
142
+ s.add_dependency(%q<algorithms>, ["~> 0.5.0"])
138
143
  s.add_dependency(%q<rspec>, [">= 2.9.0"])
139
144
  s.add_dependency(%q<jeweler>, ["~> 1.8.3"])
140
145
  s.add_dependency(%q<yard>, ["~> 0.7"])
@@ -147,6 +152,8 @@ Gem::Specification.new do |s|
147
152
  s.add_dependency(%q<rufus-scheduler>, [">= 0"])
148
153
  s.add_dependency(%q<eventmachine>, ["~> 0.12.10"])
149
154
  s.add_dependency(%q<ipaddress>, ["~> 0.8.0"])
155
+ s.add_dependency(%q<ipaddress>, ["~> 0.8.0"])
156
+ s.add_dependency(%q<algorithms>, ["~> 0.5.0"])
150
157
  s.add_dependency(%q<rspec>, [">= 2.9.0"])
151
158
  s.add_dependency(%q<jeweler>, ["~> 1.8.3"])
152
159
  s.add_dependency(%q<yard>, ["~> 0.7"])
@@ -0,0 +1,116 @@
1
+ require 'algorithms'
2
+ module BackgroundQueue::ServerLib
3
+
4
+ class ErrorTaskList
5
+
6
+ attr_reader :tasks
7
+ attr_reader :task_count
8
+
9
+ def initialize(server)
10
+ @server = server
11
+ @tasks = Containers::RBTreeMap.new
12
+ @mutex = Mutex.new
13
+ @current_next_at = nil
14
+ @current_runner = nil
15
+ @task_count = 0
16
+ end
17
+
18
+ def add_task(task)
19
+ task.increment_error_count
20
+ delay = calculate_delay(task.get_error_count)
21
+ add_item(task, Time.now.to_i + delay)
22
+ end
23
+
24
+ def calculate_delay(error_count)
25
+ delay = error_count * error_count
26
+ delay > 120 ? 120 : delay
27
+ end
28
+
29
+ def add_item(task, time_at)
30
+ @mutex.synchronize {
31
+ existing = @tasks[time_at]
32
+ if existing.nil?
33
+ existing = []
34
+ @tasks[time_at] = existing
35
+ end
36
+ existing << task
37
+ @task_count += 1
38
+ queue_next_event(time_at)
39
+ }
40
+ @server.logger.debug("Task #{task.id} queued to retry in #{time_at - Time.now.to_f} seconds")
41
+ end
42
+
43
+ def queue_next_event(time_at)
44
+ if @current_next_at.nil? || @current_next_at > time_at
45
+ @current_runner.cancel if @current_runner
46
+ @current_next_at = time_at
47
+ @current_runner = BackgroundQueue::ServerLib::ErrorTaskList::RunAt.new(time_at) {
48
+ self.next_event
49
+ }
50
+ end
51
+ end
52
+
53
+ def next_event
54
+ @mutex.synchronize {
55
+ @current_runner = nil
56
+ @current_next_at = nil
57
+ while @tasks.size > 0 && @tasks.min_key < (Time.now.to_f + 0.1)
58
+ next_tasks = @tasks.delete_min
59
+ for task in next_tasks
60
+ @server.task_queue.finish_task(task)
61
+ @server.task_queue.add_task(task)
62
+ @task_count -= 1
63
+ end
64
+ end
65
+ queue_next_event(@tasks.min_key) if @tasks.size > 0
66
+ }
67
+ end
68
+
69
+ def flush
70
+ @server.logger.debug("Flushing #{@tasks.size} tasks from error list")
71
+ @current_runner.cancel if @current_runner
72
+ @current_runner = nil
73
+ @mutex.synchronize {
74
+ while @tasks.size > 0
75
+ next_tasks = @tasks.delete_min
76
+ for task in next_tasks
77
+ @server.task_queue.finish_task(task)
78
+ @server.task_queue.add_task(task)
79
+ @task_count -= 1
80
+ end
81
+ end
82
+ }
83
+ end
84
+
85
+ def wait_for_event
86
+ runner = @current_runner
87
+ runner.wait_for_run if runner
88
+ end
89
+
90
+ class RunAt
91
+ #i dont care about thread safety: it doesnt matter if an event runs twice.
92
+ def initialize(at, &block)
93
+ @running = true
94
+ @th = Thread.new {
95
+ delay = at - Time.now.to_f
96
+ sleep(delay)
97
+ if @running
98
+ block.call
99
+ @running = false
100
+ end
101
+ }
102
+ end
103
+
104
+ def cancel
105
+ return false if !@running
106
+ @running = false
107
+ @th.run #wake the sleep up
108
+ end
109
+
110
+ def wait_for_run
111
+ @th.join
112
+ end
113
+
114
+ end
115
+ end
116
+ end
@@ -49,6 +49,7 @@ module BackgroundQueue::ServerLib
49
49
  send_result(result)
50
50
  rescue Exception=>e
51
51
  @server.logger.error("Error processing command: #{e.message}")
52
+ @server.logger.debug(e.backtrace.join("\n"))
52
53
  send_error(e.message)
53
54
  end
54
55
  end
@@ -1,6 +1,7 @@
1
1
  module BackgroundQueue::ServerLib
2
2
  class Job < PriorityQueue
3
3
 
4
+
4
5
  attr_accessor :id
5
6
  attr_reader :running_status
6
7
  attr_reader :running_ordered_status
@@ -79,6 +80,10 @@ module BackgroundQueue::ServerLib
79
80
  pop
80
81
  end
81
82
 
83
+ def remove_item(item)
84
+ remove(item)
85
+ end
86
+
82
87
  def finish_item(item)
83
88
  @synchronous_count-=1 if item.synchronous?
84
89
  end
@@ -7,6 +7,7 @@ module BackgroundQueue::ServerLib
7
7
  attr_accessor :config
8
8
  attr_accessor :thread_manager
9
9
  attr_accessor :task_queue
10
+ attr_accessor :error_tasks
10
11
  attr_accessor :event_server
11
12
  attr_accessor :workers
12
13
  attr_accessor :jobs
@@ -37,7 +38,7 @@ module BackgroundQueue::ServerLib
37
38
  OptionParser.new do |opts|
38
39
  opts.banner = "Usage: server command [options]"
39
40
  case options[:command]
40
- when :start, :test
41
+ when :start, :test, :run
41
42
  opts.on("-c", "--config PATH", "Configuration Path") do |cp|
42
43
  options[:config] = cp
43
44
  end
@@ -60,6 +61,10 @@ module BackgroundQueue::ServerLib
60
61
  opts.on("-e", "--environment [RAILS_ENV]", "testing/development/production (development)") do |env|
61
62
  env_to_load = env
62
63
  end
64
+ opts.on_tail("-h", "--help", "Show this message") do
65
+ puts opts
66
+ exit
67
+ end
63
68
  end.parse!(argv)
64
69
 
65
70
  ENV['RAILS_ENV']=env_to_load
@@ -254,6 +259,8 @@ module BackgroundQueue::ServerLib
254
259
 
255
260
  @event_server = BackgroundQueue::ServerLib::EventServer.new(self)
256
261
 
262
+ @error_tasks = BackgroundQueue::ServerLib::ErrorTaskList.new(self)
263
+
257
264
  @jobs = BackgroundQueue::ServerLib::JobRegistry.new
258
265
 
259
266
  load_tasks(config.task_file)
@@ -265,6 +272,7 @@ module BackgroundQueue::ServerLib
265
272
  @running = false
266
273
  @event_server.stop
267
274
  @thread_manager.wait(timeout_secs)
275
+ @error_tasks.flush
268
276
  save_tasks(config.task_file)
269
277
  end
270
278
 
@@ -68,6 +68,20 @@ module BackgroundQueue::ServerLib
68
68
  @options[:initial_progress_caption]
69
69
  end
70
70
 
71
+ def get_error_count
72
+ if @error_count.nil?
73
+ 0
74
+ else
75
+ @error_count
76
+ end
77
+ end
78
+
79
+
80
+ def increment_error_count
81
+ @error_count = get_error_count + 1
82
+ end
83
+
84
+
71
85
  def set_worker_status(status)
72
86
  raise "Task without job set" if @job.nil?
73
87
  status[:task_id] = self.id
@@ -111,6 +111,16 @@ class BackgroundQueue::ServerLib::ThreadManager
111
111
  #ignore
112
112
  end
113
113
  end
114
+
115
+ begin
116
+ Timeout::timeout(timeout_limit) {
117
+ for thread in @threads
118
+ thread.join
119
+ end
120
+ }
121
+ rescue Timeout::Error => te2
122
+ @server.logger.error("Timeout while waiting for forced stop threads to finish")
123
+ end
114
124
  end
115
125
  end
116
126
 
@@ -19,11 +19,17 @@ module BackgroundQueue::ServerLib
19
19
  read_response(worker, response, task)
20
20
  end
21
21
  end
22
- true
22
+ return :ok
23
+ rescue BackgroundQueue::ServerLib::WorkerError => we
24
+ @server.logger.error("Worker Error sending request #{task.id} to worker: #{e.message}")
25
+ return :worker_error
26
+ rescue BackgroundQueue::ServerLib::ThreadManager::ForcedStop => fe
27
+ @server.logger.error("Thread stop while sending request #{task.id} to worker: #{fe.message}")
28
+ return :stop
23
29
  rescue Exception=>e
24
30
  @server.logger.error("Error sending request #{task.id} to worker: #{e.message}")
25
31
  @server.logger.debug(e.backtrace.join("\n"))
26
- return false
32
+ return :fatal_error
27
33
  end
28
34
  end
29
35
 
@@ -49,7 +55,7 @@ module BackgroundQueue::ServerLib
49
55
  @prev_chunk = nil
50
56
  end
51
57
  else
52
- raise "Invalid response code (#{http_response.code}) when calling #{worker.uri.to_s}"
58
+ raise BackgroundQueue::ServerLib::WorkerError, "Invalid response code (#{http_response.code}) when calling #{worker.uri.to_s}"
53
59
  end
54
60
  end
55
61
 
@@ -91,4 +97,8 @@ module BackgroundQueue::ServerLib
91
97
  end
92
98
 
93
99
  end
100
+
101
+ class WorkerError < Exception
102
+
103
+ end
94
104
  end
@@ -31,24 +31,26 @@ module BackgroundQueue::ServerLib
31
31
  Kernel.sleep(1) unless !@server.running?
32
32
  else
33
33
  client = build_client
34
- if client.send_request(worker, task, @server.config.secret)
34
+ result = client.send_request(worker, task, @server.config.secret)
35
+ if result == :ok
35
36
  @server.logger.debug("called worker for task #{task.id}")
36
37
  @server.workers.finish_using_worker(worker, true)
37
38
  @server.task_queue.finish_task(task)
38
39
  @server.change_stat(:running, -1)
39
40
  @server.change_stat(:run_tasks, 1)
40
-
41
41
  return true
42
42
  else
43
43
  @server.logger.debug("failed calling worker for task #{task.id}")
44
- @server.workers.finish_using_worker(worker, false)
45
- error_count += 1
46
- if error_count > 5
47
- @server.logger.debug("error count exceeded for task #{task.id}, returning to queue")
48
- @server.task_queue.finish_task(task)
49
- @server.task_queue.add_task(task)
50
- return true
51
- end
44
+ @server.workers.finish_using_worker(worker, result == :worker_error)
45
+ @server.error_tasks.add_task(task)
46
+ return result != :stop
47
+ #error_count += 1
48
+ #if error_count > 5
49
+ # @server.logger.debug("error count exceeded for task #{task.id}, returning to queue")
50
+ # @server.task_queue.finish_task(task)
51
+ # @server.task_queue.add_task(task)
52
+ # return true
53
+ #end
52
54
  end
53
55
  end
54
56
  end
@@ -1,6 +1,8 @@
1
1
  module BackgroundQueue::Worker
2
2
  class Config
3
3
 
4
+ @@worker_path = nil
5
+
4
6
  def self.secret=(auth)
5
7
  @@secret = auth
6
8
  end
@@ -1,5 +1,6 @@
1
1
  require "background_queue/utils"
2
2
  require "background_queue/config"
3
+ require "background_queue/command"
3
4
  require "background_queue/server_lib/lru"
4
5
  require "background_queue/server_lib/config"
5
6
  require "background_queue/server_lib/priority_queue"
@@ -19,3 +20,4 @@ require "background_queue/server_lib/worker_thread"
19
20
  require "background_queue/server_lib/server"
20
21
  require "background_queue/server_lib/event_server"
21
22
  require "background_queue/server_lib/event_connection"
23
+ require "background_queue/server_lib/error_task_list"
@@ -0,0 +1,142 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper')
2
+ require 'background_queue_server'
3
+
4
+
5
+ describe BackgroundQueue::ServerLib::ErrorTaskList do
6
+
7
+ let(:server) {
8
+ SimpleServer.new
9
+ }
10
+
11
+ subject {
12
+ BackgroundQueue::ServerLib::ErrorTaskList.new(server)
13
+ }
14
+
15
+ let(:task) {
16
+ double("task", :get_error_count=>1, :id=>:task1)
17
+ }
18
+ let(:task2) {
19
+ double("task2", :get_error_count=>1, :id=>:task2)
20
+ }
21
+ let(:task3) {
22
+ double("task3", :get_error_count=>1, :id=>:task3)
23
+ }
24
+ let(:task4) {
25
+ double("task4", :get_error_count=>1, :id=>:task4)
26
+ }
27
+
28
+
29
+ context "#add_task" do
30
+ it "will calculate the delay then add it to the list using it" do
31
+ subject.should_receive(:calculate_delay).with(1).and_return(1)
32
+ subject.should_receive(:add_item).with(task, 101)
33
+ Time.any_instance.should_receive(:to_i).and_return(100)
34
+ task.should_receive(:increment_error_count)
35
+ subject.add_task(task)
36
+ end
37
+ end
38
+
39
+ context "#calculate_delay" do
40
+ it "will square the error count" do
41
+ subject.calculate_delay(1).should eq(1)
42
+ subject.calculate_delay(2).should eq(4)
43
+ subject.calculate_delay(3).should eq(9)
44
+ subject.calculate_delay(10).should eq(100)
45
+ end
46
+
47
+ it "will use a threshold (120)" do
48
+ subject.calculate_delay(50).should eq(120)
49
+ end
50
+ end
51
+
52
+ context "#add_item" do
53
+ it "will add an item using the scheduled time as key" do
54
+ subject.should_receive(:queue_next_event).with(10)
55
+ subject.add_item(task, 10)
56
+ subject.tasks.size.should eq(1)
57
+ subject.tasks.min_key.should be(10)
58
+ subject.tasks.delete_min.should eq([task])
59
+ end
60
+
61
+ it "will add the task to an event at the same time" do
62
+ subject.should_receive(:queue_next_event).twice.with(10)
63
+ subject.should_receive(:queue_next_event).with(20)
64
+ subject.add_item(task, 10)
65
+ subject.add_item(task2, 10)
66
+ subject.add_item(task3, 20)
67
+ subject.tasks.size.should eq(2)
68
+ subject.tasks.min_key.should be(10)
69
+ subject.tasks.delete_min.should eq([task, task2])
70
+ subject.tasks.min_key.should be(20)
71
+ subject.tasks.delete_min.should eq([task3])
72
+ end
73
+ end
74
+
75
+ context "#queue_next_event" do
76
+ it "will queue a new time if its closer then the last" do
77
+ timer = double("timer")
78
+ timer.should_receive(:cancel)
79
+ BackgroundQueue::ServerLib::ErrorTaskList::RunAt.should_receive(:new).with(10).and_return(timer)
80
+ BackgroundQueue::ServerLib::ErrorTaskList::RunAt.should_receive(:new).with(5).and_return(timer)
81
+ subject.queue_next_event(10)
82
+ subject.queue_next_event(5)
83
+ end
84
+
85
+ it "will not queue a new event if its furhter away" do
86
+ timer = double("timer")
87
+ BackgroundQueue::ServerLib::ErrorTaskList::RunAt.should_receive(:new).with(10).and_return(timer)
88
+ subject.queue_next_event(10)
89
+ subject.queue_next_event(20)
90
+ end
91
+
92
+ it "will fire an event" do
93
+ subject.should_receive(:next_event)
94
+ subject.queue_next_event(Time.now.to_f + 0.1)
95
+ subject.wait_for_event
96
+ end
97
+ end
98
+
99
+ context "#next_event" do
100
+ it "will get all the tasks before the current time and re-add them" do
101
+ subject.tasks[Time.now.to_f] = [task, task2]
102
+ subject.tasks[Time.now.to_f + 0.01] = [task3]
103
+ task_at = Time.now.to_f + 1.0
104
+ subject.tasks[task_at] = [task4]
105
+ server.task_queue = double("tq")
106
+ server.task_queue.should_receive(:finish_task).with(task)
107
+ server.task_queue.should_receive(:add_task).with(task)
108
+ server.task_queue.should_receive(:finish_task).with(task2)
109
+ server.task_queue.should_receive(:add_task).with(task2)
110
+ server.task_queue.should_receive(:finish_task).with(task3)
111
+ server.task_queue.should_receive(:add_task).with(task3)
112
+ subject.should_receive(:queue_next_event).with(task_at)
113
+ subject.next_event
114
+ subject.tasks.size.should eq(1)
115
+ end
116
+ end
117
+
118
+ context "RunAt" do
119
+ it "will run a task at the specificied time" do
120
+ run_at = Time.now.to_f + 0.1
121
+ when_run = 0
122
+ runner = BackgroundQueue::ServerLib::ErrorTaskList::RunAt.new(run_at) {
123
+ when_run = Time.now.to_f
124
+ }
125
+ runner.wait_for_run
126
+ when_run.should_not eq(0)
127
+ when_run.to_i.should eq(run_at.to_i)
128
+ end
129
+
130
+ it "can be cancelled" do
131
+ run_at = Time.now.to_f + 0.5
132
+ has_run = false
133
+ runner = BackgroundQueue::ServerLib::ErrorTaskList::RunAt.new(run_at) {
134
+ has_run = true
135
+ }
136
+ sleep(0.05)
137
+ runner.cancel.should be_true
138
+ runner.wait_for_run
139
+ has_run.should be_false
140
+ end
141
+ end
142
+ end
@@ -72,7 +72,7 @@ describe "Queue Integration" do
72
72
 
73
73
  task = subject.next_item
74
74
  task.id.should be(:task_id)
75
- subject.next_item.should be_nil
75
+ subject.next_item.should be_nil
76
76
  subject.finish_item(task)
77
77
  subject.next_item.id.should eq(:task_id2)
78
78
 
@@ -94,5 +94,29 @@ describe "Queue Integration" do
94
94
 
95
95
  end
96
96
  end
97
+
98
+
99
+
100
+ context "Duplicate Tasks" do
101
+ it "will replace a duplicate task not running" do
102
+ subject.add_task(SimpleTask.new(:owner_id, :job_id, :task_id, 3, {:synchronous=>true}))
103
+ subject.add_task(SimpleTask.new(:owner_id, :job_id, :task_id, 3, {:synchronous=>true}))
104
+ task = subject.next_item
105
+ task.id.should be(:task_id)
106
+ subject.next_item.should be_nil
107
+ end
108
+
109
+ it "will queue a duplicate task that is running" do
110
+ subject.add_task(SimpleTask.new(:owner_id, :job_id, :task_id, 3, {:synchronous=>true}))
111
+ task = subject.next_item
112
+ task.id.should be(:task_id)
113
+ subject.next_item.should be_nil
114
+ subject.add_task(SimpleTask.new(:owner_id, :job_id, :task_id, 3, {:synchronous=>true}))
115
+ subject.next_item.should be_nil
116
+ subject.finish_item(task)
117
+ task = subject.next_item
118
+ task.id.should be(:task_id)
119
+ end
120
+ end
97
121
 
98
122
  end
@@ -40,7 +40,7 @@ describe BackgroundQueue::ServerLib::WorkerClient do
40
40
  task = SimpleTask.new(:owner_id, :job_id, :task_id, 3, { :domain=>"www.example.com" })
41
41
  subject.should_receive(:build_request).with(uri, task, "auth").and_return(:post_request)
42
42
  Net::HTTP.should_receive(:start).with("127.0.0.1", 3000).and_raise("connection error")
43
- subject.send_request(worker_config, task, "auth").should be_false
43
+ subject.send_request(worker_config, task, "auth").should eq(:fatal_error)
44
44
  end
45
45
  end
46
46
 
@@ -112,7 +112,7 @@ describe BackgroundQueue::ServerLib::WorkerClient do
112
112
 
113
113
  context "can handle thread cancelling" do
114
114
  #this can cause issues with other tests....
115
- xit "will return false if exception raised" do
115
+ it "will return false if server stopped", :can_be_flakey=>true do
116
116
 
117
117
  mutex = Mutex.new
118
118
  resource = ConditionVariable.new
@@ -122,66 +122,69 @@ describe BackgroundQueue::ServerLib::WorkerClient do
122
122
 
123
123
  run_request = false
124
124
  ss = TestWorkerServer.new(8001)
125
- ss.start(Proc.new { |controller|
126
- #puts "in proc"
127
-
128
- mutex2.synchronize {
129
- resource2.signal
125
+ begin
126
+ ss.start(Proc.new { |controller|
127
+ #puts "in proc"
128
+
129
+ mutex2.synchronize {
130
+ resource2.signal
131
+ }
132
+ #puts "in proc: waiting"
133
+ mutex.synchronize {
134
+ resource.wait(mutex)
135
+ }
136
+ #puts "waited"
137
+ run_request = true
138
+ controller.render :text =>{:percent=>100, :caption=>"Done"}.to_json, :type=>"text/text"
139
+ })
140
+
141
+
142
+ uri = URI("http://127.0.0.1:8001/background_queue")
143
+ worker_config = BackgroundQueue::ServerLib::Worker.new(uri)
144
+ task = SimpleTask.new(:owner_id, :job_id, :task_id, 3, { :domain=>"www.example.com" })
145
+
146
+ call_result = nil
147
+ t1 = Thread.new {
148
+ #puts "calling"
149
+ begin
150
+ status = Timeout::timeout(2) {
151
+ call_result = subject.send_request(worker_config, task, "abcd")
152
+ # puts "called"
153
+ }
154
+ rescue Timeout::Error=>te
155
+ #puts "timeout"
156
+ call_result = :timeout
157
+ end
158
+ mutex2.synchronize {
159
+ resource2.signal
160
+ }
130
161
  }
131
- #puts "in proc: waiting"
132
- mutex.synchronize {
133
- resource.wait(mutex)
162
+
163
+ #wait until we know the request has been sent and is processing
164
+ mutex2.synchronize {
165
+ resource2.wait(mutex2)
134
166
  }
135
- #puts "waited"
136
- run_request = true
137
- controller.render :text =>{:percent=>100, :caption=>"Done"}.to_json, :type=>"text/text"
138
- })
139
-
140
-
141
- uri = URI("http://127.0.0.1:8001/background_queue")
142
- worker_config = BackgroundQueue::ServerLib::Worker.new(uri)
143
- task = SimpleTask.new(:owner_id, :job_id, :task_id, 3, { :domain=>"www.example.com" })
144
-
145
- call_result = nil
146
- t1 = Thread.new {
147
- #puts "calling"
148
- begin
149
- status = Timeout::timeout(2) {
150
- call_result = subject.send_request(worker_config, task, "abcd")
151
- # puts "called"
152
- }
153
- rescue Timeout::Error=>te
154
- #puts "timeout"
155
- call_result = :timeout
156
- end
167
+ run_request.should be_false
168
+ #puts "cancelling"
169
+ t1.raise BackgroundQueue::ServerLib::ThreadManager::ForcedStop.new("Timeout when forcing threads to stop")
170
+
171
+ #puts "canceled"
172
+ #wait until we know the request has been cancelled
157
173
  mutex2.synchronize {
158
- resource2.signal
174
+ resource2.wait(mutex2)
159
175
  }
160
- }
161
-
162
- #wait until we know the request has been sent and is processing
163
- mutex2.synchronize {
164
- resource2.wait(mutex2)
165
- }
166
- run_request.should be_false
167
- #puts "cancelling"
168
- t1.raise "Cancelling From Thread"
169
-
170
- #puts "canceled"
171
- #wait until we know the request has been cancelled
172
- mutex2.synchronize {
173
- resource2.wait(mutex2)
174
- }
175
- call_result.should be_false
176
- run_request.should be_false
177
-
178
- mutex.synchronize {
179
- resource.signal
180
- }
181
-
182
-
183
- t1.join
184
- ss.stop
176
+ call_result.should eq(:stop)
177
+ run_request.should be_false
178
+
179
+ mutex.synchronize {
180
+ resource.signal
181
+ }
182
+
183
+
184
+ t1.join
185
+ ensure
186
+ ss.stop
187
+ end
185
188
 
186
189
  end
187
190
 
@@ -11,9 +11,12 @@ describe BackgroundQueue::ServerLib::WorkerThread do
11
11
  let(:workers) {
12
12
  double("workers")
13
13
  }
14
+ let(:error_tasks) {
15
+ double("error_tasks")
16
+ }
14
17
 
15
18
  let(:server) {
16
- SimpleServer.new(:task_queue=>balanced_queue, :workers=>workers, :config=>double("conf", :secret=>:secret))
19
+ SimpleServer.new(:task_queue=>balanced_queue, :workers=>workers, :config=>double("conf", :secret=>:secret), :error_tasks=>error_tasks)
17
20
  }
18
21
 
19
22
  subject { BackgroundQueue::ServerLib::WorkerThread.new(server) }
@@ -48,31 +51,38 @@ describe BackgroundQueue::ServerLib::WorkerThread do
48
51
  server.stub('running?'=>true)
49
52
  worker = double("worker")
50
53
  task = DefaultTask.new
51
- BackgroundQueue::ServerLib::WorkerClient.any_instance.should_receive(:send_request).with(worker, task, :secret).and_return(true)
54
+ worker_client = double("worker_client")
55
+ subject.should_receive(:build_client).and_return(worker_client)
56
+ worker_client.should_receive(:send_request).with(worker, task, :secret).and_return(:ok)
52
57
  server.workers.should_receive(:get_next_worker).and_return(worker)
53
58
  server.workers.should_receive(:finish_using_worker).with(worker, true)
54
59
  server.task_queue.should_receive(:finish_task).with(task)
55
60
  subject.call_worker(task).should be_true
56
61
  end
57
62
 
58
- it "will keep trying if the worker fails" do
63
+ it "will regegister as a failed worker if send_request returns :fatal_error" do
59
64
  server.stub('running?'=>true)
60
65
  worker = double("worker")
61
66
  task = DefaultTask.new
62
- count = 0
63
- worker_client1 = double("w1")
64
- worker_client1.should_receive(:send_request).with(worker, task, :secret).and_return(false)
65
- worker_client2 = double("w2")
66
- worker_client2.should_receive(:send_request).with(worker, task, :secret).and_return(true)
67
-
68
- subject.should_receive(:build_client).twice {
69
- count += 1
70
- count == 1 ? worker_client1 : worker_client2
71
- }
72
- server.workers.should_receive(:get_next_worker).twice.and_return(worker)
67
+ server.error_tasks.should_receive(:add_task).with(task)
68
+ worker_client = double("worker_client")
69
+ subject.should_receive(:build_client).and_return(worker_client)
70
+ worker_client.should_receive(:send_request).with(worker, task, :secret).and_return(:fatal_error)
71
+ server.workers.should_receive(:get_next_worker).and_return(worker)
73
72
  server.workers.should_receive(:finish_using_worker).with(worker, false)
73
+ subject.call_worker(task).should be_true
74
+ end
75
+
76
+ it "will regegister as a ok worker if send_request returns :worker_error" do
77
+ server.stub('running?'=>true)
78
+ worker = double("worker")
79
+ task = DefaultTask.new
80
+ server.error_tasks.should_receive(:add_task).with(task)
81
+ worker_client = double("worker_client")
82
+ subject.should_receive(:build_client).and_return(worker_client)
83
+ worker_client.should_receive(:send_request).with(worker, task, :secret).and_return(:worker_error)
84
+ server.workers.should_receive(:get_next_worker).and_return(worker)
74
85
  server.workers.should_receive(:finish_using_worker).with(worker, true)
75
- server.task_queue.should_receive(:finish_task).with(task)
76
86
  subject.call_worker(task).should be_true
77
87
  end
78
88
 
@@ -86,7 +96,7 @@ describe BackgroundQueue::ServerLib::WorkerThread do
86
96
  count == 1 ? nil : worker
87
97
  }
88
98
  Kernel.should_receive(:sleep).with(1)
89
- BackgroundQueue::ServerLib::WorkerClient.any_instance.should_receive(:send_request).with(worker, task, :secret).and_return(true)
99
+ BackgroundQueue::ServerLib::WorkerClient.any_instance.should_receive(:send_request).with(worker, task, :secret).and_return(:ok)
90
100
  server.workers.should_receive(:finish_using_worker).with(worker, true)
91
101
  server.task_queue.should_receive(:finish_task).with(task)
92
102
  subject.call_worker(task).should be_true
data/spec/spec_helper.rb CHANGED
@@ -9,7 +9,7 @@ Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each {|f| require f}
9
9
  Dir["#{File.dirname(__FILE__)}/shared/**/*.rb"].each {|f| require f}
10
10
 
11
11
  RSpec.configure do |config|
12
-
12
+ #config.filter_run_excluding :can_be_flakey => true
13
13
  end
14
14
 
15
15
 
@@ -6,6 +6,7 @@ class SimpleServer
6
6
  attr_accessor :workers
7
7
  attr_accessor :jobs
8
8
  attr_accessor :logger
9
+ attr_accessor :error_tasks
9
10
 
10
11
  def initialize(options={})
11
12
  @logger = Logger.new("/dev/null")
@@ -15,6 +16,7 @@ class SimpleServer
15
16
  @event_server = options[:event_server]
16
17
  @workers = options[:workers]
17
18
  @jobs = options[:jobs]
19
+ @error_tasks = options[:error_tasks]
18
20
  end
19
21
 
20
22
  def running?
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: background_queue
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.3.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,11 +9,11 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-10-09 00:00:00.000000000 Z
12
+ date: 2012-10-17 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: json
16
- requirement: &21144380 !ruby/object:Gem::Requirement
16
+ requirement: &19238880 !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
19
  - - ! '>='
@@ -21,10 +21,10 @@ dependencies:
21
21
  version: '0'
22
22
  type: :runtime
23
23
  prerelease: false
24
- version_requirements: *21144380
24
+ version_requirements: *19238880
25
25
  - !ruby/object:Gem::Dependency
26
26
  name: rufus-scheduler
27
- requirement: &21156100 !ruby/object:Gem::Requirement
27
+ requirement: &20184220 !ruby/object:Gem::Requirement
28
28
  none: false
29
29
  requirements:
30
30
  - - ! '>='
@@ -32,10 +32,10 @@ dependencies:
32
32
  version: '0'
33
33
  type: :runtime
34
34
  prerelease: false
35
- version_requirements: *21156100
35
+ version_requirements: *20184220
36
36
  - !ruby/object:Gem::Dependency
37
37
  name: eventmachine
38
- requirement: &21161240 !ruby/object:Gem::Requirement
38
+ requirement: &20182860 !ruby/object:Gem::Requirement
39
39
  none: false
40
40
  requirements:
41
41
  - - ~>
@@ -43,10 +43,10 @@ dependencies:
43
43
  version: 0.12.10
44
44
  type: :runtime
45
45
  prerelease: false
46
- version_requirements: *21161240
46
+ version_requirements: *20182860
47
47
  - !ruby/object:Gem::Dependency
48
48
  name: ipaddress
49
- requirement: &21156960 !ruby/object:Gem::Requirement
49
+ requirement: &20180600 !ruby/object:Gem::Requirement
50
50
  none: false
51
51
  requirements:
52
52
  - - ~>
@@ -54,10 +54,32 @@ dependencies:
54
54
  version: 0.8.0
55
55
  type: :runtime
56
56
  prerelease: false
57
- version_requirements: *21156960
57
+ version_requirements: *20180600
58
+ - !ruby/object:Gem::Dependency
59
+ name: ipaddress
60
+ requirement: &20178440 !ruby/object:Gem::Requirement
61
+ none: false
62
+ requirements:
63
+ - - ~>
64
+ - !ruby/object:Gem::Version
65
+ version: 0.8.0
66
+ type: :runtime
67
+ prerelease: false
68
+ version_requirements: *20178440
69
+ - !ruby/object:Gem::Dependency
70
+ name: algorithms
71
+ requirement: &20177140 !ruby/object:Gem::Requirement
72
+ none: false
73
+ requirements:
74
+ - - ~>
75
+ - !ruby/object:Gem::Version
76
+ version: 0.5.0
77
+ type: :runtime
78
+ prerelease: false
79
+ version_requirements: *20177140
58
80
  - !ruby/object:Gem::Dependency
59
81
  name: rspec
60
- requirement: &21170000 !ruby/object:Gem::Requirement
82
+ requirement: &19623560 !ruby/object:Gem::Requirement
61
83
  none: false
62
84
  requirements:
63
85
  - - ! '>='
@@ -65,10 +87,10 @@ dependencies:
65
87
  version: 2.9.0
66
88
  type: :development
67
89
  prerelease: false
68
- version_requirements: *21170000
90
+ version_requirements: *19623560
69
91
  - !ruby/object:Gem::Dependency
70
92
  name: jeweler
71
- requirement: &21166880 !ruby/object:Gem::Requirement
93
+ requirement: &19621540 !ruby/object:Gem::Requirement
72
94
  none: false
73
95
  requirements:
74
96
  - - ~>
@@ -76,10 +98,10 @@ dependencies:
76
98
  version: 1.8.3
77
99
  type: :development
78
100
  prerelease: false
79
- version_requirements: *21166880
101
+ version_requirements: *19621540
80
102
  - !ruby/object:Gem::Dependency
81
103
  name: yard
82
- requirement: &21166160 !ruby/object:Gem::Requirement
104
+ requirement: &19617840 !ruby/object:Gem::Requirement
83
105
  none: false
84
106
  requirements:
85
107
  - - ~>
@@ -87,10 +109,10 @@ dependencies:
87
109
  version: '0.7'
88
110
  type: :development
89
111
  prerelease: false
90
- version_requirements: *21166160
112
+ version_requirements: *19617840
91
113
  - !ruby/object:Gem::Dependency
92
114
  name: rdoc
93
- requirement: &21164920 !ruby/object:Gem::Requirement
115
+ requirement: &19615920 !ruby/object:Gem::Requirement
94
116
  none: false
95
117
  requirements:
96
118
  - - ~>
@@ -98,10 +120,10 @@ dependencies:
98
120
  version: '3.12'
99
121
  type: :development
100
122
  prerelease: false
101
- version_requirements: *21164920
123
+ version_requirements: *19615920
102
124
  - !ruby/object:Gem::Dependency
103
125
  name: bundler
104
- requirement: &21180500 !ruby/object:Gem::Requirement
126
+ requirement: &20601320 !ruby/object:Gem::Requirement
105
127
  none: false
106
128
  requirements:
107
129
  - - ~>
@@ -109,10 +131,10 @@ dependencies:
109
131
  version: 1.0.0
110
132
  type: :development
111
133
  prerelease: false
112
- version_requirements: *21180500
134
+ version_requirements: *20601320
113
135
  - !ruby/object:Gem::Dependency
114
136
  name: redcarpet
115
- requirement: &21179640 !ruby/object:Gem::Requirement
137
+ requirement: &20599700 !ruby/object:Gem::Requirement
116
138
  none: false
117
139
  requirements:
118
140
  - - ~>
@@ -120,7 +142,7 @@ dependencies:
120
142
  version: 2.1.1
121
143
  type: :development
122
144
  prerelease: false
123
- version_requirements: *21179640
145
+ version_requirements: *20599700
124
146
  description: Organise background tasks so they will not overload the machine(s) running
125
147
  the tasks, while still giving a fair, balanced allocation of running time to members
126
148
  in the queue
@@ -154,6 +176,7 @@ files:
154
176
  - lib/background_queue/config.rb
155
177
  - lib/background_queue/server_lib/balanced_queue.rb
156
178
  - lib/background_queue/server_lib/config.rb
179
+ - lib/background_queue/server_lib/error_task_list.rb
157
180
  - lib/background_queue/server_lib/event_connection.rb
158
181
  - lib/background_queue/server_lib/event_server.rb
159
182
  - lib/background_queue/server_lib/job.rb
@@ -187,6 +210,7 @@ files:
187
210
  - spec/background_queue/config_spec.rb
188
211
  - spec/background_queue/server_lib/balanced_queue_spec.rb
189
212
  - spec/background_queue/server_lib/config_spec.rb
213
+ - spec/background_queue/server_lib/error_task_list_spec.rb
190
214
  - spec/background_queue/server_lib/event_connection_spec.rb
191
215
  - spec/background_queue/server_lib/event_server_spec.rb
192
216
  - spec/background_queue/server_lib/integration/full_test_spec.rb
@@ -209,7 +233,6 @@ files:
209
233
  - spec/background_queue/worker/calling_spec.rb
210
234
  - spec/background_queue/worker/environment_spec.rb
211
235
  - spec/background_queue/worker/worker_loader_spec.rb
212
- - spec/background_queue_spec.rb
213
236
  - spec/resources/config-client.yml
214
237
  - spec/resources/config-serialize.yml
215
238
  - spec/resources/config.yml
@@ -238,7 +261,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
238
261
  version: '0'
239
262
  segments:
240
263
  - 0
241
- hash: 2926344861987835839
264
+ hash: 613144620285186166
242
265
  required_rubygems_version: !ruby/object:Gem::Requirement
243
266
  none: false
244
267
  requirements:
@@ -1,7 +0,0 @@
1
- require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
-
3
- describe "BackgroundQueue" do
4
- xit "does nothing yet" do
5
-
6
- end
7
- end