chore-core 1.8.2 → 4.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (62) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE.txt +1 -1
  3. data/README.md +173 -150
  4. data/chore-core.gemspec +3 -3
  5. data/lib/chore.rb +31 -5
  6. data/lib/chore/cli.rb +22 -4
  7. data/lib/chore/configuration.rb +1 -1
  8. data/lib/chore/consumer.rb +54 -12
  9. data/lib/chore/fetcher.rb +12 -7
  10. data/lib/chore/hooks.rb +2 -1
  11. data/lib/chore/job.rb +19 -0
  12. data/lib/chore/manager.rb +18 -2
  13. data/lib/chore/publisher.rb +18 -2
  14. data/lib/chore/queues/filesystem/consumer.rb +126 -64
  15. data/lib/chore/queues/filesystem/filesystem_queue.rb +19 -0
  16. data/lib/chore/queues/filesystem/publisher.rb +13 -19
  17. data/lib/chore/queues/sqs.rb +22 -13
  18. data/lib/chore/queues/sqs/consumer.rb +64 -51
  19. data/lib/chore/queues/sqs/publisher.rb +26 -17
  20. data/lib/chore/strategies/consumer/batcher.rb +14 -15
  21. data/lib/chore/strategies/consumer/single_consumer_strategy.rb +5 -5
  22. data/lib/chore/strategies/consumer/threaded_consumer_strategy.rb +9 -7
  23. data/lib/chore/strategies/consumer/throttled_consumer_strategy.rb +120 -0
  24. data/lib/chore/strategies/worker/forked_worker_strategy.rb +5 -6
  25. data/lib/chore/strategies/worker/helpers/ipc.rb +87 -0
  26. data/lib/chore/strategies/worker/helpers/preforked_worker.rb +163 -0
  27. data/lib/chore/strategies/worker/helpers/work_distributor.rb +65 -0
  28. data/lib/chore/strategies/worker/helpers/worker_info.rb +13 -0
  29. data/lib/chore/strategies/worker/helpers/worker_killer.rb +40 -0
  30. data/lib/chore/strategies/worker/helpers/worker_manager.rb +183 -0
  31. data/lib/chore/strategies/worker/preforked_worker_strategy.rb +150 -0
  32. data/lib/chore/strategies/worker/single_worker_strategy.rb +35 -13
  33. data/lib/chore/unit_of_work.rb +10 -1
  34. data/lib/chore/util.rb +5 -1
  35. data/lib/chore/version.rb +3 -3
  36. data/lib/chore/worker.rb +32 -3
  37. data/spec/chore/cli_spec.rb +2 -2
  38. data/spec/chore/consumer_spec.rb +1 -5
  39. data/spec/chore/duplicate_detector_spec.rb +17 -5
  40. data/spec/chore/fetcher_spec.rb +0 -11
  41. data/spec/chore/manager_spec.rb +7 -0
  42. data/spec/chore/queues/filesystem/filesystem_consumer_spec.rb +74 -16
  43. data/spec/chore/queues/sqs/consumer_spec.rb +117 -78
  44. data/spec/chore/queues/sqs/publisher_spec.rb +49 -60
  45. data/spec/chore/queues/sqs_spec.rb +32 -41
  46. data/spec/chore/strategies/consumer/batcher_spec.rb +50 -0
  47. data/spec/chore/strategies/consumer/single_consumer_strategy_spec.rb +3 -3
  48. data/spec/chore/strategies/consumer/threaded_consumer_strategy_spec.rb +7 -6
  49. data/spec/chore/strategies/consumer/throttled_consumer_strategy_spec.rb +165 -0
  50. data/spec/chore/strategies/worker/forked_worker_strategy_spec.rb +17 -2
  51. data/spec/chore/strategies/worker/helpers/ipc_spec.rb +127 -0
  52. data/spec/chore/strategies/worker/helpers/preforked_worker_spec.rb +236 -0
  53. data/spec/chore/strategies/worker/helpers/work_distributor_spec.rb +131 -0
  54. data/spec/chore/strategies/worker/helpers/worker_info_spec.rb +14 -0
  55. data/spec/chore/strategies/worker/helpers/worker_killer_spec.rb +97 -0
  56. data/spec/chore/strategies/worker/helpers/worker_manager_spec.rb +304 -0
  57. data/spec/chore/strategies/worker/preforked_worker_strategy_spec.rb +183 -0
  58. data/spec/chore/strategies/worker/single_worker_strategy_spec.rb +25 -0
  59. data/spec/chore/worker_spec.rb +82 -14
  60. data/spec/spec_helper.rb +1 -1
  61. data/spec/support/queues/sqs/fake_objects.rb +18 -0
  62. metadata +39 -15
