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.
Files changed (91) hide show
  1. data/.document +5 -0
  2. data/.rspec +1 -0
  3. data/.rvmrc +48 -0
  4. data/Gemfile +19 -0
  5. data/LICENSE.txt +20 -0
  6. data/README.md +69 -0
  7. data/Rakefile +42 -0
  8. data/TODO +2 -0
  9. data/VERSION +1 -0
  10. data/background_queue.gemspec +158 -0
  11. data/bin/bg_queue +26 -0
  12. data/lib/background_queue.rb +8 -0
  13. data/lib/background_queue/client.rb +96 -0
  14. data/lib/background_queue/client_lib/command.rb +36 -0
  15. data/lib/background_queue/client_lib/config.rb +109 -0
  16. data/lib/background_queue/client_lib/connection.rb +105 -0
  17. data/lib/background_queue/client_lib/job_handle.rb +19 -0
  18. data/lib/background_queue/command.rb +49 -0
  19. data/lib/background_queue/config.rb +118 -0
  20. data/lib/background_queue/server_lib/balanced_queue.rb +108 -0
  21. data/lib/background_queue/server_lib/config.rb +339 -0
  22. data/lib/background_queue/server_lib/event_connection.rb +133 -0
  23. data/lib/background_queue/server_lib/event_server.rb +35 -0
  24. data/lib/background_queue/server_lib/job.rb +252 -0
  25. data/lib/background_queue/server_lib/job_registry.rb +30 -0
  26. data/lib/background_queue/server_lib/lru.rb +193 -0
  27. data/lib/background_queue/server_lib/owner.rb +54 -0
  28. data/lib/background_queue/server_lib/priority_queue.rb +156 -0
  29. data/lib/background_queue/server_lib/queue_registry.rb +123 -0
  30. data/lib/background_queue/server_lib/server.rb +314 -0
  31. data/lib/background_queue/server_lib/sorted_workers.rb +52 -0
  32. data/lib/background_queue/server_lib/task.rb +79 -0
  33. data/lib/background_queue/server_lib/task_registry.rb +51 -0
  34. data/lib/background_queue/server_lib/thread_manager.rb +121 -0
  35. data/lib/background_queue/server_lib/worker.rb +18 -0
  36. data/lib/background_queue/server_lib/worker_balancer.rb +97 -0
  37. data/lib/background_queue/server_lib/worker_client.rb +94 -0
  38. data/lib/background_queue/server_lib/worker_thread.rb +70 -0
  39. data/lib/background_queue/utils.rb +40 -0
  40. data/lib/background_queue/worker/base.rb +46 -0
  41. data/lib/background_queue/worker/calling.rb +59 -0
  42. data/lib/background_queue/worker/config.rb +35 -0
  43. data/lib/background_queue/worker/environment.rb +70 -0
  44. data/lib/background_queue/worker/worker_loader.rb +94 -0
  45. data/lib/background_queue_server.rb +21 -0
  46. data/lib/background_queue_worker.rb +5 -0
  47. data/spec/background_queue/client_lib/command_spec.rb +75 -0
  48. data/spec/background_queue/client_lib/config_spec.rb +156 -0
  49. data/spec/background_queue/client_lib/connection_spec.rb +170 -0
  50. data/spec/background_queue/client_spec.rb +95 -0
  51. data/spec/background_queue/command_spec.rb +34 -0
  52. data/spec/background_queue/config_spec.rb +134 -0
  53. data/spec/background_queue/server_lib/balanced_queue_spec.rb +122 -0
  54. data/spec/background_queue/server_lib/config_spec.rb +443 -0
  55. data/spec/background_queue/server_lib/event_connection_spec.rb +190 -0
  56. data/spec/background_queue/server_lib/event_server_spec.rb +48 -0
  57. data/spec/background_queue/server_lib/integration/full_test_spec.rb +247 -0
  58. data/spec/background_queue/server_lib/integration/queue_integration_spec.rb +98 -0
  59. data/spec/background_queue/server_lib/integration/serialize_spec.rb +127 -0
  60. data/spec/background_queue/server_lib/job_registry_spec.rb +65 -0
  61. data/spec/background_queue/server_lib/job_spec.rb +525 -0
  62. data/spec/background_queue/server_lib/owner_spec.rb +33 -0
  63. data/spec/background_queue/server_lib/priority_queue_spec.rb +182 -0
  64. data/spec/background_queue/server_lib/server_spec.rb +353 -0
  65. data/spec/background_queue/server_lib/sorted_workers_spec.rb +122 -0
  66. data/spec/background_queue/server_lib/task_registry_spec.rb +69 -0
  67. data/spec/background_queue/server_lib/task_spec.rb +20 -0
  68. data/spec/background_queue/server_lib/thread_manager_spec.rb +106 -0
  69. data/spec/background_queue/server_lib/worker_balancer_spec.rb +127 -0
  70. data/spec/background_queue/server_lib/worker_client_spec.rb +196 -0
  71. data/spec/background_queue/server_lib/worker_thread_spec.rb +125 -0
  72. data/spec/background_queue/utils_spec.rb +27 -0
  73. data/spec/background_queue/worker/base_spec.rb +35 -0
  74. data/spec/background_queue/worker/calling_spec.rb +103 -0
  75. data/spec/background_queue/worker/environment_spec.rb +67 -0
  76. data/spec/background_queue/worker/worker_loader_spec.rb +103 -0
  77. data/spec/background_queue_spec.rb +7 -0
  78. data/spec/resources/config-client.yml +7 -0
  79. data/spec/resources/config-serialize.yml +12 -0
  80. data/spec/resources/config.yml +12 -0
  81. data/spec/resources/example_worker.rb +4 -0
  82. data/spec/resources/example_worker_with_error.rb +4 -0
  83. data/spec/resources/test_worker.rb +8 -0
  84. data/spec/shared/queue_registry_shared.rb +216 -0
  85. data/spec/spec_helper.rb +15 -0
  86. data/spec/support/default_task.rb +9 -0
  87. data/spec/support/private.rb +23 -0
  88. data/spec/support/simple_server.rb +28 -0
  89. data/spec/support/simple_task.rb +58 -0
  90. data/spec/support/test_worker_server.rb +205 -0
  91. metadata +254 -0
