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,122 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper')
2
+ require 'background_queue_server'
3
+
4
+
5
+ class BasicWorker
6
+ attr_accessor :connections
7
+ def initialize(connections)
8
+ @connections = connections
9
+ end
10
+ end
11
+
12
+ describe BackgroundQueue::ServerLib::SortedWorkers do
13
+
14
+
15
+
16
+ context "#add_worker" do
17
+ it "adds the worker to the correct position in list" do
18
+ w1 = BasicWorker.new(1)
19
+ w2 = BasicWorker.new(2)
20
+ w3 = BasicWorker.new(3)
21
+ w4 = BasicWorker.new(0)
22
+ subject.add_worker(w1)
23
+ subject.add_worker(w2)
24
+ subject.add_worker(w3)
25
+ subject.add_worker(w4)
26
+ subject.worker_list.should eq([w4, w1, w2, w3])
27
+ end
28
+ end
29
+
30
+ context "#remove_worker" do
31
+ it "just removed the worker from the array" do
32
+ w1 = double("worker")
33
+ subject.add_worker(w1)
34
+ subject.worker_list[0].should be(w1)
35
+ subject.remove_worker(w1)
36
+ subject.worker_list.should have(0).items
37
+ end
38
+ end
39
+
40
+ context "#adjust_worker" do
41
+ it "moves the worker forward if it now has less connections to the worker before it" do
42
+ w1 = BasicWorker.new(1)
43
+ w2 = BasicWorker.new(1)
44
+
45
+ subject.add_worker(w1)
46
+ subject.add_worker(w2)
47
+ w2.connections = 0
48
+
49
+ subject.adjust_worker(w2)
50
+ subject.worker_list.should eq([w2, w1])
51
+ end
52
+
53
+ it "moves the worker back if it now has more connections to the worker after it" do
54
+ w1 = BasicWorker.new(1)
55
+ w2 = BasicWorker.new(2)
56
+
57
+ subject.add_worker(w1)
58
+ subject.add_worker(w2)
59
+
60
+ w1.connections = 3
61
+ subject.adjust_worker(w1)
62
+ subject.worker_list.should eq([w2, w1])
63
+ end
64
+
65
+ it "leaves the worker where it is if its positions hasnt changed" do
66
+ w1 = BasicWorker.new(1)
67
+ w2 = BasicWorker.new(2)
68
+ w3 = BasicWorker.new(3)
69
+
70
+ subject.add_worker(w1)
71
+ subject.add_worker(w2)
72
+ subject.add_worker(w3)
73
+ subject.worker_list.should eq([w1,w2,w3])
74
+
75
+ subject.adjust_worker(w2)
76
+ subject.worker_list.should eq([w1,w2,w3])
77
+
78
+ end
79
+
80
+ it "leaves where where it is if connection counts are the same" do
81
+ w1 = BasicWorker.new(1)
82
+ w2 = BasicWorker.new(2)
83
+ w3 = BasicWorker.new(3)
84
+ w4 = BasicWorker.new(4)
85
+
86
+ subject.add_worker(w1)
87
+ subject.add_worker(w2)
88
+ subject.add_worker(w3)
89
+ subject.add_worker(w4)
90
+ subject.worker_list.should eq([w1,w2,w3,w4])
91
+
92
+ w2.connections = 3
93
+ subject.adjust_worker(w2)
94
+ subject.worker_list.should eq([w1,w2,w3,w4])
95
+ w2.connections = 1
96
+ subject.adjust_worker(w2)
97
+ subject.worker_list.should eq([w1,w2,w3,w4])
98
+ end
99
+
100
+ it "can jump multiple places" do
101
+ w1 = BasicWorker.new(1)
102
+ w2 = BasicWorker.new(2)
103
+ w3 = BasicWorker.new(2)
104
+ w4 = BasicWorker.new(2)
105
+
106
+ subject.add_worker(w1)
107
+ subject.add_worker(w2)
108
+ subject.add_worker(w3)
109
+ subject.add_worker(w4)
110
+ subject.worker_list.should eq([w1,w4,w3,w2])
111
+
112
+ w2.connections = 3
113
+ subject.adjust_worker(w2)
114
+ subject.worker_list.should eq([w1,w4,w3,w2])
115
+ w3.connections = 1
116
+ subject.adjust_worker(w3)
117
+ subject.worker_list.should eq([w1,w3,w4,w2])
118
+ end
119
+ end
120
+
121
+
122
+ end
@@ -0,0 +1,69 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper')
2
+ require 'background_queue_server'
3
+
4
+
5
+ describe BackgroundQueue::ServerLib::TaskRegistry do
6
+
7
+ let(:task) { SimpleTask.new(:owner_id, :job_id, :task_id, 3) }
8
+ let(:task2) { SimpleTask.new(:owner_id, :job_id, :task_id, 3) }
9
+
10
+ context "#register_waiting_task" do
11
+ it "will add task to waiting list" do
12
+ subject.register_waiting_task(task)
13
+ subject.waiting_tasks[:task_id].should eq(task)
14
+ end
15
+
16
+ it "will remove and existing waiting task with same id" do
17
+ subject.register_waiting_task(task)
18
+ subject.register_waiting_task(task2)
19
+ subject.waiting_tasks[:task_id].should_not eq(task)
20
+ subject.waiting_tasks[:task_id].should eq(task2)
21
+ end
22
+ end
23
+
24
+ context "#get_waiting_task" do
25
+ it "will get a matching task and remove it from the list" do
26
+ subject.register_waiting_task(task)
27
+ subject.waiting_tasks.should have(1).item
28
+ subject.get_waiting_task(:task_id).should eq(task)
29
+ subject.waiting_tasks.should have(0).items
30
+ end
31
+
32
+ it "will return nil if not waiting task" do
33
+ subject.get_waiting_task(:task_id).should eq(nil)
34
+ end
35
+ end
36
+
37
+ context "#register" do
38
+ it "will add the task if it does not exist and return :new" do
39
+ subject.register(task).should eq([:new, nil])
40
+ subject.tasks.should have(1).item
41
+ end
42
+
43
+ it "will add the task to waiting list if running and return :waiting" do
44
+ subject.register(task)
45
+ task.running = true
46
+ subject.register(task2).should eq([:waiting, nil])
47
+ end
48
+
49
+ it "will replace an existing task and return the existing task" do
50
+ subject.register(task)
51
+ subject.register(task2).should eq([:existing, task])
52
+ end
53
+ end
54
+
55
+ context "#de_register" do
56
+ it "will remove the task from the list of tasks and return nil if none are waiting" do
57
+ subject.register(task)
58
+ subject.de_register(task.id).should eq(nil)
59
+ end
60
+
61
+ it "will remove the task from the list of tasks and return waiting task if none are waiting" do
62
+ subject.register(task)
63
+ task.running = true
64
+ subject.register(task2)
65
+ subject.de_register(task.id).should eq(task2)
66
+ subject.tasks[task.id].should eq(task2)
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,20 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper')
2
+ require 'background_queue_server'
3
+
4
+
5
+ describe BackgroundQueue::ServerLib::Task do
6
+
7
+ subject { BackgroundQueue::ServerLib::Task.new(:owner_id, :job_id, :id, :priority, :worker, :params, {}) }
8
+
9
+ context "#set_worker_status" do
10
+ it "calls the jobs set_worker_status" do
11
+ job = double("job")
12
+ status = {}
13
+ job.should_receive(:set_worker_status).with({:task_id=>:id, :exclude=>false}).and_return(:something)
14
+ subject.set_job(job)
15
+ subject.set_worker_status(status).should eq(:something)
16
+ end
17
+ end
18
+
19
+
20
+ end
@@ -0,0 +1,106 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper')
2
+ require 'background_queue_server'
3
+
4
+
5
+ describe BackgroundQueue::ServerLib::ThreadManager do
6
+
7
+
8
+ let(:server) {
9
+ SimpleServer.new
10
+ }
11
+
12
+ subject { BackgroundQueue::ServerLib::ThreadManager.new(server, 5) }
13
+
14
+ context "#protect_access" do
15
+ it "will grab a mutex and serialize access" do
16
+ Mutex.any_instance.should_receive(:synchronize).and_yield
17
+ subject.protect_access {
18
+ 5
19
+ }.should eq(5)
20
+ end
21
+ end
22
+
23
+
24
+ context "#control_access" do
25
+ it "will serialize access then execute block if concurrent number of threads is below threshold" do
26
+ subject.control_access {
27
+ 5
28
+ }.should eq(5)
29
+ end
30
+
31
+ it "will wait on condition if number of threads is above threshold" do
32
+ subject.max_threads = 0
33
+ ConditionVariable.any_instance.should_receive(:wait).and_return(nil)
34
+ subject.control_access {
35
+ 5
36
+ }.should eq(5)
37
+ end
38
+
39
+ end
40
+
41
+ context "#signal_access" do
42
+ it "will signal a waiting thread if concurrent number of threads is below threshold" do
43
+ ConditionVariable.any_instance.should_receive(:signal).and_return(nil)
44
+ subject.signal_access
45
+ end
46
+
47
+ it "will do nothing is number of threads is above threshold" do
48
+ subject.max_threads = 0
49
+ ConditionVariable.any_instance.should_not_receive(:signal)
50
+ subject.signal_access
51
+ end
52
+ end
53
+
54
+ context "#wait_on_access" do
55
+ it "will wait on the condition" do
56
+ ConditionVariable.any_instance.should_receive(:wait).and_return(nil)
57
+ subject.wait_on_access
58
+ end
59
+ end
60
+
61
+ context "#change_concurrency" do
62
+ it "will do nothing if the concurrency is reduced" do
63
+ ConditionVariable.any_instance.should_not_receive(:signal)
64
+ subject.change_concurrency(0)
65
+ subject.max_threads.should eq(0)
66
+ end
67
+
68
+ it "will wake threads when concurrency is increased" do
69
+ ConditionVariable.any_instance.should_receive(:signal).twice
70
+ subject.change_concurrency(7)
71
+ subject.max_threads.should eq(7)
72
+ end
73
+ end
74
+
75
+ context "#start" do
76
+ it "will start a number of threads and call run on a worker thread object" do
77
+
78
+
79
+ subject.max_threads = 2
80
+ runner = double("runner")
81
+ #runner.should_receive(:run).twice
82
+
83
+ Thread.should_receive(:new).twice #.and_yield(runner)
84
+
85
+ BackgroundQueue::ServerLib::WorkerThread.should_receive(:new).twice.and_return(runner)
86
+
87
+ subject.start(BackgroundQueue::ServerLib::WorkerThread)
88
+ subject.running_threads.should eq(2)
89
+ end
90
+ end
91
+
92
+ #hmmm.. after refactor this is tricky to test...
93
+ #context "#wait" do
94
+ # it "will wait for each thread to finish" do
95
+ # thread = double("thread")
96
+ # thread.should_receive(:join).twice
97
+ # Thread.should_receive(:new).twice.and_return(thread)
98
+ # subject.max_threads = 2
99
+ # subject.start(BackgroundQueue::ServerLib::WorkerThread)
100
+ # subject.running_threads.should eq(2)
101
+ # subject.wait
102
+ # subject.running_threads.should eq(0)
103
+ # end
104
+ #end
105
+
106
+ end
@@ -0,0 +1,127 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper')
2
+ require 'background_queue_server'
3
+
4
+
5
+ describe BackgroundQueue::ServerLib::WorkerBalancer do
6
+
7
+
8
+
9
+ context "#initialize" do
10
+ it "loads the workers from the config" do
11
+ config = BackgroundQueue::ServerLib::Config.new([double("wc", :uri=>:worker_1), double("wc", :uri=>:worker_2)], :secret, :memcache, :address, :cons, :sopts, :task_file)
12
+ server = SimpleServer.new(:config=>config)
13
+
14
+ balancer = BackgroundQueue::ServerLib::WorkerBalancer.new(server)
15
+ balancer.available_workers.worker_list[0].uri.should eq(:worker_1)
16
+ balancer.available_workers.worker_list[1].uri.should eq(:worker_2)
17
+ end
18
+ end
19
+
20
+ context "#initialized" do
21
+
22
+ let(:config) {
23
+ BackgroundQueue::ServerLib::Config.new([double("wc", :uri=>:worker_1), double("wc", :uri=>:worker_2), double("w3", :uri=>:worker_3)], :secret, :memcache, :address, :cons, :sopts, :task_file)
24
+ }
25
+ subject { BackgroundQueue::ServerLib::WorkerBalancer.new(SimpleServer.new(:config=>config)) }
26
+
27
+
28
+ context "#register_start" do
29
+ it "will increment the running count for the worker and reposition it" do
30
+ w1 = subject.available_workers.worker_list[0]
31
+ w1.connections.should eq(0)
32
+ subject.__prv__register_start(w1)
33
+ w1.connections.should eq(1)
34
+ subject.available_workers.worker_list[0].should_not be(w1)
35
+ subject.available_workers.worker_list.last.should be(w1)
36
+ end
37
+ end
38
+
39
+ context "#register_finish" do
40
+ it "will decrement the running count for the worker and reposition it" do
41
+ w1 = subject.available_workers.worker_list.last
42
+ w1.connections.should eq(0)
43
+ subject.__prv__register_finish(w1)
44
+ w1.connections.should eq(-1)
45
+ subject.available_workers.worker_list.last.should_not be(w1)
46
+ subject.available_workers.worker_list.first.should be(w1)
47
+ end
48
+ end
49
+
50
+ context "#register_offline" do
51
+ it "will remove the worker from the available workers and add it to offline workers" do
52
+ w1 = subject.available_workers.worker_list[0]
53
+ subject.__prv__register_offline(w1)
54
+ w1.offline?.should be_true
55
+ subject.available_workers.worker_list[0].should_not be(w1)
56
+ subject.offline_workers.first.should be(w1)
57
+ end
58
+
59
+ it "will will not double add workers to offline list" do
60
+ w1 = subject.available_workers.worker_list[0]
61
+ subject.__prv__register_offline(w1)
62
+ subject.__prv__register_offline(w1)
63
+ subject.offline_workers.should have(1).item
64
+ end
65
+ end
66
+
67
+ context "#register_online" do
68
+ it "will add the worker back into the available workers list" do
69
+ w1 = subject.available_workers.worker_list[0]
70
+ subject.__prv__register_offline(w1)
71
+ w1.offline?.should be_true
72
+ subject.available_workers.worker_list[0].should_not be(w1)
73
+ subject.offline_workers.first.should be(w1)
74
+
75
+ subject.__prv__register_online(w1)
76
+ w1.offline?.should be_false
77
+ subject.available_workers.worker_list[0].should be(w1)
78
+ subject.offline_workers.should have(0).items
79
+ end
80
+ end
81
+
82
+ context "#build_poll_task" do
83
+ it "creates a fake task using itself as the job" do
84
+ task = subject.__prv__build_poll_task
85
+ task.worker.should eq(:poll_worker)
86
+ subject.__prv__build_poll_task.should be(task)
87
+ end
88
+ end
89
+
90
+ context "#check_offline" do
91
+ it "will poll the workers" do
92
+ w1 = subject.available_workers.worker_list[0]
93
+ subject.__prv__register_offline(w1)
94
+ BackgroundQueue::ServerLib::WorkerClient.any_instance.should_receive(:send_request).with(w1, subject.__prv__build_poll_task, :secret ).and_return(true)
95
+ subject.should_receive(:register_online).with(w1)
96
+ subject.check_offline
97
+
98
+ end
99
+ end
100
+
101
+ context "#get_next_worker" do
102
+ it "will get the worker with the least number of active connections" do
103
+ w1 = subject.available_workers.worker_list[0]
104
+ subject.should_receive(:register_start).with(w1)
105
+ subject.get_next_worker.should be(w1)
106
+ end
107
+
108
+ it "will return nil if no workers available" do
109
+ subject.available_workers.worker_list[0] = nil
110
+ subject.get_next_worker.should be_nil
111
+ end
112
+ end
113
+
114
+ context "#finish_using_worker" do
115
+ it "will not register offline if online" do
116
+ subject.should_not_receive(:register_offline).with(:worker)
117
+ subject.should_receive(:register_finish).with(:worker)
118
+ subject.finish_using_worker(:worker, true)
119
+ end
120
+
121
+ it "will register offline if not online" do
122
+ subject.should_receive(:register_offline).with(:worker)
123
+ subject.finish_using_worker(:worker, false)
124
+ end
125
+ end
126
+ end
127
+ end