symphony 0.8.0 → 0.9.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
- checksums.yaml.gz.sig +0 -0
- data.tar.gz.sig +0 -0
- data/ChangeLog +91 -3
- data/History.rdoc +13 -0
- data/Manifest.txt +10 -1
- data/README.rdoc +1 -1
- data/Rakefile +5 -5
- data/UPGRADING.md +38 -0
- data/USAGE.rdoc +1 -1
- data/lib/symphony.rb +86 -5
- data/lib/symphony/daemon.rb +94 -147
- data/lib/symphony/queue.rb +11 -4
- data/lib/symphony/routing.rb +5 -4
- data/lib/symphony/signal_handling.rb +1 -2
- data/lib/symphony/statistics.rb +96 -0
- data/lib/symphony/task.rb +71 -11
- data/lib/symphony/task_group.rb +165 -0
- data/lib/symphony/task_group/longlived.rb +98 -0
- data/lib/symphony/task_group/oneshot.rb +25 -0
- data/lib/symphony/tasks/oneshot_simulator.rb +61 -0
- data/lib/symphony/tasks/simulator.rb +22 -18
- data/spec/helpers.rb +67 -1
- data/spec/symphony/daemon_spec.rb +83 -31
- data/spec/symphony/queue_spec.rb +2 -2
- data/spec/symphony/routing_spec.rb +297 -0
- data/spec/symphony/statistics_spec.rb +71 -0
- data/spec/symphony/task_group/longlived_spec.rb +219 -0
- data/spec/symphony/task_group/oneshot_spec.rb +56 -0
- data/spec/symphony/task_group_spec.rb +93 -0
- data/spec/symphony_spec.rb +6 -0
- metadata +41 -18
- metadata.gz.sig +0 -0
- data/TODO.md +0 -3
data/spec/symphony/queue_spec.rb
CHANGED
@@ -19,7 +19,7 @@ describe Symphony::Queue do
|
|
19
19
|
it "can build a Hash of AMQP options from its configuration" do
|
20
20
|
expect( described_class.amqp_session_options ).to include({
|
21
21
|
heartbeat: :server,
|
22
|
-
logger: Loggability[
|
22
|
+
logger: Loggability[ Bunny ],
|
23
23
|
})
|
24
24
|
end
|
25
25
|
|
@@ -30,7 +30,7 @@ describe Symphony::Queue do
|
|
30
30
|
host: 'spimethorpe.com',
|
31
31
|
port: 23456,
|
32
32
|
heartbeat: :server,
|
33
|
-
logger: Loggability[
|
33
|
+
logger: Loggability[ Bunny ],
|
34
34
|
})
|
35
35
|
end
|
36
36
|
|
@@ -2,6 +2,7 @@
|
|
2
2
|
|
3
3
|
require_relative '../helpers'
|
4
4
|
|
5
|
+
require 'ostruct'
|
5
6
|
require 'symphony'
|
6
7
|
require 'symphony/routing'
|
7
8
|
|
@@ -63,5 +64,301 @@ describe Symphony::Routing do
|
|
63
64
|
).to include( scheduled: '2 times an hour' )
|
64
65
|
end
|
65
66
|
|
67
|
+
|
68
|
+
describe "route-matching" do
|
69
|
+
|
70
|
+
let( :task_class ) do
|
71
|
+
Class.new( Symphony::Task ) do
|
72
|
+
include Symphony::Routing
|
73
|
+
def initialize( * )
|
74
|
+
super
|
75
|
+
@run_data = Hash.new {|h,k| h[k] = [] }
|
76
|
+
end
|
77
|
+
attr_reader :run_data
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
|
82
|
+
### Call the task's #work method with the same stuff Bunny would for the
|
83
|
+
### given eventname
|
84
|
+
def publish( eventname )
|
85
|
+
delivery_info = OpenStruct.new( routing_key: eventname )
|
86
|
+
properties = OpenStruct.new( content_type: 'application/json' )
|
87
|
+
|
88
|
+
metadata = {
|
89
|
+
delivery_info: delivery_info,
|
90
|
+
properties: properties,
|
91
|
+
content_type: properties.content_type,
|
92
|
+
}
|
93
|
+
|
94
|
+
payload = '[]'
|
95
|
+
return task.work( payload, metadata )
|
96
|
+
end
|
97
|
+
|
98
|
+
|
99
|
+
context "for one-segment explicit routing keys (`simple`)" do
|
100
|
+
|
101
|
+
let( :task ) do
|
102
|
+
task_class.on( 'simple' ) {|*args| self.run_data[:simple] << args }
|
103
|
+
task_class.new( :queue )
|
104
|
+
end
|
105
|
+
|
106
|
+
|
107
|
+
it "runs the job for routing keys that exactly match" do
|
108
|
+
expect {
|
109
|
+
publish( 'simple' )
|
110
|
+
}.to change { task.run_data[:simple].length }.by( 1 )
|
111
|
+
end
|
112
|
+
|
113
|
+
|
114
|
+
it "doesn't run the job for routing keys which don't exactly match" do
|
115
|
+
expect {
|
116
|
+
publish( 'notsimple' )
|
117
|
+
}.to_not change { task.run_data[:simple].length }
|
118
|
+
end
|
119
|
+
|
120
|
+
|
121
|
+
it "doesn't run the job for routing keys which contain additional segments" do
|
122
|
+
expect {
|
123
|
+
publish( 'simple.additional1' )
|
124
|
+
}.to_not change { task.run_data[:simple_splat].length }
|
125
|
+
end
|
126
|
+
|
127
|
+
end
|
128
|
+
|
129
|
+
|
130
|
+
context "for routing keys with one segment wildcard (`simple.*`)" do
|
131
|
+
|
132
|
+
let( :task ) do
|
133
|
+
task_class.on( 'simple.*' ) {|*args| self.run_data[:simple_splat] << args }
|
134
|
+
task_class.new( :queue )
|
135
|
+
end
|
136
|
+
|
137
|
+
|
138
|
+
it "runs the job for routing keys with the same first segment and one additional segment" do
|
139
|
+
expect {
|
140
|
+
publish( 'simple.additional1' )
|
141
|
+
}.to change { task.run_data[:simple_splat].length }
|
142
|
+
end
|
143
|
+
|
144
|
+
|
145
|
+
it "doesn't run the job for routing keys with only the same first segment" do
|
146
|
+
expect {
|
147
|
+
publish( 'simple' )
|
148
|
+
}.to_not change { task.run_data[:simple_splat].length }
|
149
|
+
end
|
150
|
+
|
151
|
+
|
152
|
+
it "doesn't run the job for routing keys with a different first segment" do
|
153
|
+
expect {
|
154
|
+
publish( 'notsimple.additional1' )
|
155
|
+
}.to_not change { task.run_data[:simple_splat].length }
|
156
|
+
end
|
157
|
+
|
158
|
+
|
159
|
+
it "doesn't run the job for routing keys which contain two additional segments" do
|
160
|
+
expect {
|
161
|
+
publish( 'simple.additional1.additional2' )
|
162
|
+
}.to_not change { task.run_data[:simple_splat].length }
|
163
|
+
end
|
164
|
+
|
165
|
+
|
166
|
+
it "doesn't run the job for routing keys with a matching second segment" do
|
167
|
+
expect {
|
168
|
+
publish( 'prepended.simple.additional1' )
|
169
|
+
}.to_not change { task.run_data[:simple_splat].length }
|
170
|
+
end
|
171
|
+
|
172
|
+
end
|
173
|
+
|
174
|
+
|
175
|
+
context "for routing keys with two consecutive segment wildcards (`simple.*.*`)" do
|
176
|
+
|
177
|
+
let( :task ) do
|
178
|
+
task_class.on( 'simple.*.*' ) {|*args| self.run_data[:simple_splat_splat] << args }
|
179
|
+
task_class.new( :queue )
|
180
|
+
end
|
181
|
+
|
182
|
+
|
183
|
+
it "runs the job for routing keys which contain two additional segments" do
|
184
|
+
expect {
|
185
|
+
publish( 'simple.additional1.additional2' )
|
186
|
+
}.to change { task.run_data[:simple_splat_splat].length }
|
187
|
+
end
|
188
|
+
|
189
|
+
|
190
|
+
it "doesn't run the job for routing keys with the same first segment and one additional segment" do
|
191
|
+
expect {
|
192
|
+
publish( 'simple.additional1' )
|
193
|
+
}.to_not change { task.run_data[:simple_splat_splat].length }
|
194
|
+
end
|
195
|
+
|
196
|
+
|
197
|
+
it "doesn't run the job for routing keys with only the same first segment" do
|
198
|
+
expect {
|
199
|
+
publish( 'simple' )
|
200
|
+
}.to_not change { task.run_data[:simple_splat_splat].length }
|
201
|
+
end
|
202
|
+
|
203
|
+
|
204
|
+
it "doesn't run the job for routing keys with a different first segment" do
|
205
|
+
expect {
|
206
|
+
publish( 'notsimple.additional1' )
|
207
|
+
}.to_not change { task.run_data[:simple_splat_splat].length }
|
208
|
+
end
|
209
|
+
|
210
|
+
|
211
|
+
it "doesn't run the job for routing keys with a matching second segment" do
|
212
|
+
expect {
|
213
|
+
publish( 'prepended.simple.additional1' )
|
214
|
+
}.to_not change { task.run_data[:simple_splat_splat].length }
|
215
|
+
end
|
216
|
+
|
217
|
+
end
|
218
|
+
|
219
|
+
|
220
|
+
context "for routing keys with bracketing segment wildcards (`*.simple.*`)" do
|
221
|
+
|
222
|
+
let( :task ) do
|
223
|
+
task_class.on( '*.simple.*' ) {|*args| self.run_data[:splat_simple_splat] << args }
|
224
|
+
task_class.new( :queue )
|
225
|
+
end
|
226
|
+
|
227
|
+
|
228
|
+
it "runs the job for routing keys with a matching second segment" do
|
229
|
+
expect {
|
230
|
+
publish( 'prepended.simple.additional1' )
|
231
|
+
}.to change { task.run_data[:splat_simple_splat].length }
|
232
|
+
end
|
233
|
+
|
234
|
+
|
235
|
+
it "doesn't run the job for routing keys which contain two additional segments" do
|
236
|
+
expect {
|
237
|
+
publish( 'simple.additional1.additional2' )
|
238
|
+
}.to_not change { task.run_data[:splat_simple_splat].length }
|
239
|
+
end
|
240
|
+
|
241
|
+
|
242
|
+
it "doesn't run the job for routing keys with the same first segment and one additional segment" do
|
243
|
+
expect {
|
244
|
+
publish( 'simple.additional1' )
|
245
|
+
}.to_not change { task.run_data[:splat_simple_splat].length }
|
246
|
+
end
|
247
|
+
|
248
|
+
|
249
|
+
it "doesn't run the job for routing keys with only the same first segment" do
|
250
|
+
expect {
|
251
|
+
publish( 'simple' )
|
252
|
+
}.to_not change { task.run_data[:splat_simple_splat].length }
|
253
|
+
end
|
254
|
+
|
255
|
+
|
256
|
+
it "doesn't run the job for routing keys with a different first segment" do
|
257
|
+
expect {
|
258
|
+
publish( 'notsimple.additional1' )
|
259
|
+
}.to_not change { task.run_data[:splat_simple_splat].length }
|
260
|
+
end
|
261
|
+
|
262
|
+
end
|
263
|
+
|
264
|
+
|
265
|
+
context "for routing keys with a multi-segment wildcard (`simple.#`)" do
|
266
|
+
|
267
|
+
let( :task ) do
|
268
|
+
task_class.on( 'simple.#' ) {|*args| self.run_data[:simple_hash] << args }
|
269
|
+
task_class.new( :queue )
|
270
|
+
end
|
271
|
+
|
272
|
+
|
273
|
+
it "runs the job for routing keys with the same first segment and one additional segment" do
|
274
|
+
expect {
|
275
|
+
publish( 'simple.additional1' )
|
276
|
+
}.to change { task.run_data[:simple_hash].length }
|
277
|
+
end
|
278
|
+
|
279
|
+
|
280
|
+
it "runs the job for routing keys which contain two additional segments" do
|
281
|
+
expect {
|
282
|
+
publish( 'simple.additional1.additional2' )
|
283
|
+
}.to change { task.run_data[:simple_hash].length }
|
284
|
+
end
|
285
|
+
|
286
|
+
|
287
|
+
it "runs the job for routing keys with only the same first segment" do
|
288
|
+
expect {
|
289
|
+
publish( 'simple' )
|
290
|
+
}.to change { task.run_data[:simple_hash].length }
|
291
|
+
end
|
292
|
+
|
293
|
+
|
294
|
+
it "doesn't run the job for routing keys with a different first segment" do
|
295
|
+
expect {
|
296
|
+
publish( 'notsimple.additional1' )
|
297
|
+
}.to_not change { task.run_data[:simple_hash].length }
|
298
|
+
end
|
299
|
+
|
300
|
+
|
301
|
+
it "doesn't run the job for routing keys with a matching second segment" do
|
302
|
+
expect {
|
303
|
+
publish( 'prepended.simple.additional1' )
|
304
|
+
}.to_not change { task.run_data[:simple_hash].length }
|
305
|
+
end
|
306
|
+
|
307
|
+
end
|
308
|
+
|
309
|
+
|
310
|
+
context "for routing keys with bracketing multi-segment wildcards (`#.simple.#`)" do
|
311
|
+
|
312
|
+
let( :task ) do
|
313
|
+
task_class.on( '#.simple.#' ) {|*args| self.run_data[:hash_simple_hash] << args }
|
314
|
+
task_class.new( :queue )
|
315
|
+
end
|
316
|
+
|
317
|
+
|
318
|
+
it "runs the job for routing keys with the same first segment and one additional segment" do
|
319
|
+
expect {
|
320
|
+
publish( 'simple.additional1' )
|
321
|
+
}.to change { task.run_data[:hash_simple_hash].length }
|
322
|
+
end
|
323
|
+
|
324
|
+
|
325
|
+
it "runs the job for routing keys which contain two additional segments" do
|
326
|
+
expect {
|
327
|
+
publish( 'simple.additional1.additional2' )
|
328
|
+
}.to change { task.run_data[:hash_simple_hash].length }
|
329
|
+
end
|
330
|
+
|
331
|
+
|
332
|
+
it "runs the job for routing keys with only the same first segment" do
|
333
|
+
expect {
|
334
|
+
publish( 'simple' )
|
335
|
+
}.to change { task.run_data[:hash_simple_hash].length }
|
336
|
+
end
|
337
|
+
|
338
|
+
|
339
|
+
it "runs the job for three-segment routing keys with a matching second segment" do
|
340
|
+
expect {
|
341
|
+
publish( 'prepended.simple.additional1' )
|
342
|
+
}.to change { task.run_data[:hash_simple_hash].length }
|
343
|
+
end
|
344
|
+
|
345
|
+
|
346
|
+
it "runs the job for two-segment routing keys with a matching second segment" do
|
347
|
+
expect {
|
348
|
+
publish( 'prepended.simple' )
|
349
|
+
}.to change { task.run_data[:hash_simple_hash].length }
|
350
|
+
end
|
351
|
+
|
352
|
+
|
353
|
+
it "doesn't run the job for routing keys with a different first segment" do
|
354
|
+
expect {
|
355
|
+
publish( 'notsimple.additional1' )
|
356
|
+
}.to_not change { task.run_data[:hash_simple_hash].length }
|
357
|
+
end
|
358
|
+
|
359
|
+
end
|
360
|
+
|
361
|
+
end
|
362
|
+
|
66
363
|
end
|
67
364
|
|
@@ -0,0 +1,71 @@
|
|
1
|
+
#!/usr/bin/env rspec -cfd
|
2
|
+
|
3
|
+
require_relative '../helpers'
|
4
|
+
|
5
|
+
require 'symphony/statistics'
|
6
|
+
|
7
|
+
|
8
|
+
describe Symphony::Statistics do
|
9
|
+
|
10
|
+
|
11
|
+
let( :including_class ) do
|
12
|
+
new_class = Class.new
|
13
|
+
new_class.instance_exec( described_class ) do |mixin|
|
14
|
+
include( mixin )
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
let( :object_with_statistics ) { including_class.new }
|
19
|
+
|
20
|
+
|
21
|
+
def make_samples( *counts )
|
22
|
+
start = 1414002605.0
|
23
|
+
offset = 0
|
24
|
+
return counts.each_with_object([]) do |count, accum|
|
25
|
+
accum << [ start + offset, count ]
|
26
|
+
offset += 1
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
|
31
|
+
it "can detect an upwards trend in a sample set" do
|
32
|
+
samples = make_samples( 1, 2, 2, 3, 3, 3, 4 )
|
33
|
+
|
34
|
+
object_with_statistics.sample_size = samples.size
|
35
|
+
object_with_statistics.samples.concat( samples )
|
36
|
+
|
37
|
+
expect( object_with_statistics.sample_values_increasing? ).to be_truthy
|
38
|
+
end
|
39
|
+
|
40
|
+
|
41
|
+
it "can detect a downwards trend in a sample set" do
|
42
|
+
samples = make_samples( 4, 3, 3, 2, 2, 2, 1 )
|
43
|
+
|
44
|
+
object_with_statistics.sample_size = samples.size
|
45
|
+
object_with_statistics.samples.concat( samples )
|
46
|
+
|
47
|
+
expect( object_with_statistics.sample_values_decreasing? ).to be_truthy
|
48
|
+
end
|
49
|
+
|
50
|
+
|
51
|
+
it "isn't fooled by transitory spikes" do
|
52
|
+
samples = make_samples( 1, 2, 222, 3, 2, 3, 2 )
|
53
|
+
|
54
|
+
object_with_statistics.sample_size = samples.size
|
55
|
+
object_with_statistics.samples.concat( samples )
|
56
|
+
|
57
|
+
expect( object_with_statistics.sample_values_increasing? ).to be_falsey
|
58
|
+
end
|
59
|
+
|
60
|
+
|
61
|
+
it "doesn't try to detect a trend with a sample set that's too small" do
|
62
|
+
upward_samples = make_samples( 1, 2, 2, 3, 3, 3, 4 )
|
63
|
+
object_with_statistics.samples.replace( upward_samples )
|
64
|
+
expect( object_with_statistics.sample_values_increasing? ).to be_falsey
|
65
|
+
|
66
|
+
downward_samples = make_samples( 4, 3, 3, 2, 2, 2, 1 )
|
67
|
+
object_with_statistics.samples.replace( downward_samples )
|
68
|
+
expect( object_with_statistics.sample_values_decreasing? ).to be_falsey
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
@@ -0,0 +1,219 @@
|
|
1
|
+
#!/usr/bin/env rspec -cfd
|
2
|
+
|
3
|
+
require_relative '../../helpers'
|
4
|
+
|
5
|
+
require 'symphony/task_group/longlived'
|
6
|
+
|
7
|
+
describe Symphony::TaskGroup::LongLived do
|
8
|
+
|
9
|
+
let( :task ) do
|
10
|
+
Class.new( Symphony::Task ) do
|
11
|
+
extend Symphony::MethodUtilities
|
12
|
+
|
13
|
+
singleton_attr_accessor :has_before_forked, :has_after_forked, :has_run
|
14
|
+
|
15
|
+
def self::before_fork
|
16
|
+
self.has_before_forked = true
|
17
|
+
end
|
18
|
+
def self::after_fork
|
19
|
+
self.has_after_forked = true
|
20
|
+
end
|
21
|
+
def self::run
|
22
|
+
self.has_run = true
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
let( :task_group ) do
|
28
|
+
described_class.new( task, 2 )
|
29
|
+
end
|
30
|
+
|
31
|
+
|
32
|
+
# not enough samples
|
33
|
+
# trending up
|
34
|
+
|
35
|
+
|
36
|
+
|
37
|
+
it "doesn't start anything if it's throttled" do
|
38
|
+
# Simulate a child starting up and failing
|
39
|
+
task_group.instance_variable_set( :@last_child_started, Time.now )
|
40
|
+
task_group.adjust_throttle( 5 )
|
41
|
+
|
42
|
+
expect( Process ).to_not receive( :fork )
|
43
|
+
expect( task_group.adjust_workers ).to be_nil
|
44
|
+
end
|
45
|
+
|
46
|
+
|
47
|
+
context "when told to adjust its worker pool" do
|
48
|
+
|
49
|
+
it "starts an initial worker if it doesn't have any" do
|
50
|
+
expect( Process ).to receive( :fork ).and_return( 414 )
|
51
|
+
allow( Process ).to receive( :setpgid ).with( 414, 0 )
|
52
|
+
|
53
|
+
channel = double( Bunny::Channel )
|
54
|
+
queue = double( Bunny::Queue )
|
55
|
+
expect( Symphony::Queue ).to receive( :amqp_channel ).
|
56
|
+
and_return( channel )
|
57
|
+
expect( channel ).to receive( :queue ).
|
58
|
+
with( task.queue_name, passive: true, prefetch: 0 ).
|
59
|
+
and_return( queue )
|
60
|
+
|
61
|
+
task_group.adjust_workers
|
62
|
+
|
63
|
+
expect( task_group.started_one_worker? ).to be_truthy
|
64
|
+
expect( task_group.pids ).to include( 414 )
|
65
|
+
end
|
66
|
+
|
67
|
+
|
68
|
+
it "starts an additional worker if its work load is trending upward" do
|
69
|
+
samples = [ 1, 2, 2, 3, 3, 3, 4 ]
|
70
|
+
task_group.sample_size = samples.size
|
71
|
+
|
72
|
+
expect( Process ).to receive( :fork ).and_return( 525, 528 )
|
73
|
+
allow( Process ).to receive( :setpgid )
|
74
|
+
|
75
|
+
channel = double( Bunny::Channel )
|
76
|
+
queue = double( Bunny::Queue )
|
77
|
+
expect( Symphony::Queue ).to receive( :amqp_channel ).
|
78
|
+
and_return( channel )
|
79
|
+
expect( channel ).to receive( :queue ).
|
80
|
+
with( task.queue_name, passive: true, prefetch: 0 ).
|
81
|
+
and_return( queue )
|
82
|
+
|
83
|
+
allow( queue ).to receive( :consumer_count ).and_return( 1 )
|
84
|
+
expect( queue ).to receive( :message_count ).and_return( *samples )
|
85
|
+
|
86
|
+
start = 1414002605
|
87
|
+
start.upto( start + samples.size ) do |time|
|
88
|
+
Timecop.freeze( time ) do
|
89
|
+
task_group.adjust_workers
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
expect( task_group.started_one_worker? ).to be_truthy
|
94
|
+
expect( task_group.pids ).to include( 525, 528 )
|
95
|
+
end
|
96
|
+
|
97
|
+
|
98
|
+
it "starts an additional worker if its work load is holding steady at a non-zero value" do
|
99
|
+
pending "this being a problem we see in practice"
|
100
|
+
samples = [ 4, 4, 4, 5, 5, 4, 4 ]
|
101
|
+
task_group.sample_size = samples.size
|
102
|
+
|
103
|
+
expect( Process ).to receive( :fork ).and_return( 525, 528 )
|
104
|
+
allow( Process ).to receive( :setpgid )
|
105
|
+
|
106
|
+
channel = double( Bunny::Channel )
|
107
|
+
queue = double( Bunny::Queue )
|
108
|
+
expect( Symphony::Queue ).to receive( :amqp_channel ).
|
109
|
+
and_return( channel )
|
110
|
+
expect( channel ).to receive( :queue ).
|
111
|
+
with( task.queue_name, passive: true, prefetch: 0 ).
|
112
|
+
and_return( queue )
|
113
|
+
|
114
|
+
allow( queue ).to receive( :consumer_count ).and_return( 1 )
|
115
|
+
expect( queue ).to receive( :message_count ).and_return( *samples )
|
116
|
+
|
117
|
+
start = 1414002605
|
118
|
+
start.upto( start + samples.size ) do |time|
|
119
|
+
Timecop.freeze( time ) do
|
120
|
+
task_group.adjust_workers
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
expect( task_group.pids.size ).to eq( 2 )
|
125
|
+
end
|
126
|
+
|
127
|
+
|
128
|
+
it "doesn't start a worker if it's already running the maximum number of workers" do
|
129
|
+
samples = [ 1, 2, 2, 3, 3, 3, 4, 4, 4, 5 ]
|
130
|
+
consumers = [ 1, 1, 1, 1, 1, 1, 1, 2, 2, 2 ]
|
131
|
+
task_group.sample_size = samples.size - 3
|
132
|
+
|
133
|
+
expect( Process ).to receive( :fork ).and_return( 525, 528 )
|
134
|
+
allow( Process ).to receive( :setpgid )
|
135
|
+
|
136
|
+
channel = double( Bunny::Channel )
|
137
|
+
queue = double( Bunny::Queue )
|
138
|
+
expect( Symphony::Queue ).to receive( :amqp_channel ).
|
139
|
+
and_return( channel )
|
140
|
+
expect( channel ).to receive( :queue ).
|
141
|
+
with( task.queue_name, passive: true, prefetch: 0 ).
|
142
|
+
and_return( queue )
|
143
|
+
|
144
|
+
expect( queue ).to receive( :consumer_count ).and_return( *consumers )
|
145
|
+
expect( queue ).to receive( :message_count ).and_return( *samples )
|
146
|
+
|
147
|
+
start = 1414002605
|
148
|
+
start.upto( start + samples.size ) do |time|
|
149
|
+
Timecop.freeze( time ) do
|
150
|
+
task_group.adjust_workers
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
expect( task_group.pids.size ).to eq( 2 )
|
155
|
+
end
|
156
|
+
|
157
|
+
|
158
|
+
it "doesn't start anything if its work load is holding steady at zero" do
|
159
|
+
samples = [ 0, 1, 0, 0, 0, 0, 1, 0, 0 ]
|
160
|
+
task_group.sample_size = samples.size - 3
|
161
|
+
|
162
|
+
expect( Process ).to receive( :fork ).and_return( 525 )
|
163
|
+
allow( Process ).to receive( :setpgid )
|
164
|
+
|
165
|
+
channel = double( Bunny::Channel )
|
166
|
+
queue = double( Bunny::Queue )
|
167
|
+
expect( Symphony::Queue ).to receive( :amqp_channel ).
|
168
|
+
and_return( channel )
|
169
|
+
expect( channel ).to receive( :queue ).
|
170
|
+
with( task.queue_name, passive: true, prefetch: 0 ).
|
171
|
+
and_return( queue )
|
172
|
+
|
173
|
+
allow( queue ).to receive( :consumer_count ).and_return( 1 )
|
174
|
+
expect( queue ).to receive( :message_count ).and_return( *samples )
|
175
|
+
|
176
|
+
start = 1414002605
|
177
|
+
start.upto( start + samples.size ) do |time|
|
178
|
+
Timecop.freeze( time ) do
|
179
|
+
task_group.adjust_workers
|
180
|
+
end
|
181
|
+
end
|
182
|
+
|
183
|
+
expect( task_group.pids.size ).to eq( 1 )
|
184
|
+
end
|
185
|
+
|
186
|
+
|
187
|
+
it "doesn't start anything if its work load is trending downward" do
|
188
|
+
samples = [ 4, 3, 3, 2, 2, 2, 1, 1, 0, 0 ]
|
189
|
+
task_group.sample_size = samples.size
|
190
|
+
|
191
|
+
expect( Process ).to receive( :fork ).and_return( 525 )
|
192
|
+
allow( Process ).to receive( :setpgid )
|
193
|
+
|
194
|
+
channel = double( Bunny::Channel )
|
195
|
+
queue = double( Bunny::Queue )
|
196
|
+
expect( Symphony::Queue ).to receive( :amqp_channel ).
|
197
|
+
and_return( channel )
|
198
|
+
expect( channel ).to receive( :queue ).
|
199
|
+
with( task.queue_name, passive: true, prefetch: 0 ).
|
200
|
+
and_return( queue )
|
201
|
+
|
202
|
+
allow( queue ).to receive( :consumer_count ).and_return( 1 )
|
203
|
+
expect( queue ).to receive( :message_count ).and_return( *samples )
|
204
|
+
|
205
|
+
start = 1414002605
|
206
|
+
start.upto( start + samples.size ) do |time|
|
207
|
+
Timecop.freeze( time ) do
|
208
|
+
task_group.adjust_workers
|
209
|
+
end
|
210
|
+
end
|
211
|
+
|
212
|
+
expect( task_group.started_one_worker? ).to be_truthy
|
213
|
+
expect( task_group.pids.size ).to eq( 1 )
|
214
|
+
end
|
215
|
+
|
216
|
+
end
|
217
|
+
|
218
|
+
end
|
219
|
+
|