chore-core 1.8.2 → 4.0.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 (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