qmore 0.6.3 → 0.7.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 +7 -0
- data/CHANGELOG +16 -0
- data/README.md +3 -2
- data/lib/qmore.rb +2 -3
- data/lib/qmore/reservers.rb +7 -0
- data/lib/qmore/reservers/default.rb +21 -0
- data/lib/qmore/reservers/delegating.rb +30 -0
- data/lib/qmore/reservers/strategies.rb +7 -0
- data/lib/qmore/reservers/strategies/filtering.rb +28 -0
- data/lib/qmore/reservers/strategies/ordering.rb +24 -0
- data/lib/qmore/reservers/strategies/sources.rb +57 -0
- data/lib/qmore/version.rb +1 -1
- data/spec/attributes_spec.rb +3 -3
- data/spec/reservers/default_spec.rb +21 -0
- data/spec/reservers/delegating_spec.rb +79 -0
- data/spec/reservers/strategies/filtering_spec.rb +62 -0
- data/spec/reservers/strategies/ordering_spec.rb +42 -0
- data/spec/reservers/strategies/sources_spec.rb +128 -0
- data/spec/spec_helper.rb +9 -1
- metadata +66 -68
- data/lib/qmore/job_reserver.rb +0 -63
- data/spec/job_reserver_spec.rb +0 -195
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 34dc454ee618a38dedc1f352dfaa2165cbd8f399
|
4
|
+
data.tar.gz: 81c85cdf9bd56911966eb74b6ee6e7fcc0530241
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: e96673443f8265ed6efb052532c15675fa3d52ed443623d1ce4b673cee527afc4d60495b6434ddd04fe8e98ac1a0d79915a125b8fc23d40e67aedda33b2621a4
|
7
|
+
data.tar.gz: fdb5218c001f5995fa0640e07e7ac89b2bd82dd36ad4f78af29e952d0e848a1300302341cf65bb576cf9f2e913d699c78257ac2e7c3e5d9b1c6a8cccd6b156f3
|
data/CHANGELOG
CHANGED
@@ -1,3 +1,19 @@
|
|
1
|
+
0.7.0 (06/23/2014)
|
2
|
+
------------------
|
3
|
+
|
4
|
+
Refactoring of qmore reserver to break it into its component pieces
|
5
|
+
making it more flexible.
|
6
|
+
|
7
|
+
Added strategies for the reservers:
|
8
|
+
Sources - where we pull the Qless::Queue from (Enumeration that returns Qless:Queue)
|
9
|
+
Filters - filters out queues from Sources based on some criteria (acts as a Source)
|
10
|
+
Ordering- Reorders the results of an iteration through an enumeration.
|
11
|
+
|
12
|
+
Added a Delegating Reserver which delegates down to a collect of reservers to
|
13
|
+
find jobs.
|
14
|
+
|
15
|
+
Added Background Source which updates its available queues asynchronously
|
16
|
+
|
1
17
|
0.6.3 (06/13/2014)
|
2
18
|
------------------
|
3
19
|
|
data/README.md
CHANGED
@@ -8,11 +8,11 @@ Authored against Qless 0.9.3, so it at least works with that - try running the t
|
|
8
8
|
Usage
|
9
9
|
-----
|
10
10
|
|
11
|
-
To use the rake tasks built into qless, just adding qless to your Gemfile should cause it to get required before the task executes. If you aren't using a gemfile, then you'll need to require qmore directly so that it sets up ENV['JOB_RESERVER'] to use Qmore::
|
11
|
+
To use the rake tasks built into qless, just adding qless to your Gemfile should cause it to get required before the task executes. If you aren't using a gemfile, then you'll need to require qmore directly so that it sets up ENV['JOB_RESERVER'] to use Qmore::Reservers::Default.
|
12
12
|
|
13
13
|
Alternatively, if you have some other way of launching workers (e.g. qless-pool), you can assign the reserver explicitly in the setup rake task or some other initializer:
|
14
14
|
|
15
|
-
Qless::Pool.pool_factory.reserver_class = Qmore::
|
15
|
+
Qless::Pool.pool_factory.reserver_class = Qmore::Reservers::Default
|
16
16
|
Qmore.client == Qless::Pool.pool_factory.client
|
17
17
|
|
18
18
|
# Enabling Monitoring
|
@@ -129,3 +129,4 @@ Contributors
|
|
129
129
|
|
130
130
|
Matt Conway ( https://github.com/wr0ngway )
|
131
131
|
Bert Goethals ( https://github.com/Bertg )
|
132
|
+
James Lawrence ( https://github.com/jambli )
|
data/lib/qmore.rb
CHANGED
@@ -4,10 +4,9 @@ require 'gem_logger'
|
|
4
4
|
require 'qmore/configuration'
|
5
5
|
require 'qmore/persistence'
|
6
6
|
require 'qmore/attributes'
|
7
|
-
require 'qmore/
|
7
|
+
require 'qmore/reservers'
|
8
8
|
|
9
9
|
module Qmore
|
10
|
-
|
11
10
|
def self.client=(client)
|
12
11
|
@client = client
|
13
12
|
end
|
@@ -43,7 +42,7 @@ end
|
|
43
42
|
|
44
43
|
module Qless
|
45
44
|
module JobReservers
|
46
|
-
QmoreReserver = Qmore::
|
45
|
+
QmoreReserver = Qmore::Reservers::Default
|
47
46
|
end
|
48
47
|
end
|
49
48
|
ENV['JOB_RESERVER'] ||= 'QmoreReserver'
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Qmore::Reservers
|
2
|
+
# @param [Enumerable] queues - qless queues to check for work
|
3
|
+
class Default < Struct.new(:queues)
|
4
|
+
def description
|
5
|
+
@description ||= queues.collect(&:name).uniq.join(', ') + " (qmore)"
|
6
|
+
end
|
7
|
+
|
8
|
+
def prep_for_work!
|
9
|
+
# nothing here on purpose
|
10
|
+
end
|
11
|
+
|
12
|
+
def reserve
|
13
|
+
queues.each do |queue|
|
14
|
+
if (job = queue.pop)
|
15
|
+
return job
|
16
|
+
end
|
17
|
+
end
|
18
|
+
nil
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module Qmore::Reservers
|
2
|
+
# @param [Enumerable] reservers - a set of reservers
|
3
|
+
# to check for work.
|
4
|
+
class Delegating < Struct.new(:reservers)
|
5
|
+
def description
|
6
|
+
"Delegating Reserver"
|
7
|
+
end
|
8
|
+
|
9
|
+
def prep_for_work!
|
10
|
+
# nothing here on purpose
|
11
|
+
end
|
12
|
+
|
13
|
+
def queues
|
14
|
+
Enumerator.new do |yielder|
|
15
|
+
reservers.each do |reserver|
|
16
|
+
reserver.queues.each {|queue| yielder << queue}
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def reserve
|
22
|
+
reservers.each do |reserver|
|
23
|
+
if (job = reserver.reserve)
|
24
|
+
return job
|
25
|
+
end
|
26
|
+
end
|
27
|
+
nil
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module Qmore::Reservers::Strategies
|
2
|
+
module Filtering
|
3
|
+
extend Qmore::Attributes
|
4
|
+
# @param [Enumerable] queues - a source of queues
|
5
|
+
# @param [Array] regexes - a list of regexes to match against.
|
6
|
+
# Return an enumerator of the filtered queues in
|
7
|
+
# in prioritized order.
|
8
|
+
def self.default(queues, regexes)
|
9
|
+
Enumerator.new do |yielder|
|
10
|
+
# Map queues to their names
|
11
|
+
mapped_queues = queues.reduce({}) do |hash,queue|
|
12
|
+
hash[queue.name] = queue
|
13
|
+
hash
|
14
|
+
end
|
15
|
+
|
16
|
+
# Filter the queue names against the regexes provided.
|
17
|
+
matches = Filtering.expand_queues(regexes, mapped_queues.keys)
|
18
|
+
|
19
|
+
# Prioritize the queues.
|
20
|
+
prioritized_names = Filtering.prioritize_queues(Qmore.configuration.priority_buckets, matches)
|
21
|
+
|
22
|
+
prioritized_names.each do |name|
|
23
|
+
yielder << mapped_queues[name]
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module Qmore::Reservers::Strategies::Ordering
|
2
|
+
# Shuffles the underlying enumerable
|
3
|
+
# for each iteration.
|
4
|
+
# @param [Enumerable] enumerable - underlying enumerator to iterate over
|
5
|
+
def self.shuffled(enumerable)
|
6
|
+
Enumerator.new do |yielder|
|
7
|
+
enumerable.to_a.shuffle.each do |e|
|
8
|
+
yielder << e
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
# Samples a subset of the underlying enumerable
|
14
|
+
# for each iteration.
|
15
|
+
# @param [Enumerable] enumerable - underlying enumerator to iterate over
|
16
|
+
# @param [Integer] sample_size - number of items to take per iteration
|
17
|
+
def self.sampled(enumerable, sample_size = 5)
|
18
|
+
Enumerator.new do |yielder|
|
19
|
+
enumerable.to_a.sample(sample_size).each do |e|
|
20
|
+
yielder << e
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
# This module provides the different kinds of queue sources used by qmore
|
2
|
+
module Qmore::Reservers::Strategies::Sources
|
3
|
+
# Direct source uses a client to generate the queues we should
|
4
|
+
# pull work from. Ignores any queues that do not have tasks available.
|
5
|
+
def self.direct(client)
|
6
|
+
Enumerator.new do |yielder|
|
7
|
+
queues = client.queues.counts.reject do |queue|
|
8
|
+
total = %w(waiting recurring depends stalled scheduled).inject(0) { |sum, state| sum += queue[state].to_i }
|
9
|
+
total == 0
|
10
|
+
end
|
11
|
+
|
12
|
+
queues.each do |queue|
|
13
|
+
yielder << client.queues[queue['name']]
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
# Background Queue source runs in a background thread
|
19
|
+
# to periodically update the queues available.
|
20
|
+
class Background
|
21
|
+
include Enumerable
|
22
|
+
attr_reader :delegate, :delay
|
23
|
+
# @param [Enumerator] delegate queue source to load the queues from.
|
24
|
+
# @param [Integer] delay - how long between updates
|
25
|
+
def initialize(delegate, delay)
|
26
|
+
@delegate = delegate
|
27
|
+
@delay = delay
|
28
|
+
end
|
29
|
+
|
30
|
+
# Spawns a thread to periodically update the
|
31
|
+
# queues.
|
32
|
+
# @return [Thread] returns the spawned thread.
|
33
|
+
def start
|
34
|
+
@stop = false
|
35
|
+
@queues = delegate.to_a
|
36
|
+
Thread.new do
|
37
|
+
begin
|
38
|
+
loop do
|
39
|
+
sleep delay
|
40
|
+
break if @stop
|
41
|
+
@queues = delegate.to_a
|
42
|
+
end
|
43
|
+
rescue => e
|
44
|
+
retry
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def stop
|
50
|
+
@stop = true
|
51
|
+
end
|
52
|
+
|
53
|
+
def each(&block)
|
54
|
+
@queues.each { |q| block.call(q) }
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
data/lib/qmore/version.rb
CHANGED
data/spec/attributes_spec.rb
CHANGED
@@ -164,9 +164,9 @@ describe "Attributes" do
|
|
164
164
|
priority_buckets = [{'pattern' => 'other*', 'fairly' => true},
|
165
165
|
{'pattern' => 'default', 'fairly' => false}]
|
166
166
|
queues = prioritize_queues(priority_buckets, @real_queues)
|
167
|
-
queues[0..4].sort.should
|
168
|
-
queues[5..-1].should
|
169
|
-
queues.should_not
|
167
|
+
(queues[0..4].sort).should eq(others.sort)
|
168
|
+
queues[5..-1].should eq(["high_x", "foo", "high_y", "superhigh_z"])
|
169
|
+
expect(queues).should_not eq(others.sort + ["high_x", "foo", "high_y", "superhigh_z"])
|
170
170
|
end
|
171
171
|
end
|
172
172
|
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe "Reservers::Default" do
|
4
|
+
before(:each) do
|
5
|
+
Qmore.client.redis.flushall
|
6
|
+
Qmore.configuration = Qmore::Configuration.new
|
7
|
+
end
|
8
|
+
|
9
|
+
it "can reserve from multiple queues" do
|
10
|
+
high_queue = Qmore.client.queues['high']
|
11
|
+
critical_queue = Qmore.client.queues['critical']
|
12
|
+
|
13
|
+
high_queue.put(SomeJob, {})
|
14
|
+
critical_queue.put(SomeJob, {})
|
15
|
+
|
16
|
+
reserver = Qmore::Reservers::Default.new([critical_queue, high_queue])
|
17
|
+
|
18
|
+
expect(reserver.reserve.queue.name).to eq('critical')
|
19
|
+
expect(reserver.reserve.queue.name).to eq('high')
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,79 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe 'Reservers::Delegating' do
|
4
|
+
before(:each) do
|
5
|
+
Redis.connect(:port => 6380).flushall
|
6
|
+
Qmore.client.redis.flushall
|
7
|
+
Qmore.configuration = Qmore::Configuration.new
|
8
|
+
end
|
9
|
+
|
10
|
+
it 'should implement #queues' do
|
11
|
+
qless1 = Qless::Client.new(:redis => Redis.connect(:port => 6379))
|
12
|
+
qless2 = Qless::Client.new(:redis => Redis.connect(:port => 6380))
|
13
|
+
queue_a = qless1.queues["a"]
|
14
|
+
queue_b = qless2.queues["b"]
|
15
|
+
|
16
|
+
job1 = queue_a.put(SomeJob, {})
|
17
|
+
job2 = queue_b.put(SomeJob, {})
|
18
|
+
|
19
|
+
expect(queue_a.length).to eq(1)
|
20
|
+
expect(queue_b.length).to eq(1)
|
21
|
+
|
22
|
+
reservers = []
|
23
|
+
reservers << Qmore::Reservers::Default.new([queue_a])
|
24
|
+
reservers << Qmore::Reservers::Default.new([queue_b])
|
25
|
+
|
26
|
+
reserver = Qmore::Reservers::Delegating.new(reservers)
|
27
|
+
|
28
|
+
queues = reserver.queues.to_a
|
29
|
+
expect(queues).to include(queue_a)
|
30
|
+
expect(queues).to include(queue_b)
|
31
|
+
end
|
32
|
+
|
33
|
+
it 'can delegate to multiple reservers' do
|
34
|
+
qless1 = Qless::Client.new(:redis => Redis.connect(:port => 6379))
|
35
|
+
qless2 = Qless::Client.new(:redis => Redis.connect(:port => 6380))
|
36
|
+
queue_a = qless1.queues["a"]
|
37
|
+
queue_b = qless2.queues["b"]
|
38
|
+
|
39
|
+
job1 = queue_a.put(SomeJob, {})
|
40
|
+
job2 = queue_b.put(SomeJob, {})
|
41
|
+
|
42
|
+
expect(queue_a.length).to eq(1)
|
43
|
+
expect(queue_b.length).to eq(1)
|
44
|
+
|
45
|
+
reserver1 = Qmore::Reservers::Default.new([queue_a])
|
46
|
+
reserver2 = Qmore::Reservers::Default.new([queue_b])
|
47
|
+
|
48
|
+
reserver = Qmore::Reservers::Delegating.new([reserver1, reserver2])
|
49
|
+
expect(reserver.reserve.queue.name).to eq('a')
|
50
|
+
expect(reserver.reserve.queue.name).to eq('b')
|
51
|
+
end
|
52
|
+
|
53
|
+
context 'with ordering' do
|
54
|
+
it 'should work with ordering strategy' do
|
55
|
+
qless1 = Qless::Client.new(:redis => Redis.connect(:port => 6379))
|
56
|
+
qless2 = Qless::Client.new(:redis => Redis.connect(:port => 6380))
|
57
|
+
queue_a = qless1.queues["a"]
|
58
|
+
queue_b = qless2.queues["b"]
|
59
|
+
|
60
|
+
job1 = queue_a.put(SomeJob, {})
|
61
|
+
job2 = queue_b.put(SomeJob, {})
|
62
|
+
|
63
|
+
expect(queue_a.length).to eq(1)
|
64
|
+
expect(queue_b.length).to eq(1)
|
65
|
+
|
66
|
+
reservers = []
|
67
|
+
reservers << Qmore::Reservers::Default.new([queue_a])
|
68
|
+
reservers << Qmore::Reservers::Default.new([queue_b])
|
69
|
+
|
70
|
+
reservers = Qmore::Reservers::Strategies::Ordering.shuffled(reservers)
|
71
|
+
reserver = Qmore::Reservers::Delegating.new(reservers)
|
72
|
+
|
73
|
+
expected = ['a', 'b']
|
74
|
+
expected.delete(reserver.reserve.queue.name)
|
75
|
+
expected.delete(reserver.reserve.queue.name)
|
76
|
+
expect(expected).to be_empty
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe "Reservers::Strategies::Filtering" do
|
4
|
+
before(:each) do
|
5
|
+
Qmore.client.redis.flushall
|
6
|
+
Qmore.configuration = Qmore::Configuration.new
|
7
|
+
end
|
8
|
+
|
9
|
+
context "default qless filtering behavior" do
|
10
|
+
it "can filter multiple queues" do
|
11
|
+
high_queue = Qmore.client.queues['high']
|
12
|
+
critical_queue = Qmore.client.queues['critical']
|
13
|
+
|
14
|
+
high_queue.put(SomeJob, {})
|
15
|
+
critical_queue.put(SomeJob, {})
|
16
|
+
|
17
|
+
queues = [high_queue, critical_queue]
|
18
|
+
filter = Qmore::Reservers::Strategies::Filtering.default(queues, ["*"])
|
19
|
+
|
20
|
+
queues = filter.collect(&:name)
|
21
|
+
expect(queues).to include('critical')
|
22
|
+
expect(queues).to include('high')
|
23
|
+
end
|
24
|
+
|
25
|
+
it "should only return matching queues" do
|
26
|
+
high_queue = Qmore.client.queues['high']
|
27
|
+
critical_queue = Qmore.client.queues['critical']
|
28
|
+
|
29
|
+
high_queue.put(SomeJob, {})
|
30
|
+
critical_queue.put(SomeJob, {})
|
31
|
+
|
32
|
+
queues = [high_queue, critical_queue]
|
33
|
+
filter = Qmore::Reservers::Strategies::Filtering.default(queues, ['critical'])
|
34
|
+
|
35
|
+
queues = filter.collect(&:name)
|
36
|
+
expect(queues).to include('critical')
|
37
|
+
expect(queues).to_not include('high')
|
38
|
+
end
|
39
|
+
|
40
|
+
it "handles priorities" do
|
41
|
+
Qmore.configuration.priority_buckets = [{'pattern' => 'foo*', 'fairly' => false},
|
42
|
+
{'pattern' => 'default', 'fairly' => false},
|
43
|
+
{'pattern' => 'bar', 'fairly' => true}]
|
44
|
+
|
45
|
+
queues = []
|
46
|
+
['other', 'blah', 'foobie', 'bar', 'foo'].each do |q|
|
47
|
+
queue = Qmore.client.queues[q]
|
48
|
+
queue.put(SomeJob, {})
|
49
|
+
expect(queue.length).to be(1)
|
50
|
+
queues << queue
|
51
|
+
end
|
52
|
+
|
53
|
+
filter = Qmore::Reservers::Strategies::Filtering.default(queues, ['*', '!blah'])
|
54
|
+
|
55
|
+
expect(filter.next.name).to eq('foo')
|
56
|
+
expect(filter.next.name).to eq('foobie')
|
57
|
+
expect(filter.next.name).to eq('other')
|
58
|
+
expect(filter.next.name).to eq('bar')
|
59
|
+
expect { filter.next }.to raise_error(StopIteration)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe "Reservers::Strategies::Ordering" do
|
4
|
+
before(:each) do
|
5
|
+
Qmore.client.redis.flushall
|
6
|
+
Qmore.configuration = Qmore::Configuration.new
|
7
|
+
end
|
8
|
+
|
9
|
+
context 'shuffled order' do
|
10
|
+
it 'should return all items in a shuffled order' do
|
11
|
+
input = [1,2,3,4,5,6,7,8]
|
12
|
+
ordering = Qmore::Reservers::Strategies::Ordering.shuffled(input)
|
13
|
+
|
14
|
+
round1 = ordering.to_a
|
15
|
+
round2 = ordering.to_a
|
16
|
+
|
17
|
+
expect(round1).not_to eq(input)
|
18
|
+
expect(round2).not_to eq(input)
|
19
|
+
expect(round1).not_to eq(round2)
|
20
|
+
|
21
|
+
input.each do |element|
|
22
|
+
expect(round1).to include(element)
|
23
|
+
expect(round2).to include(element)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
context 'sampled subset' do
|
29
|
+
it 'should return the specified number of elements' do
|
30
|
+
input = [1,2,3,4,5,6,7,8]
|
31
|
+
ordering = Qmore::Reservers::Strategies::Ordering.sampled(input, 4)
|
32
|
+
|
33
|
+
round1 = ordering.to_a
|
34
|
+
round2 = ordering.to_a
|
35
|
+
|
36
|
+
expect(round1.length).to be(4)
|
37
|
+
expect(round2.length).to be(4)
|
38
|
+
|
39
|
+
expect(round1).not_to eq(round2)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,128 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe "Reservers::Strategies::Sources" do
|
4
|
+
before(:each) do
|
5
|
+
Qmore.client.redis.flushall
|
6
|
+
Qmore.configuration = Qmore::Configuration.new
|
7
|
+
end
|
8
|
+
|
9
|
+
context 'direct source' do
|
10
|
+
it "does not return queues that have no work available" do
|
11
|
+
no_work_queue = Qmore.client.queues['no-work']
|
12
|
+
has_work_queue = Qmore.client.queues['has-work']
|
13
|
+
|
14
|
+
no_work_queue.put(SomeJob, {})
|
15
|
+
has_work_queue.put(SomeJob, {})
|
16
|
+
|
17
|
+
# drain the no work queue
|
18
|
+
no_work_queue.pop
|
19
|
+
|
20
|
+
source = Qmore::Reservers::Strategies::Sources.direct(Qmore.client)
|
21
|
+
|
22
|
+
queues = source.collect(&:name)
|
23
|
+
expect(queues).to include("has-work")
|
24
|
+
expect(queues).not_to include("no-work")
|
25
|
+
end
|
26
|
+
|
27
|
+
it "should not ignore queues that have work in scheduled state" do
|
28
|
+
work_queue = Qmore.client.queues['work']
|
29
|
+
work_queue.put(SomeJob, {}, {:delay => 1600})
|
30
|
+
|
31
|
+
%w(waiting recurring depends stalled).each do |state|
|
32
|
+
expect(work_queue.counts[state]).to be(0)
|
33
|
+
end
|
34
|
+
expect(work_queue.counts["scheduled"]).to be(1)
|
35
|
+
|
36
|
+
source = Qmore::Reservers::Strategies::Sources.direct(Qmore.client)
|
37
|
+
|
38
|
+
queues = source.collect(&:name)
|
39
|
+
expect(queues).to include("work")
|
40
|
+
end
|
41
|
+
|
42
|
+
it "should not ignore queues that have work in the depends state" do
|
43
|
+
work_queue = Qmore.client.queues['work']
|
44
|
+
jid = work_queue.put(SomeJob, {})
|
45
|
+
work_queue.put(SomeJob, {}, {:depends => [jid]})
|
46
|
+
|
47
|
+
work_queue.pop
|
48
|
+
|
49
|
+
%w(waiting recurring stalled scheduled).each do |state|
|
50
|
+
expect(work_queue.counts[state]).to be(0)
|
51
|
+
end
|
52
|
+
expect(work_queue.counts["depends"]).to be(1)
|
53
|
+
|
54
|
+
source = Qmore::Reservers::Strategies::Sources.direct(Qmore.client)
|
55
|
+
|
56
|
+
queues = source.collect(&:name)
|
57
|
+
expect(queues).to include("work")
|
58
|
+
end
|
59
|
+
|
60
|
+
it "should not ignore queues that have work in the recurring state" do
|
61
|
+
work_queue = Qmore.client.queues['work']
|
62
|
+
work_queue.recur(SomeJob, {}, 1000)
|
63
|
+
|
64
|
+
%w(waiting depends stalled scheduled).each do |state|
|
65
|
+
expect(work_queue.counts[state]).to be(0)
|
66
|
+
end
|
67
|
+
expect(work_queue.counts["recurring"]).to be(1)
|
68
|
+
|
69
|
+
source = Qmore::Reservers::Strategies::Sources.direct(Qmore.client)
|
70
|
+
|
71
|
+
queues = source.collect(&:name)
|
72
|
+
expect(queues).to include("work")
|
73
|
+
end
|
74
|
+
|
75
|
+
it "should not ignore queues that have work in the waiting state" do
|
76
|
+
work_queue = Qmore.client.queues['work']
|
77
|
+
work_queue.put(SomeJob, {})
|
78
|
+
|
79
|
+
%w(recurring depends stalled scheduled).each do |state|
|
80
|
+
expect(work_queue.counts[state]).to be(0)
|
81
|
+
end
|
82
|
+
expect(work_queue.counts["waiting"]).to be(1)
|
83
|
+
|
84
|
+
source = Qmore::Reservers::Strategies::Sources.direct(Qmore.client)
|
85
|
+
|
86
|
+
queues = source.collect(&:name)
|
87
|
+
expect(queues).to include("work")
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
context 'background source' do
|
92
|
+
it 'should return the results from the delegate' do
|
93
|
+
work_queue = Qmore.client.queues['work']
|
94
|
+
work_queue.put(SomeJob, {})
|
95
|
+
source = Qmore::Reservers::Strategies::Sources.direct(Qmore.client)
|
96
|
+
source = Qmore::Reservers::Strategies::Sources::Background.new(source, 0.1)
|
97
|
+
thread = source.start # Start the update
|
98
|
+
source.stop
|
99
|
+
thread.join
|
100
|
+
|
101
|
+
queues = source.collect(&:name)
|
102
|
+
expect(queues).to include("work")
|
103
|
+
end
|
104
|
+
|
105
|
+
context 'start' do
|
106
|
+
it 'should update from the source' do
|
107
|
+
work_queue = Qmore.client.queues['work']
|
108
|
+
work_queue.put(SomeJob, {})
|
109
|
+
source = Qmore::Reservers::Strategies::Sources.direct(Qmore.client)
|
110
|
+
source = Qmore::Reservers::Strategies::Sources::Background.new(source, 0.1)
|
111
|
+
thread = source.start # Start the update
|
112
|
+
|
113
|
+
# Add another queue to the source
|
114
|
+
queue = Qmore.client.queues['work-queue']
|
115
|
+
queue.put(SomeJob, {})
|
116
|
+
|
117
|
+
# Sleep long enough for multiple updates to occur
|
118
|
+
sleep 0.3
|
119
|
+
source.stop
|
120
|
+
thread.join
|
121
|
+
|
122
|
+
queues = source.collect(&:name)
|
123
|
+
expect(queues).to include("work")
|
124
|
+
expect(queues).to include("work-queue")
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
data/spec/spec_helper.rb
CHANGED
@@ -1,6 +1,14 @@
|
|
1
1
|
require 'rspec'
|
2
|
+
RSpec.configure do |config|
|
3
|
+
config.expect_with :rspec do |c|
|
4
|
+
c.syntax = [:should, :expect]
|
5
|
+
end
|
6
|
+
config.mock_with :rspec do |c|
|
7
|
+
c.syntax = [:should, :expect]
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
2
11
|
require 'coveralls'
|
3
|
-
require 'pry'
|
4
12
|
Coveralls.wear!
|
5
13
|
|
6
14
|
require 'qmore'
|
metadata
CHANGED
@@ -1,144 +1,127 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: qmore
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
5
|
-
prerelease:
|
4
|
+
version: 0.7.0
|
6
5
|
platform: ruby
|
7
6
|
authors:
|
8
7
|
- Matt Conway
|
9
8
|
autorequire:
|
10
9
|
bindir: bin
|
11
10
|
cert_chain: []
|
12
|
-
date: 2014-
|
11
|
+
date: 2014-07-07 00:00:00.000000000 Z
|
13
12
|
dependencies:
|
14
13
|
- !ruby/object:Gem::Dependency
|
15
|
-
prerelease: false
|
16
14
|
name: qless
|
17
|
-
type: :runtime
|
18
15
|
requirement: !ruby/object:Gem::Requirement
|
19
16
|
requirements:
|
20
|
-
- - ~>
|
17
|
+
- - "~>"
|
21
18
|
- !ruby/object:Gem::Version
|
22
19
|
version: '0.9'
|
23
|
-
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
24
22
|
version_requirements: !ruby/object:Gem::Requirement
|
25
23
|
requirements:
|
26
|
-
- - ~>
|
24
|
+
- - "~>"
|
27
25
|
- !ruby/object:Gem::Version
|
28
26
|
version: '0.9'
|
29
|
-
none: false
|
30
27
|
- !ruby/object:Gem::Dependency
|
31
|
-
prerelease: false
|
32
28
|
name: multi_json
|
33
|
-
type: :runtime
|
34
29
|
requirement: !ruby/object:Gem::Requirement
|
35
30
|
requirements:
|
36
|
-
- - ~>
|
31
|
+
- - "~>"
|
37
32
|
- !ruby/object:Gem::Version
|
38
33
|
version: '1.7'
|
39
|
-
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
40
36
|
version_requirements: !ruby/object:Gem::Requirement
|
41
37
|
requirements:
|
42
|
-
- - ~>
|
38
|
+
- - "~>"
|
43
39
|
- !ruby/object:Gem::Version
|
44
40
|
version: '1.7'
|
45
|
-
none: false
|
46
41
|
- !ruby/object:Gem::Dependency
|
47
|
-
prerelease: false
|
48
42
|
name: gem_logger
|
49
|
-
type: :runtime
|
50
43
|
requirement: !ruby/object:Gem::Requirement
|
51
44
|
requirements:
|
52
|
-
- -
|
45
|
+
- - ">="
|
53
46
|
- !ruby/object:Gem::Version
|
54
47
|
version: '0'
|
55
|
-
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
56
50
|
version_requirements: !ruby/object:Gem::Requirement
|
57
51
|
requirements:
|
58
|
-
- -
|
52
|
+
- - ">="
|
59
53
|
- !ruby/object:Gem::Version
|
60
54
|
version: '0'
|
61
|
-
none: false
|
62
55
|
- !ruby/object:Gem::Dependency
|
63
|
-
prerelease: false
|
64
56
|
name: rake
|
65
|
-
type: :development
|
66
57
|
requirement: !ruby/object:Gem::Requirement
|
67
58
|
requirements:
|
68
|
-
- -
|
59
|
+
- - ">="
|
69
60
|
- !ruby/object:Gem::Version
|
70
61
|
version: '0'
|
71
|
-
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
72
64
|
version_requirements: !ruby/object:Gem::Requirement
|
73
65
|
requirements:
|
74
|
-
- -
|
66
|
+
- - ">="
|
75
67
|
- !ruby/object:Gem::Version
|
76
68
|
version: '0'
|
77
|
-
none: false
|
78
69
|
- !ruby/object:Gem::Dependency
|
79
|
-
prerelease: false
|
80
70
|
name: rspec
|
81
|
-
type: :development
|
82
71
|
requirement: !ruby/object:Gem::Requirement
|
83
72
|
requirements:
|
84
|
-
- -
|
73
|
+
- - ">="
|
85
74
|
- !ruby/object:Gem::Version
|
86
75
|
version: '0'
|
87
|
-
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
88
78
|
version_requirements: !ruby/object:Gem::Requirement
|
89
79
|
requirements:
|
90
|
-
- -
|
80
|
+
- - ">="
|
91
81
|
- !ruby/object:Gem::Version
|
92
82
|
version: '0'
|
93
|
-
none: false
|
94
83
|
- !ruby/object:Gem::Dependency
|
95
|
-
prerelease: false
|
96
84
|
name: rack-test
|
97
|
-
type: :development
|
98
85
|
requirement: !ruby/object:Gem::Requirement
|
99
86
|
requirements:
|
100
|
-
- -
|
87
|
+
- - ">="
|
101
88
|
- !ruby/object:Gem::Version
|
102
89
|
version: '0'
|
103
|
-
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
104
92
|
version_requirements: !ruby/object:Gem::Requirement
|
105
93
|
requirements:
|
106
|
-
- -
|
94
|
+
- - ">="
|
107
95
|
- !ruby/object:Gem::Version
|
108
96
|
version: '0'
|
109
|
-
none: false
|
110
97
|
- !ruby/object:Gem::Dependency
|
111
|
-
prerelease: false
|
112
98
|
name: pry
|
113
|
-
type: :development
|
114
99
|
requirement: !ruby/object:Gem::Requirement
|
115
100
|
requirements:
|
116
|
-
- -
|
101
|
+
- - ">="
|
117
102
|
- !ruby/object:Gem::Version
|
118
103
|
version: '0'
|
119
|
-
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
120
106
|
version_requirements: !ruby/object:Gem::Requirement
|
121
107
|
requirements:
|
122
|
-
- -
|
108
|
+
- - ">="
|
123
109
|
- !ruby/object:Gem::Version
|
124
110
|
version: '0'
|
125
|
-
none: false
|
126
111
|
- !ruby/object:Gem::Dependency
|
127
|
-
prerelease: false
|
128
112
|
name: orderedhash
|
129
|
-
type: :development
|
130
113
|
requirement: !ruby/object:Gem::Requirement
|
131
114
|
requirements:
|
132
|
-
- -
|
115
|
+
- - ">="
|
133
116
|
- !ruby/object:Gem::Version
|
134
117
|
version: '0'
|
135
|
-
|
118
|
+
type: :development
|
119
|
+
prerelease: false
|
136
120
|
version_requirements: !ruby/object:Gem::Requirement
|
137
121
|
requirements:
|
138
|
-
- -
|
122
|
+
- - ">="
|
139
123
|
- !ruby/object:Gem::Version
|
140
124
|
version: '0'
|
141
|
-
none: false
|
142
125
|
description: Qmore allows one to specify the queues a worker processes by the use
|
143
126
|
of wildcards, negations, or dynamic look up from redis. It also allows one to specify
|
144
127
|
the relative priority between queues (rather than within a single queue). It plugs
|
@@ -149,8 +132,8 @@ executables: []
|
|
149
132
|
extensions: []
|
150
133
|
extra_rdoc_files: []
|
151
134
|
files:
|
152
|
-
- .gitignore
|
153
|
-
- .travis.yml
|
135
|
+
- ".gitignore"
|
136
|
+
- ".travis.yml"
|
154
137
|
- CHANGELOG
|
155
138
|
- Gemfile
|
156
139
|
- LICENSE
|
@@ -161,8 +144,14 @@ files:
|
|
161
144
|
- lib/qmore.rb
|
162
145
|
- lib/qmore/attributes.rb
|
163
146
|
- lib/qmore/configuration.rb
|
164
|
-
- lib/qmore/job_reserver.rb
|
165
147
|
- lib/qmore/persistence.rb
|
148
|
+
- lib/qmore/reservers.rb
|
149
|
+
- lib/qmore/reservers/default.rb
|
150
|
+
- lib/qmore/reservers/delegating.rb
|
151
|
+
- lib/qmore/reservers/strategies.rb
|
152
|
+
- lib/qmore/reservers/strategies/filtering.rb
|
153
|
+
- lib/qmore/reservers/strategies/ordering.rb
|
154
|
+
- lib/qmore/reservers/strategies/sources.rb
|
166
155
|
- lib/qmore/server.rb
|
167
156
|
- lib/qmore/server/views/dynamicqueues.erb
|
168
157
|
- lib/qmore/server/views/priorities.erb
|
@@ -170,40 +159,49 @@ files:
|
|
170
159
|
- qmore.gemspec
|
171
160
|
- spec/attributes_spec.rb
|
172
161
|
- spec/configuration_spec.rb
|
173
|
-
- spec/job_reserver_spec.rb
|
174
162
|
- spec/persistance_spec.rb
|
175
163
|
- spec/redis/qless01-test.conf
|
176
164
|
- spec/redis/qless02-test.conf
|
165
|
+
- spec/reservers/default_spec.rb
|
166
|
+
- spec/reservers/delegating_spec.rb
|
167
|
+
- spec/reservers/strategies/filtering_spec.rb
|
168
|
+
- spec/reservers/strategies/ordering_spec.rb
|
169
|
+
- spec/reservers/strategies/sources_spec.rb
|
177
170
|
- spec/server_spec.rb
|
178
171
|
- spec/spec_helper.rb
|
179
172
|
homepage: ''
|
180
173
|
licenses: []
|
174
|
+
metadata: {}
|
181
175
|
post_install_message:
|
182
176
|
rdoc_options: []
|
183
177
|
require_paths:
|
184
178
|
- lib
|
185
179
|
required_ruby_version: !ruby/object:Gem::Requirement
|
186
180
|
requirements:
|
187
|
-
- -
|
181
|
+
- - ">="
|
188
182
|
- !ruby/object:Gem::Version
|
189
183
|
version: '0'
|
190
|
-
segments:
|
191
|
-
- 0
|
192
|
-
hash: -593121205799441535
|
193
|
-
none: false
|
194
184
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
195
185
|
requirements:
|
196
|
-
- -
|
186
|
+
- - ">="
|
197
187
|
- !ruby/object:Gem::Version
|
198
188
|
version: '0'
|
199
|
-
segments:
|
200
|
-
- 0
|
201
|
-
hash: -593121205799441535
|
202
|
-
none: false
|
203
189
|
requirements: []
|
204
190
|
rubyforge_project: qmore
|
205
|
-
rubygems_version:
|
191
|
+
rubygems_version: 2.2.2
|
206
192
|
signing_key:
|
207
|
-
specification_version:
|
193
|
+
specification_version: 4
|
208
194
|
summary: A qless plugin that gives more control over how queues are processed
|
209
|
-
test_files:
|
195
|
+
test_files:
|
196
|
+
- spec/attributes_spec.rb
|
197
|
+
- spec/configuration_spec.rb
|
198
|
+
- spec/persistance_spec.rb
|
199
|
+
- spec/redis/qless01-test.conf
|
200
|
+
- spec/redis/qless02-test.conf
|
201
|
+
- spec/reservers/default_spec.rb
|
202
|
+
- spec/reservers/delegating_spec.rb
|
203
|
+
- spec/reservers/strategies/filtering_spec.rb
|
204
|
+
- spec/reservers/strategies/ordering_spec.rb
|
205
|
+
- spec/reservers/strategies/sources_spec.rb
|
206
|
+
- spec/server_spec.rb
|
207
|
+
- spec/spec_helper.rb
|
data/lib/qmore/job_reserver.rb
DELETED
@@ -1,63 +0,0 @@
|
|
1
|
-
module Qmore
|
2
|
-
class JobReserver
|
3
|
-
include Qmore::Attributes
|
4
|
-
# define queues for Qless worker to invoke.
|
5
|
-
attr_reader :queues
|
6
|
-
attr_reader :clients
|
7
|
-
|
8
|
-
def initialize(queues)
|
9
|
-
@queues = queues
|
10
|
-
# Pull the regex off of the Qless::Queue#name, we want to keep the same interface
|
11
|
-
# that Qless reservers use.
|
12
|
-
@regexes = queues.collect(&:name).uniq
|
13
|
-
@clients = {}
|
14
|
-
queues.each do |q|
|
15
|
-
@clients[q.client] ||= []
|
16
|
-
@clients[q.client] << q.name
|
17
|
-
end
|
18
|
-
end
|
19
|
-
|
20
|
-
def description
|
21
|
-
@description ||= @regexes.join(', ') + " (qmore)"
|
22
|
-
end
|
23
|
-
|
24
|
-
def prep_for_work!
|
25
|
-
# nothing here on purpose
|
26
|
-
end
|
27
|
-
|
28
|
-
def reserve
|
29
|
-
self.clients.keys.shuffle.each do |client|
|
30
|
-
self.extract_queues(client, self.clients[client]).each do |q|
|
31
|
-
job = q.pop
|
32
|
-
return job if job
|
33
|
-
end
|
34
|
-
end
|
35
|
-
|
36
|
-
nil
|
37
|
-
end
|
38
|
-
|
39
|
-
# @param [Qless::Client] client - client to pull queues from.
|
40
|
-
# @param [Array] regexes - array of regular expressions to match
|
41
|
-
# queues against.
|
42
|
-
def extract_queues(client, regexes)
|
43
|
-
# Cache the queues so we don't make multiple calls.
|
44
|
-
# and remove any queues that don't have work.
|
45
|
-
actual_queues = client.queues.counts.reject do |queue|
|
46
|
-
total = %w(waiting recurring depends stalled scheduled).inject(0) { |sum, state| sum += queue[state].to_i }
|
47
|
-
total == 0
|
48
|
-
end
|
49
|
-
|
50
|
-
# Grab all the actual queue names from the client.
|
51
|
-
queue_names = actual_queues.collect {|h| h['name'] }
|
52
|
-
|
53
|
-
# Match the queue names against the regexes provided.
|
54
|
-
matched_names = expand_queues(regexes, queue_names)
|
55
|
-
|
56
|
-
# Prioritize the queues.
|
57
|
-
prioritized_names = prioritize_queues(Qmore.configuration.priority_buckets, matched_names)
|
58
|
-
|
59
|
-
# collect the matched queues names in prioritized order.
|
60
|
-
prioritized_names.collect {|name| client.queues[name] }
|
61
|
-
end
|
62
|
-
end
|
63
|
-
end
|
data/spec/job_reserver_spec.rb
DELETED
@@ -1,195 +0,0 @@
|
|
1
|
-
require "spec_helper"
|
2
|
-
|
3
|
-
describe "JobReserver" do
|
4
|
-
include Qmore::Attributes
|
5
|
-
|
6
|
-
before(:each) do
|
7
|
-
Qmore.client.redis.flushall
|
8
|
-
Qmore.configuration = Qmore::Configuration.new
|
9
|
-
end
|
10
|
-
|
11
|
-
context "multiple qless server environment" do
|
12
|
-
it "can reserve jobs from regex queue names on multiple clients" do
|
13
|
-
qless1 = Qless::Client.new(:redis => Redis.connect(:port => 6379))
|
14
|
-
qless2 = Qless::Client.new(:redis => Redis.connect(:port => 6380))
|
15
|
-
queue_a = qless1.queues["a"]
|
16
|
-
queue_b = qless2.queues["b"]
|
17
|
-
queue_a.put(SomeJob, {})
|
18
|
-
queue_b.put(SomeJob, {})
|
19
|
-
|
20
|
-
queue_a.length.should == 1
|
21
|
-
queue_b.length.should == 1
|
22
|
-
|
23
|
-
reserver = Qmore::JobReserver.new([qless1.queues["*"], qless2.queues["*"]])
|
24
|
-
worker = Qless::Worker.new(reserver, :run_as_single_process => true)
|
25
|
-
worker.work(0)
|
26
|
-
|
27
|
-
queue_a.length.should == 0
|
28
|
-
queue_b.length.should == 0
|
29
|
-
end
|
30
|
-
|
31
|
-
it "shuffles the order of the clients" do
|
32
|
-
queue = Qmore.client.queues['queue']
|
33
|
-
|
34
|
-
reserver = Qmore::JobReserver.new([queue])
|
35
|
-
k = reserver.clients.keys
|
36
|
-
k.should_receive(:shuffle).once.and_return(reserver.clients.keys)
|
37
|
-
reserver.clients.should_receive(:keys).and_return(k)
|
38
|
-
reserver.reserve
|
39
|
-
end
|
40
|
-
end
|
41
|
-
|
42
|
-
context "basic qless behavior still works" do
|
43
|
-
it "ignores queues that have no work available" do
|
44
|
-
no_work_queue = Qmore.client.queues['no-work']
|
45
|
-
has_work_queue = Qmore.client.queues['has-work']
|
46
|
-
|
47
|
-
no_work_queue.put(SomeJob, {})
|
48
|
-
has_work_queue.put(SomeJob, {})
|
49
|
-
|
50
|
-
# drain the no work queue
|
51
|
-
no_work_queue.pop
|
52
|
-
|
53
|
-
reserver = Qmore::JobReserver.new([no_work_queue, has_work_queue])
|
54
|
-
|
55
|
-
queues = reserver.extract_queues(Qmore.client, ["*"]).collect(&:name)
|
56
|
-
queues.should include("has-work")
|
57
|
-
queues.should_not include("no-work")
|
58
|
-
end
|
59
|
-
|
60
|
-
it "should not ignore queues that have work in scheduled state" do
|
61
|
-
work_queue = Qmore.client.queues['work']
|
62
|
-
work_queue.put(SomeJob, {}, {:delay => 1600})
|
63
|
-
|
64
|
-
%w(waiting recurring depends stalled).each do |state|
|
65
|
-
work_queue.counts[state].should equal(0)
|
66
|
-
end
|
67
|
-
work_queue.counts["scheduled"].should equal(1)
|
68
|
-
|
69
|
-
reserver = Qmore::JobReserver.new([work_queue])
|
70
|
-
queues = reserver.extract_queues(Qmore.client, ["*"]).collect(&:name)
|
71
|
-
queues.should include("work")
|
72
|
-
end
|
73
|
-
|
74
|
-
it "should not ignore queues that have work in the depends state" do
|
75
|
-
work_queue = Qmore.client.queues['work']
|
76
|
-
jid = work_queue.put(SomeJob, {})
|
77
|
-
work_queue.put(SomeJob, {}, {:depends => [jid]})
|
78
|
-
|
79
|
-
work_queue.pop
|
80
|
-
|
81
|
-
%w(waiting recurring stalled scheduled).each do |state|
|
82
|
-
work_queue.counts[state].should equal(0)
|
83
|
-
end
|
84
|
-
work_queue.counts["depends"].should equal(1)
|
85
|
-
|
86
|
-
reserver = Qmore::JobReserver.new([work_queue])
|
87
|
-
queues = reserver.extract_queues(Qmore.client, ["*"]).collect(&:name)
|
88
|
-
queues.should include("work")
|
89
|
-
end
|
90
|
-
|
91
|
-
it "should not ignore queues that have work in the recurring state" do
|
92
|
-
work_queue = Qmore.client.queues['work']
|
93
|
-
work_queue.recur(SomeJob, {}, 1000)
|
94
|
-
|
95
|
-
%w(waiting depends stalled scheduled).each do |state|
|
96
|
-
work_queue.counts[state].should equal(0)
|
97
|
-
end
|
98
|
-
work_queue.counts["recurring"].should equal(1)
|
99
|
-
|
100
|
-
reserver = Qmore::JobReserver.new([work_queue])
|
101
|
-
queues = reserver.extract_queues(Qmore.client, ["*"]).collect(&:name)
|
102
|
-
queues.should include("work")
|
103
|
-
end
|
104
|
-
|
105
|
-
it "should not ignore queues that have work in the waiting state" do
|
106
|
-
work_queue = Qmore.client.queues['work']
|
107
|
-
work_queue.put(SomeJob, {})
|
108
|
-
|
109
|
-
%w(recurring depends stalled scheduled).each do |state|
|
110
|
-
work_queue.counts[state].should equal(0)
|
111
|
-
end
|
112
|
-
work_queue.counts["waiting"].should equal(1)
|
113
|
-
|
114
|
-
reserver = Qmore::JobReserver.new([work_queue])
|
115
|
-
queues = reserver.extract_queues(Qmore.client, ["*"]).collect(&:name)
|
116
|
-
queues.should include("work")
|
117
|
-
end
|
118
|
-
|
119
|
-
it "can reserve from multiple queues" do
|
120
|
-
high_queue = Qmore.client.queues['high']
|
121
|
-
critical_queue = Qmore.client.queues['critical']
|
122
|
-
|
123
|
-
high_queue.put(SomeJob, {})
|
124
|
-
critical_queue.put(SomeJob, {})
|
125
|
-
|
126
|
-
reserver = Qmore::JobReserver.new([critical_queue, high_queue])
|
127
|
-
|
128
|
-
reserver.reserve.queue.name.should == 'critical'
|
129
|
-
reserver.reserve.queue.name.should == 'high'
|
130
|
-
end
|
131
|
-
|
132
|
-
it "can work on multiple queues" do
|
133
|
-
high_queue = Qmore.client.queues['high']
|
134
|
-
critical_queue = Qmore.client.queues['critical']
|
135
|
-
high_queue.put(SomeJob, {})
|
136
|
-
critical_queue.put(SomeJob, {})
|
137
|
-
|
138
|
-
high_queue.length.should == 1
|
139
|
-
critical_queue.length.should == 1
|
140
|
-
|
141
|
-
reserver = Qmore::JobReserver.new([critical_queue, high_queue])
|
142
|
-
|
143
|
-
worker = Qless::Worker.new(reserver,
|
144
|
-
:run_as_single_process => true)
|
145
|
-
worker.work(0)
|
146
|
-
|
147
|
-
high_queue.length.should == 0
|
148
|
-
critical_queue.length.should == 0
|
149
|
-
end
|
150
|
-
|
151
|
-
it "can work on all queues" do
|
152
|
-
queues = []
|
153
|
-
['high', 'critical', 'blahblah'].each do |q|
|
154
|
-
queue = Qmore.client.queues[q]
|
155
|
-
queue.put(SomeJob, {})
|
156
|
-
queue.length.should == 1
|
157
|
-
queues << queue
|
158
|
-
end
|
159
|
-
|
160
|
-
reserver = Qmore::JobReserver.new([Qmore.client.queues['*']])
|
161
|
-
worker = Qless::Worker.new(reserver,
|
162
|
-
:run_as_single_process => true)
|
163
|
-
worker.work(0)
|
164
|
-
|
165
|
-
queues.each do |q|
|
166
|
-
q.length.should == 0
|
167
|
-
end
|
168
|
-
end
|
169
|
-
|
170
|
-
it "handles priorities" do
|
171
|
-
Qmore.configuration.priority_buckets = [{'pattern' => 'foo*', 'fairly' => false},
|
172
|
-
{'pattern' => 'default', 'fairly' => false},
|
173
|
-
{'pattern' => 'bar', 'fairly' => true}]
|
174
|
-
|
175
|
-
|
176
|
-
queues = []
|
177
|
-
['other', 'blah', 'foobie', 'bar', 'foo'].each do |q|
|
178
|
-
queue = Qmore.client.queues[q]
|
179
|
-
queue.put(SomeJob, {})
|
180
|
-
queue.length.should == 1
|
181
|
-
queues << queue
|
182
|
-
end
|
183
|
-
|
184
|
-
reserver = Qmore::JobReserver.new([Qmore.client.queues['*'], Qmore.client.queues['!blah']])
|
185
|
-
|
186
|
-
reserver.reserve.queue.name.should == 'foo'
|
187
|
-
reserver.reserve.queue.name.should == 'foobie'
|
188
|
-
reserver.reserve.queue.name.should == 'other'
|
189
|
-
reserver.reserve.queue.name.should == 'bar'
|
190
|
-
reserver.reserve.should be_nil
|
191
|
-
end
|
192
|
-
|
193
|
-
end
|
194
|
-
|
195
|
-
end
|