@@ -0,0 +1,236 @@
1
+ require 'spec_helper'
2
+
3
+ describe Chore::Strategy::PreforkedWorker do
4
+ before(:each) do
5
+ allow_any_instance_of(Chore::Strategy::PreforkedWorker).to receive(:post_fork_setup)
6
+ end
7
+
8
+ let(:preforkedworker) { Chore::Strategy::PreforkedWorker.new }
9
+ let(:socket) { double("socket") }
10
+ let(:work) { double("work") }
11
+ let(:consumer) { double("consumer") }
12
+ let(:worker) { double("worker") }
13
+ let(:config) { double("config") }
14
+ let(:queues) { double("queues") }
15
+ let(:consumer_object) { double("consumer_object") }
16
+
17
+ context '#start_worker' do
18
+ it 'should connect to the master to signal that it is ready, and process messages with the worker' do
19
+ allow(preforkedworker).to receive(:connect_to_master).and_return(socket)
20
+ expect(preforkedworker).to receive(:connect_to_master).with(socket)
21
+ allow(preforkedworker).to receive(:worker).and_return(true)
22
+ expect(preforkedworker).to receive(:worker).with(socket)
23
+ preforkedworker.start_worker(socket)
24
+ end
25
+ end
26
+
27
+ describe '#worker' do
28
+ before(:each) do
29
+ preforkedworker.instance_variable_set(:@self_read, socket)
30
+ allow(preforkedworker).to receive(:select_sockets).and_return([[socket],nil,nil])
31
+ allow(preforkedworker).to receive(:read_msg).and_return(nil)
32
+ allow(preforkedworker).to receive(:is_orphan?).and_return(false)
33
+ allow(preforkedworker).to receive(:process_work).and_return(true)
34
+ allow(preforkedworker).to receive(:signal_ready).and_return(true)
35
+ allow(work).to receive(:queue_timeout).and_return(20*60)
36
+ end
37
+
38
+
39
+ it 'should not run while @running is false' do
40
+ allow(preforkedworker).to receive(:running?).and_return(false)
41
+ expect(preforkedworker).to receive(:select_sockets).exactly(0).times
42
+ begin
43
+ preforkedworker.send(:worker, nil)
44
+ rescue SystemExit=>e
45
+ expect(e.status).to eq(0)
46
+ end
47
+ end
48
+
49
+ it 'should be able to handle timeouts on readable sockets' do
50
+ allow(preforkedworker).to receive(:running?).and_return(true, true, false)
51
+ allow(preforkedworker).to receive(:select_sockets).and_return(nil)
52
+ expect(preforkedworker).to receive(:select_sockets).exactly(2).times
53
+ begin
54
+ preforkedworker.send(:worker, nil)
55
+ rescue SystemExit=>e
56
+ expect(e.status).to eq(0)
57
+ end
58
+ end
59
+
60
+ it 'should read a message if the connection is readable' do
61
+ allow(preforkedworker).to receive(:running?).and_return(true, false)
62
+ expect(preforkedworker).to receive(:read_msg).once
63
+ begin
64
+ preforkedworker.send(:worker, nil)
65
+ rescue SystemExit=>e
66
+ expect(e.status).to eq(0)
67
+ end
68
+ end
69
+
70
+ it 'should check if the master is alive, and if not, it should end' do
71
+ allow(preforkedworker).to receive(:running).and_return(true)
72
+ allow(preforkedworker).to receive(:select_sockets).and_return([[socket],nil,nil])
73
+ allow(preforkedworker).to receive(:read_msg).and_return(nil)
74
+ allow(preforkedworker).to receive(:is_orphan?).and_return(true)
75
+ begin
76
+ preforkedworker.send(:worker, socket)
77
+ rescue SystemExit=>e
78
+ expect(e.status).to eq(0)
79
+ end
80
+ end
81
+
82
+ it 'should process work if it is read from the master and signal ready' do
83
+ allow(preforkedworker).to receive(:running?).and_return(true, false)
84
+ allow(preforkedworker).to receive(:read_msg).and_return(work)
85
+ expect(preforkedworker).to receive(:process_work).once
86
+ expect(preforkedworker).to receive(:signal_ready).once
87
+ begin
88
+ preforkedworker.send(:worker, socket)
89
+ rescue SystemExit=>e
90
+ expect(e.status).to eq(0)
91
+ end
92
+ end
93
+
94
+ it 'should exit the process if the connection to master is closed' do
95
+ allow(preforkedworker).to receive(:running?).and_return(true)
96
+ allow(preforkedworker).to receive(:read_msg).and_raise(Errno::ECONNRESET)
97
+ begin
98
+ preforkedworker.send(:worker, socket)
99
+ rescue SystemExit=>e
100
+ expect(e.status).to eq(0)
101
+ end
102
+ end
103
+ end
104
+
105
+ context '#connect_to_master' do
106
+ it 'should create a connection to the master, and send it its PID and a ready message' do
107
+ allow(preforkedworker).to receive(:child_connection).and_return(socket)
108
+ allow(preforkedworker).to receive(:send_msg).and_return(true)
109
+ allow(preforkedworker).to receive(:signal_ready).and_return(true)
110
+
111
+ expect(preforkedworker).to receive(:child_connection).once
112
+ expect(preforkedworker).to receive(:send_msg).once
113
+ expect(preforkedworker).to receive(:signal_ready).once
114
+
115
+ res = preforkedworker.send(:connect_to_master,socket)
116
+
117
+ expect(res).to eq(socket)
118
+ end
119
+ end
120
+
121
+ context '#post_fork_setup' do
122
+ before(:each) do
123
+ allow(preforkedworker).to receive(:procline).and_return(true)
124
+ allow(preforkedworker).to receive(:trap_signals)
125
+ allow_any_instance_of(Chore::Strategy::PreforkedWorker).to receive(:post_fork_setup).and_call_original
126
+ end
127
+
128
+ it 'should change the process name' do
129
+ expect(preforkedworker).to receive(:procline)
130
+ preforkedworker.send(:post_fork_setup)
131
+ end
132
+
133
+ it 'should trap new relevant signals' do
134
+ expect(preforkedworker).to receive(:trap_signals)
135
+ preforkedworker.send(:post_fork_setup)
136
+ end
137
+
138
+ it "should run after_fork hooks" do
139
+ hook_called = false
140
+ Chore.add_hook(:after_fork) { hook_called = true }
141
+ preforkedworker.send(:post_fork_setup)
142
+ expect(hook_called).to be true
143
+ end
144
+ end
145
+
146
+ context '#process_work' do
147
+ before(:each) do
148
+ allow(preforkedworker).to receive(:consumer).and_return(consumer)
149
+ allow(work).to receive(:queue_name).and_return("test_queue")
150
+ allow(work).to receive(:consumer=)
151
+ allow(work).to receive(:queue_timeout).and_return(10)
152
+ allow(Chore::Worker).to receive(:new).and_return(worker)
153
+ allow(worker).to receive(:start)
154
+ end
155
+
156
+ it 'should fetch the consumer object associated with the queue' do
157
+ expect(preforkedworker).to receive(:consumer)
158
+ preforkedworker.send(:process_work, [work])
159
+ end
160
+
161
+ it 'should create and start a worker object with the job sent to it' do
162
+ expect(worker).to receive(:start)
163
+ preforkedworker.send(:process_work, [work])
164
+ end
165
+
166
+ it 'should timeout if the work runs for longer than the queue timeout' do
167
+ allow(work).to receive(:queue_timeout).and_return(1)
168
+ allow(worker).to receive(:start) { sleep 5 }
169
+ begin
170
+ preforkedworker.send(:process_work, [work])
171
+ rescue => e
172
+ expect(e.class).to eq(Timeout::Error)
173
+ end
174
+ end
175
+ end
176
+
177
+ context '#consumer' do
178
+ before(:each) do
179
+ preforkedworker.instance_variable_set(:@consumer_cache, {key_1: :value_1})
180
+ allow(Chore).to receive(:config).and_return(config)
181
+ allow(config).to receive(:queues).and_return(queues)
182
+ allow(queues).to receive(:size).and_return(2)
183
+ allow(config).to receive(:consumer).and_return(consumer)
184
+ allow(consumer).to receive(:new).and_return(:value_2)
185
+ end
186
+
187
+ it 'should fetch a consumer object if it was created previously for this queue' do
188
+ preforkedworker.instance_variable_set(:@consumer_cache, {key_1: :value_1})
189
+ res = preforkedworker.send(:consumer,:key_1)
190
+ expect(res).to eq(:value_1)
191
+ end
192
+
193
+ it 'should create and return a new consumer object if one does not exist for this queue' do
194
+ preforkedworker.instance_variable_set(:@consumer_cache, {})
195
+ expect(consumer).to receive(:new)
196
+ res = preforkedworker.send(:consumer,:key_2)
197
+ expect(res).to eq(:value_2)
198
+ end
199
+ end
200
+
201
+ context '#trap_signals' do
202
+ it 'should reset signals' do
203
+ allow(Chore::Signal).to receive(:reset)
204
+ allow(Chore::Signal).to receive(:trap)
205
+ expect(Chore::Signal).to receive(:reset)
206
+ preforkedworker.send(:trap_signals)
207
+ end
208
+
209
+ it 'should trap the signals passed to it' do
210
+ allow(Chore::Signal).to receive(:reset)
211
+ allow(Chore::Signal).to receive(:trap)
212
+ expect(Chore::Signal).to receive(:trap).with(:INT).once
213
+ expect(Chore::Signal).to receive(:trap).with(:QUIT).once
214
+ expect(Chore::Signal).to receive(:trap).with(:TERM).once
215
+ expect(Chore::Signal).to receive(:trap).with(:USR1).once
216
+ preforkedworker.send(:trap_signals)
217
+ end
218
+ end
219
+
220
+ context '#is_orphan?' do
221
+ it 'should return true if the parent is dead' do
222
+ allow(Process).to receive(:ppid).and_return(10)
223
+ preforkedworker.instance_variable_set(:@manager_pid, 9)
224
+ res = preforkedworker.send(:is_orphan?)
225
+ expect(res).to eq(true)
226
+ end
227
+
228
+ it 'should return false if the parent is alive' do
229
+ allow(Process).to receive(:ppid).and_return(10)
230
+ preforkedworker.instance_variable_set(:@manager_pid, 10)
231
+ res = preforkedworker.send(:is_orphan?)
232
+ expect(res).to eq(false)
233
+ end
234
+ end
235
+ end
236
+
@@ -0,0 +1,131 @@
1
+ require 'spec_helper'
2
+ require 'chore/strategies/worker/helpers/work_distributor'
3
+
4
+ describe Chore::Strategy::WorkDistributor do
5
+ let(:timestamp) { Time.now }
6
+ let(:manager) { double('manager') }
7
+ let(:worker) { Chore::Strategy::WorkerInfo.new(1) }
8
+ let(:consumer) { double('consumer') }
9
+ let(:job) do
10
+ Chore::UnitOfWork.new(
11
+ SecureRandom.uuid,
12
+ 'test',
13
+ 60,
14
+ Chore::Encoder::JsonEncoder.encode(TestJob.job_hash([1,2,"3"])),
15
+ 0
16
+ )
17
+ end
18
+ let(:socket) { double('socket') }
19
+
20
+ context '#include_ipc' do
21
+ it 'should include the Ipc module' do
22
+ expect(described_class.ipc_help).to eq(:available)
23
+ end
24
+ end
25
+
26
+ context '#fetch_and_assign_jobs' do
27
+ it 'should fetch jobs from the consumer' do
28
+ allow(described_class).to receive(:assign_jobs).and_return(true)
29
+ allow(manager).to receive(:fetch_work).with(1).and_return([job])
30
+ allow(manager).to receive(:return_work)
31
+ expect(manager).to receive(:fetch_work).with(1)
32
+ described_class.fetch_and_assign_jobs([worker], manager)
33
+ end
34
+
35
+ it 'should assign the fetched jobs to the workers' do
36
+ allow(manager).to receive(:fetch_work).with(1).and_return([job])
37
+ allow(manager).to receive(:return_work)
38
+ expect(described_class).to receive(:assign_jobs).with([job], [worker])
39
+ described_class.fetch_and_assign_jobs([worker], manager)
40
+ end
41
+
42
+ it 'should not return any work' do
43
+ allow(described_class).to receive(:assign_jobs).and_return([])
44
+ allow(manager).to receive(:fetch_work).with(1).and_return([job])
45
+ expect(manager).to receive(:return_work).with([])
46
+ described_class.fetch_and_assign_jobs([worker], manager)
47
+ end
48
+
49
+ it 'should raise and exception if it does not get an array from the manager' do
50
+ allow(manager).to receive(:fetch_work).with(1).and_return(nil)
51
+ allow(manager).to receive(:return_work)
52
+ expect { described_class.fetch_and_assign_jobs([worker], manager) }.to raise_error("DW: jobs needs to be a list got NilClass")
53
+ end
54
+
55
+ it 'should sleep if no jobs are available' do
56
+ expect(described_class).to receive(:sleep).with(0.1)
57
+ allow(manager).to receive(:fetch_work).with(1).and_return([])
58
+ allow(manager).to receive(:return_work)
59
+ described_class.fetch_and_assign_jobs([worker], manager)
60
+ end
61
+ end
62
+
63
+ context '#assign_jobs' do
64
+ it 'should raise an exception if we have no free workers' do
65
+ expect { described_class.send(:assign_jobs, [job], []) }.to raise_error('DW: assign_jobs got 0 workers')
66
+ end
67
+
68
+ it 'should remove the consumer object from the job object' do
69
+ allow(described_class).to receive(:push_job_to_worker).and_return(true)
70
+
71
+ described_class.send(:assign_jobs, [job], [worker])
72
+ end
73
+
74
+ it 'should raise an exception if more jobs than workers are provided to it' do
75
+ allow(described_class).to receive(:push_job_to_worker).and_return(true)
76
+
77
+ expect { described_class.send(:assign_jobs, [job, job], [worker]) }.to raise_error('DW: More Jobs than Sockets')
78
+ end
79
+
80
+ it 'should send the job object to the free worker' do
81
+ allow(described_class).to receive(:push_job_to_worker).and_return(true)
82
+
83
+ expect(described_class).to receive(:push_job_to_worker).with(job, worker)
84
+ described_class.send(:assign_jobs, [job], [worker])
85
+ end
86
+
87
+ it 'should return jobs that failed to be assigned' do
88
+ job2 = Chore::UnitOfWork.new(
89
+ SecureRandom.uuid,
90
+ 'test',
91
+ 60,
92
+ Chore::Encoder::JsonEncoder.encode(TestJob.job_hash([1,2,"3"])),
93
+ 0
94
+ )
95
+ worker2 = Chore::Strategy::WorkerInfo.new(2)
96
+
97
+ allow(described_class).to receive(:push_job_to_worker).and_return(true, false)
98
+
99
+ expect(described_class).to receive(:push_job_to_worker).with(job, worker)
100
+ expect(described_class).to receive(:push_job_to_worker).with(job2, worker2)
101
+ unassigned_jobs = described_class.send(:assign_jobs, [job, job2], [worker, worker2])
102
+ expect(unassigned_jobs).to eq([job2])
103
+ end
104
+ end
105
+
106
+ context '#push_job_to_worker' do
107
+ before(:each) do
108
+ allow(described_class).to receive(:clear_ready).with(worker.socket).and_return(true)
109
+ allow(described_class).to receive(:send_msg).with(worker.socket, job).and_return(true)
110
+ end
111
+
112
+ it 'should clear all signals from the worker' do
113
+ expect(described_class).to receive(:clear_ready).with(worker.socket)
114
+ described_class.send(:push_job_to_worker, job, worker)
115
+ end
116
+
117
+ it 'should send the job as a message on the worker' do
118
+ expect(described_class).to receive(:send_msg).with(worker.socket, job)
119
+ described_class.send(:push_job_to_worker, job, worker)
120
+ end
121
+
122
+ it 'should return true if job successfully sent' do
123
+ expect(described_class.send(:push_job_to_worker, job, worker)).to eq(true)
124
+ end
125
+
126
+ it 'should return false if job failed to send' do
127
+ expect(described_class).to receive(:send_msg).and_raise(StandardError)
128
+ expect(described_class.send(:push_job_to_worker, job, worker)).to eq(false)
129
+ end
130
+ end
131
+ end
@@ -0,0 +1,14 @@
1
+ require 'spec_helper'
2
+
3
+ describe Chore::Strategy::WorkerInfo do
4
+ let(:pid) { 1 }
5
+ let(:worker_info) { Chore::Strategy::WorkerInfo.new(pid) }
6
+
7
+ context '#initialize' do
8
+ it 'should initialize the WorkerInfo with a pid' do
9
+ wi = Chore::Strategy::WorkerInfo.new(pid)
10
+ expect(wi.pid).to equal(pid)
11
+ expect(wi.socket).to equal(nil)
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,97 @@
1
+ require 'spec_helper'
2
+ require 'get_process_mem'
3
+
4
+ describe Chore::Strategy::WorkerKiller do
5
+ let(:memory_limit) { 1024 }
6
+ let(:request_limit) { 100 }
7
+ let(:check_cycle) { 16 }
8
+ let(:worker_killer) { Chore::Strategy::WorkerKiller.new }
9
+ let(:process_mem_obj) { double('process_mem_obj', bytes: 10) }
10
+
11
+ context '#initialize' do
12
+ it 'should initialize the WorkerKiller correctly' do
13
+ allow(Chore.config).to receive(:memory_limit_bytes).and_return(memory_limit)
14
+ allow(Chore.config).to receive(:request_limit).and_return(request_limit)
15
+ allow(Chore.config).to receive(:check_cycle).and_return(check_cycle)
16
+
17
+ wk = Chore::Strategy::WorkerKiller.new
18
+ expect(wk.instance_variable_get(:@memory_limit)).to equal(memory_limit)
19
+ expect(wk.instance_variable_get(:@request_limit)).to equal(request_limit)
20
+ expect(wk.instance_variable_get(:@check_cycle)).to equal(check_cycle)
21
+ expect(wk.instance_variable_get(:@check_count)).to equal(0)
22
+ expect(wk.instance_variable_get(:@current_requests)).to equal(0)
23
+ end
24
+ end
25
+
26
+ context '#check_memory' do
27
+ before(:each) do
28
+ allow(GetProcessMem).to receive(:new).and_return(process_mem_obj)
29
+ worker_killer.instance_variable_set(:@memory_limit, memory_limit)
30
+ worker_killer.instance_variable_set(:@check_cycle, check_cycle)
31
+ end
32
+
33
+ it 'should return nil when memory_limit is nil' do
34
+ worker_killer.instance_variable_set(:@memory_limit, nil)
35
+ expect(worker_killer.check_memory).to eq(nil)
36
+ end
37
+
38
+ it 'should increment the check count by 1' do
39
+ worker_killer.instance_variable_set(:@check_count, 1)
40
+ worker_killer.check_memory
41
+ expect(worker_killer.instance_variable_get(:@check_count)).to eq(2)
42
+ end
43
+
44
+ context 'check_count equals check_cycle' do
45
+ before(:each) do
46
+ worker_killer.instance_variable_set(:@check_count, 15)
47
+ end
48
+
49
+ it 'should check memory' do
50
+ expect(process_mem_obj).to receive(:bytes)
51
+ worker_killer.check_memory
52
+ end
53
+
54
+ it 'should reset the check_count to zero' do
55
+ allow(process_mem_obj).to receive(:bytes).and_return(0)
56
+ worker_killer.check_memory
57
+ expect(worker_killer.instance_variable_get(:@check_count)).to equal(0)
58
+ end
59
+
60
+ it 'should exit if the process mem exceeds the memory_limit' do
61
+ allow(process_mem_obj).to receive(:bytes).and_return(2048)
62
+ begin
63
+ worker_killer.check_memory
64
+ rescue SystemExit=>e
65
+ expect(e.status).to eq(0)
66
+ end
67
+ end
68
+ end
69
+ end
70
+
71
+ context '#check_requests' do
72
+ before(:each) do
73
+ worker_killer.instance_variable_set(:@request_limit, request_limit)
74
+ worker_killer.instance_variable_set(:@current_requests, 0)
75
+ end
76
+
77
+ it 'should return nil when request_limit is nil' do
78
+ worker_killer.instance_variable_set(:@request_limit, nil)
79
+ expect(worker_killer.check_requests).to eq(nil)
80
+ end
81
+
82
+ it 'should increment current requests' do
83
+ worker_killer.check_requests
84
+ expect(worker_killer.instance_variable_get(:@current_requests)).to eq(1)
85
+ end
86
+
87
+ it 'should exit when current_requests exceeds request_limit' do
88
+ worker_killer.instance_variable_set(:@current_requests, request_limit - 1)
89
+
90
+ begin
91
+ worker_killer.check_requests
92
+ rescue SystemExit=>e
93
+ expect(e.status).to eq(0)
94
+ end
95
+ end
96
+ end
97
+ end