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.
- 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
|