background_queue 0.2.0 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
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