@@ -0,0 +1,67 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper')
2
+ require 'background_queue_worker'
3
+
4
+
5
+ describe BackgroundQueue::Worker::Environment do
6
+
7
+ let(:logger) {
8
+ l = double("logger")
9
+ l.stub(:debug)
10
+ l.stub(:info)
11
+ l.stub(:error)
12
+ l.stub(:warn)
13
+ l
14
+ }
15
+
16
+ let(:params) {
17
+ {
18
+ :auth=>"ABCD",
19
+ :task=>{:params=>{:a=>"b"}, :owner_id=>'oid', :job_id=>'jid', :id=>'tid', :priority=>1}.to_json,
20
+ :server_port=>213
21
+ }
22
+ }
23
+
24
+ let(:response) {
25
+ double("resp")
26
+ }
27
+
28
+ let(:request) {
29
+ double("req", :remote_ip=>"192.168.1.1")
30
+ }
31
+
32
+ let(:controller) {
33
+ double("controller", :logger=>logger, :response=>response, :params=>params, :request=>request)
34
+ }
35
+
36
+
37
+ context "#init_params" do
38
+ it "will load task data from params" do
39
+ subject.init_params(params)
40
+ subject.params[:a].should eq("b")
41
+ subject.owner_id.should eq("oid")
42
+ subject.job_id.should eq("jid")
43
+ subject.task_id.should eq("tid")
44
+ subject.priority.should eq(1)
45
+ end
46
+
47
+ it "will error if the task definition is not valid json" do
48
+ params[:task] = '[123'
49
+ expect { subject.init_params(params) }.to raise_exception
50
+ end
51
+
52
+ it "will error if the task definition is not a json hash" do
53
+ params[:task] = '[123]'
54
+ expect { subject.init_params(params) }.to raise_exception("Invalid json root object (should be hash)")
55
+ end
56
+ end
57
+
58
+ context "#init_server_address" do
59
+ it "will get the server ip from the request" do
60
+ subject.init_server_address(controller)
61
+ subject.server_address.port.should eq(213)
62
+ subject.server_address.host.should eq("192.168.1.1")
63
+ end
64
+ end
65
+
66
+
67
+ end
@@ -0,0 +1,103 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper')
2
+ require 'background_queue_worker'
3
+
4
+ class ThisWorkerExists
5
+
6
+ end
7
+
8
+ describe BackgroundQueue::Worker::WorkerLoader do
9
+
10
+ let(:worker_entry) {
11
+ BackgroundQueue::Worker::WorkerLoader::WorkerEntry.new(:worker, :path, Time.at(10), :worker_name)
12
+ }
13
+
14
+ context "#worker_class_name" do
15
+ it "will convert to camelcase" do
16
+ subject.worker_class_name("something").should eq("Something")
17
+ subject.worker_class_name("something_else").should eq("SomethingElse")
18
+ end
19
+ end
20
+
21
+
22
+ context "#worker_path" do
23
+ it "will get the worker path using the worker name" do
24
+ BackgroundQueue::Worker::Config.worker_path = "ROOT"
25
+ subject.worker_path("something").should eq("ROOT/something.rb")
26
+ subject.worker_path("something_else").should eq("ROOT/something_else.rb")
27
+ end
28
+ end
29
+
30
+ context "#load_worker" do
31
+ it "will wrap a worker in a worker entry if the file loads and defines the worker class" do
32
+ subject.should_receive(:load_file).with(:path).and_return(true)
33
+ subject.should_receive(:load_class).with(:worker_name, :path).and_return(:clazz)
34
+ subject.should_receive(:worker_path).with(:worker_name).and_return(:path)
35
+ File.should_receive(:mtime).with(:path).and_return(:datestamp)
36
+ worker_entry = subject.load_worker(:worker_name)
37
+ worker_entry.worker.should eq(:clazz)
38
+ worker_entry.name.should eq(:worker_name)
39
+ worker_entry.datestamp.should eq(:datestamp)
40
+ worker_entry.path.should eq(:path)
41
+ end
42
+ end
43
+
44
+ context "#load_file" do
45
+ it "will load a file if it is a valid ruby file" do
46
+ path = File.expand_path(File.dirname(__FILE__) + '/../../resources/example_worker.rb')
47
+ subject.load_file(path)
48
+ end
49
+
50
+ it "will raise an error if the file does not exist" do
51
+ path = File.expand_path(File.dirname(__FILE__) + '/../../resources/example_worker_not_found.rb')
52
+ expect { subject.load_file(path) }.to raise_exception
53
+ end
54
+
55
+ it "will raise an error if the file is not valid ruby" do
56
+ path = File.expand_path(File.dirname(__FILE__) + '/../../resources/example_worker_with_error.rb')
57
+ expect { subject.load_file(path) }.to raise_exception
58
+ end
59
+ end
60
+
61
+ context "#load_class" do
62
+ it "will create an instance of the worker class" do
63
+ subject.load_class("this_worker_exists", "path").class.name.should eq("ThisWorkerExists")
64
+ end
65
+
66
+ it "will error if the class is not found" do
67
+ expect { subject.load_class("this_worker_does_not_exist", "path") }.to raise_exception("path did not define ThisWorkerDoesNotExist")
68
+ end
69
+ end
70
+
71
+ context "#reload_if_updated" do
72
+ it "will reload the worker if the filestamp has changed" do
73
+ File.should_receive(:mtime).with(:path).and_return(Time.at(11))
74
+ subject.should_receive(:load_file).with(:path).and_return(true)
75
+ subject.should_receive(:load_class).with(:worker_name).and_return(:new_worker)
76
+
77
+ subject.reload_if_updated(worker_entry)
78
+ worker_entry.worker.should eq(:new_worker)
79
+ end
80
+
81
+ it "will not reload the worker if the filestamp has not changed" do
82
+ File.should_receive(:mtime).with(:path).and_return(Time.at(10))
83
+ subject.reload_if_updated(worker_entry)
84
+ worker_entry.worker.should eq(:worker)
85
+ end
86
+ end
87
+
88
+ context "#get_worker" do
89
+ it "will load the worker if not in cache" do
90
+ subject.should_receive(:load_worker).with(:worker_name).and_return(worker_entry)
91
+ subject.get_worker(:worker_name).should eq(:worker)
92
+ end
93
+
94
+ it "will cache the results" do
95
+ subject.should_receive(:load_worker).with(:worker_name).and_return(worker_entry)
96
+ subject.should_receive(:reload_if_updated).with(worker_entry).and_return(nil)
97
+ subject.get_worker(:worker_name).should eq(:worker)
98
+ subject.get_worker(:worker_name).should eq(:worker)
99
+ end
100
+ end
101
+
102
+
103
+ end
@@ -0,0 +1,7 @@
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
@@ -0,0 +1,7 @@
1
+ development:
2
+ server:
3
+ host: 127.0.0.1
4
+ port: 3000
5
+ memcache: 127.0.0.1:9999
6
+
7
+
@@ -0,0 +1,12 @@
1
+ development:
2
+ address:
3
+ host: 127.0.0.1
4
+ port: 3000
5
+ workers:
6
+ - http://127.0.0.1:8001/background_queue
7
+ secret: this_is_used_to_make_sure_it_is_secure
8
+ connections_per_worker: 10
9
+ system_task_options:
10
+ domain: the_default_domain
11
+ task_file: /tmp/bq_tasks
12
+
@@ -0,0 +1,12 @@
1
+ development:
2
+ address:
3
+ host: 127.0.0.1
4
+ port: 3000
5
+ workers:
6
+ - http://127.0.0.1:8001/background_queue
7
+ secret: this_is_used_to_make_sure_it_is_secure
8
+ connections_per_worker: 10
9
+ system_task_options:
10
+ domain: the_default_domain
11
+
12
+
@@ -0,0 +1,4 @@
1
+ class ExampleWorker
2
+
3
+
4
+ end
@@ -0,0 +1,4 @@
1
+ classx ExampleWorker
2
+
3
+
4
+ end
@@ -0,0 +1,8 @@
1
+ class TestWorker < BackgroundQueue::Worker::Base
2
+
3
+ def run
4
+ set_progress("Not Yet..", 50)
5
+ set_progress("Not Yet..", 75)
6
+ set_progress("Done", 100)
7
+ end
8
+ end
@@ -0,0 +1,216 @@
1
+ require 'background_queue_server'
2
+
3
+ shared_examples "a queue registry" do
4
+
5
+ context "queue registry management" do
6
+
7
+ subject { new_instance }
8
+
9
+ it "will create an queue" do
10
+ subject.should_receive(:create_queue).with(:queue_id) { :the_queue}
11
+ subject.__prv__get_queue(:queue_id, true).should eq([false, :the_queue])
12
+ end
13
+
14
+ it "will find existing queue" do
15
+ subject.should_receive(:create_queue).with(:queue_id) { :the_queue}
16
+ subject.__prv__get_queue(:queue_id, true)
17
+ subject.stub(:create_queue).with(:queue_id) { :this_should_not_be_used }
18
+ subject.__prv__get_queue(:queue_id, true).should eq([true, :the_queue])
19
+ end
20
+
21
+ end
22
+
23
+
24
+ context "queue items" do
25
+ let(:queue_class) { described_class.queue_class }
26
+
27
+ let(:task1) { SimpleTask.new(:owner_id, :job_id, :task_id, 3) }
28
+ let(:task2) { SimpleTask.new(:owner_id2, :job_id, :task_id2, 2) }
29
+
30
+ before do
31
+ queue_class.any_instance.stub(:add_item) do |item|
32
+ @priority = item.priority if @priority.nil? || @priority > item.priority
33
+ end
34
+
35
+ queue_class.any_instance.stub(:priority) do
36
+ @priority
37
+ end
38
+
39
+ queue_class.any_instance.stub(:set_priority) do |priority|
40
+ @priority = priority
41
+ end
42
+
43
+ queue_class.any_instance.stub(:next_item) do
44
+ @priority += 1 #just assume we lower priority...
45
+ end
46
+ end
47
+
48
+ context "#add_item" do
49
+
50
+ context "new queue" do
51
+
52
+ subject { new_instance }
53
+
54
+ it "tracks if the queue priority changed" do
55
+ in_queue, queue = subject.__prv__get_queue(:queue_id, true)
56
+ subject.__prv__track_priority_when_adding_to_queue(queue, task1).should eq([true, nil])
57
+ end
58
+
59
+ it "will add with correct priority" do
60
+
61
+ subject.add_item(task1)
62
+ subject.__prv__get_queues.should have(1).items
63
+ subject.__prv__get_queues.first.priority.should eq(3)
64
+ end
65
+ end
66
+
67
+ context "existing queue" do
68
+
69
+ subject {
70
+ bg = new_instance
71
+ bg.add_item(task1)
72
+ bg
73
+ }
74
+
75
+ it "will re-prioritise existing queue to higher priority" do
76
+ subject.__prv__get_queues.should have(1).items
77
+ subject.__prv__get_queues.first.priority.should eq(3)
78
+ subject.add_item(SimpleTask.new(:owner_id, :job_id, :task_id2, 2))
79
+ subject.__prv__get_queues.should have(1).items
80
+ subject.__prv__get_queues.first.priority.should eq(2)
81
+ end
82
+
83
+ it "will not re-prioritise existing owner to lower priority" do
84
+ subject.__prv__get_queues.should have(1).items
85
+ subject.__prv__get_queues.first.priority.should eq(3)
86
+ subject.add_item(SimpleTask.new(:owner_id, :job_id, :task_id2, 4))
87
+ subject.__prv__get_queues.should have(1).items
88
+ subject.__prv__get_queues.first.priority.should eq(3)
89
+ end
90
+ end
91
+ end
92
+
93
+ context "#next_item" do
94
+
95
+ subject {
96
+ bg = new_instance
97
+ bg.stub(:get_queue_id_from_item) { |item| item.owner_id}
98
+ bg.add_item(task1)
99
+ bg.add_item(task2)
100
+ bg
101
+ }
102
+
103
+ it "will add to stalled items if no items left for queue" do
104
+ subject.should_receive(:remove_item_from_queue).with(any_args) { [true, 2, :task]}
105
+ queue_class.any_instance.stub(:empty?) { true }
106
+ subject.should_receive(:stall_queue)
107
+ subject.__prv__get_queue_for_priority(2, false)
108
+ subject.next_item.should eq(:task)
109
+ subject.__prv__get_queues.should have(1).items
110
+ subject.__prv__get_queues.first.priority.should eq(3)
111
+ #make sure the owner was removed
112
+ subject.__prv__get_queue(:owner_id2, true).first.should eq(false)
113
+ end
114
+
115
+
116
+
117
+ it "will lower priority when items left are lower priority" do
118
+ subject.should_receive(:remove_item_from_queue).with(any_args) { [true, 2, :task]}
119
+ queue_class.any_instance.stub(:empty?) { false }
120
+
121
+ subject.__prv__get_queue(:owner_id2, true).last.set_priority(3)
122
+
123
+ subject.next_item
124
+ subject.__prv__get_queues.should have(1).items
125
+ subject.__prv__get_queues.first.priority.should eq(3)
126
+ subject.__prv__get_queues.first.should have(2).items
127
+ end
128
+
129
+
130
+ end
131
+
132
+ context "#remove_item_from_queue" do
133
+
134
+ subject {
135
+ bg = new_instance
136
+ bg.stub(:get_queue_id_from_item) { |item| item.owner_id}
137
+ bg.add_item(task1)
138
+ bg.add_item(task2)
139
+ bg
140
+ }
141
+
142
+ it "tracks if priority changed when removing next item" do
143
+ owner = double("owner")
144
+ owner.stub(:priority) { @priority ||= 2 }
145
+ owner.stub(:next_item) { @priority += 1; :task }
146
+
147
+ subject.__prv__remove_item_from_queue(owner, :next).should eq([true, 2, :task])
148
+ end
149
+
150
+ it "tracks if priority unchanged when removing next item" do
151
+ owner = double("owner")
152
+ owner.stub(:priority) { @priority ||= 2 }
153
+ owner.stub(:next_item) {:task }
154
+
155
+ subject.__prv__remove_item_from_queue(owner, :next).should eq([false, 2, :task])
156
+ end
157
+
158
+ it "tracks if priority changed when removing specific item" do
159
+ owner = double("owner")
160
+ owner.stub(:priority) { @priority ||= 2 }
161
+ owner.stub(:remove_item) { @priority += 1; :task }
162
+
163
+ subject.__prv__remove_item_from_queue(owner, :task).should eq([true, 2, :task])
164
+ end
165
+
166
+ it "tracks if priority unchanged when removing specific item" do
167
+ owner = double("owner")
168
+ owner.stub(:priority) { @priority ||= 2 }
169
+ owner.stub(:remove_item) {:task }
170
+
171
+ subject.__prv__remove_item_from_queue(owner, :task).should eq([false, 2, :task])
172
+ end
173
+
174
+ end
175
+
176
+
177
+ context "#remove_item" do
178
+
179
+ subject {
180
+ bg = new_instance
181
+ bg.stub(:get_queue_id_from_item) { |item| item.owner_id}
182
+ bg.add_item(task1)
183
+ bg.add_item(task2)
184
+ bg
185
+ }
186
+
187
+ it "will remove the queue if no items left for queue" do
188
+ subject.should_receive(:remove_item_from_queue).with(any_args) { [true, 3, :task]}
189
+ queue_class.any_instance.stub(:empty?) { true }
190
+
191
+ subject.remove_item(task1)
192
+ subject.__prv__get_queues.should have(1).items
193
+ subject.__prv__get_queues.first.priority.should eq(2)
194
+ #make sure the owner was removed
195
+ subject.__prv__get_queue(:owner_id, false).first.should be_false
196
+ end
197
+
198
+ it "will lower priority when items left are lower priority" do
199
+
200
+ subject.add_item(SimpleTask.new(:owner_id2, :job_id, :task_id3, 3))
201
+
202
+ subject.should_receive(:remove_item_from_queue).with(any_args) { [true, 2, :task]}
203
+ queue_class.any_instance.stub(:empty?) { false }
204
+
205
+ subject.__prv__get_queue(:owner_id2, true).last.set_priority(3) #need to manually set the priority to 3 because we are mocking remove_item_from_queue
206
+
207
+ subject.remove_item(task2)
208
+ subject.__prv__get_queues.should have(1).items #both owners should be at same priority (3)
209
+ subject.__prv__get_queues.first.priority.should eq(3)
210
+ subject.__prv__get_queues.first.should have(2).items
211
+ end
212
+ end
213
+ end
214
+
215
+
216
+ end