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.
- checksums.yaml +4 -4
- data/LICENSE.txt +1 -1
- data/README.md +173 -150
- data/chore-core.gemspec +3 -3
- data/lib/chore.rb +31 -5
- data/lib/chore/cli.rb +22 -4
- data/lib/chore/configuration.rb +1 -1
- data/lib/chore/consumer.rb +54 -12
- data/lib/chore/fetcher.rb +12 -7
- data/lib/chore/hooks.rb +2 -1
- data/lib/chore/job.rb +19 -0
- data/lib/chore/manager.rb +18 -2
- data/lib/chore/publisher.rb +18 -2
- data/lib/chore/queues/filesystem/consumer.rb +126 -64
- data/lib/chore/queues/filesystem/filesystem_queue.rb +19 -0
- data/lib/chore/queues/filesystem/publisher.rb +13 -19
- data/lib/chore/queues/sqs.rb +22 -13
- data/lib/chore/queues/sqs/consumer.rb +64 -51
- data/lib/chore/queues/sqs/publisher.rb +26 -17
- data/lib/chore/strategies/consumer/batcher.rb +14 -15
- data/lib/chore/strategies/consumer/single_consumer_strategy.rb +5 -5
- data/lib/chore/strategies/consumer/threaded_consumer_strategy.rb +9 -7
- data/lib/chore/strategies/consumer/throttled_consumer_strategy.rb +120 -0
- data/lib/chore/strategies/worker/forked_worker_strategy.rb +5 -6
- data/lib/chore/strategies/worker/helpers/ipc.rb +87 -0
- data/lib/chore/strategies/worker/helpers/preforked_worker.rb +163 -0
- data/lib/chore/strategies/worker/helpers/work_distributor.rb +65 -0
- data/lib/chore/strategies/worker/helpers/worker_info.rb +13 -0
- data/lib/chore/strategies/worker/helpers/worker_killer.rb +40 -0
- data/lib/chore/strategies/worker/helpers/worker_manager.rb +183 -0
- data/lib/chore/strategies/worker/preforked_worker_strategy.rb +150 -0
- data/lib/chore/strategies/worker/single_worker_strategy.rb +35 -13
- data/lib/chore/unit_of_work.rb +10 -1
- data/lib/chore/util.rb +5 -1
- data/lib/chore/version.rb +3 -3
- data/lib/chore/worker.rb +32 -3
- data/spec/chore/cli_spec.rb +2 -2
- data/spec/chore/consumer_spec.rb +1 -5
- data/spec/chore/duplicate_detector_spec.rb +17 -5
- data/spec/chore/fetcher_spec.rb +0 -11
- data/spec/chore/manager_spec.rb +7 -0
- data/spec/chore/queues/filesystem/filesystem_consumer_spec.rb +74 -16
- data/spec/chore/queues/sqs/consumer_spec.rb +117 -78
- data/spec/chore/queues/sqs/publisher_spec.rb +49 -60
- data/spec/chore/queues/sqs_spec.rb +32 -41
- data/spec/chore/strategies/consumer/batcher_spec.rb +50 -0
- data/spec/chore/strategies/consumer/single_consumer_strategy_spec.rb +3 -3
- data/spec/chore/strategies/consumer/threaded_consumer_strategy_spec.rb +7 -6
- data/spec/chore/strategies/consumer/throttled_consumer_strategy_spec.rb +165 -0
- data/spec/chore/strategies/worker/forked_worker_strategy_spec.rb +17 -2
- data/spec/chore/strategies/worker/helpers/ipc_spec.rb +127 -0
- data/spec/chore/strategies/worker/helpers/preforked_worker_spec.rb +236 -0
- data/spec/chore/strategies/worker/helpers/work_distributor_spec.rb +131 -0
- data/spec/chore/strategies/worker/helpers/worker_info_spec.rb +14 -0
- data/spec/chore/strategies/worker/helpers/worker_killer_spec.rb +97 -0
- data/spec/chore/strategies/worker/helpers/worker_manager_spec.rb +304 -0
- data/spec/chore/strategies/worker/preforked_worker_strategy_spec.rb +183 -0
- data/spec/chore/strategies/worker/single_worker_strategy_spec.rb +25 -0
- data/spec/chore/worker_spec.rb +82 -14
- data/spec/spec_helper.rb +1 -1
- data/spec/support/queues/sqs/fake_objects.rb +18 -0
- 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
|