qmore 0.6.2 → 0.6.3

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.
@@ -8,7 +8,7 @@ module Qmore
8
8
  def self.registered(app)
9
9
 
10
10
  app.helpers do
11
-
11
+
12
12
  def qmore_view(filename, options = {}, locals = {})
13
13
  options = {:layout => true, :locals => { :title => filename.to_s.capitalize }}.merge(options)
14
14
  dir = File.expand_path("../server/views/", __FILE__)
@@ -24,7 +24,7 @@ module Qmore
24
24
  queue_tab_index = original_tabs.index {|t| t[:name] == 'Queues' }
25
25
  original_tabs.insert(queue_tab_index + 1, *qmore_tabs)
26
26
  end
27
-
27
+
28
28
  end
29
29
 
30
30
  #
@@ -34,7 +34,11 @@ module Qmore
34
34
  app.get "/dynamicqueues" do
35
35
  @queues = []
36
36
  real_queues = Qmore.client.queues.counts.collect {|q| q['name'] }
37
- dqueues = Attr.get_dynamic_queues
37
+
38
+ # For the UI we always want the latest persisted data
39
+ configuration = Qmore.persistence.load
40
+
41
+ dqueues = configuration.dynamic_queues
38
42
  dqueues.each do |k, v|
39
43
  expanded = Attr.expand_queues(["@#{k}"], real_queues)
40
44
  expanded = expanded.collect { |q| q.split(":").last }
@@ -69,7 +73,14 @@ module Qmore
69
73
  values = queue['value'].to_s.split(',').collect { |q| q.gsub(/\s/, '') }
70
74
  queues[key] = values
71
75
  end
72
- Attr.set_dynamic_queues(queues)
76
+
77
+ # For the UI we always want the latest persisted data
78
+ configuration = Qmore.persistence.load
79
+ configuration.dynamic_queues = queues
80
+
81
+ Qmore.persistence.write(configuration)
82
+ Qmore.configuration.dynamic_queues = configuration.dynamic_queues
83
+
73
84
  redirect to("/dynamicqueues")
74
85
  end
75
86
 
@@ -78,16 +89,25 @@ module Qmore
78
89
  #
79
90
 
80
91
  app.get "/queuepriority" do
81
- @priorities = Attr.get_priority_buckets
92
+ # For the UI we always want the latest persisted data
93
+ configuration = Qmore.persistence.load
94
+
95
+ @priorities = configuration.priority_buckets
82
96
  qmore_view :priorities
83
97
  end
84
98
 
85
99
  app.post "/queuepriority" do
86
100
  priorities = params['priorities']
87
- Attr.set_priority_buckets priorities
101
+
102
+ # For the UI we always want the latest persisted data
103
+ configuration = Qmore.persistence.load
104
+ configuration.priority_buckets = priorities
105
+
106
+ Qmore.persistence.write(configuration)
107
+ Qmore.configuration.priority_buckets = configuration.priority_buckets
108
+
88
109
  redirect to("/queuepriority")
89
110
  end
90
-
91
111
  end
92
112
  end
93
113
  end
@@ -1,3 +1,3 @@
1
1
  module Qmore
2
- VERSION = "0.6.2"
2
+ VERSION = "0.6.3"
3
3
  end
@@ -22,6 +22,7 @@ Gem::Specification.new do |s|
22
22
 
23
23
  s.add_dependency("qless", '~> 0.9')
24
24
  s.add_dependency("multi_json", '~> 1.7')
25
+ s.add_dependency("gem_logger")
25
26
 
26
27
  s.add_development_dependency('rake')
27
28
  s.add_development_dependency('rspec')
@@ -5,70 +5,12 @@ describe "Attributes" do
5
5
 
6
6
  before(:each) do
7
7
  Qmore.client.redis.flushall
8
- @real_queues = ["high_x", "foo", "high_y", "superhigh_z"]
9
- end
10
-
11
- context "dynamic attributes" do
12
-
13
- it "should always have a fallback pattern" do
14
- get_dynamic_queues.should == {'default' => ['*']}
15
- end
16
-
17
- it "should allow setting single patterns" do
18
- get_dynamic_queue('foo').should == ['*']
19
- set_dynamic_queue('foo', ['bar'])
20
- get_dynamic_queue('foo').should == ['bar']
21
- end
22
-
23
- it "should allow setting multiple patterns" do
24
- set_dynamic_queues({'foo' => ['bar'], 'baz' => ['boo']})
25
- get_dynamic_queues.should == {'foo' => ['bar'], 'baz' => ['boo'], 'default' => ['*']}
26
- end
27
-
28
- it "should remove mapping when setting empty value" do
29
- get_dynamic_queues
30
- set_dynamic_queues({'foo' => ['bar'], 'baz' => ['boo']})
31
- get_dynamic_queues.should == {'foo' => ['bar'], 'baz' => ['boo'], 'default' => ['*']}
32
-
33
- set_dynamic_queues({'foo' => [], 'baz' => ['boo']})
34
- get_dynamic_queues.should == {'baz' => ['boo'], 'default' => ['*']}
35
- set_dynamic_queues({'baz' => nil})
36
- get_dynamic_queues.should == {'default' => ['*']}
37
-
38
- set_dynamic_queues({'foo' => ['bar'], 'baz' => ['boo']})
39
- set_dynamic_queue('foo', [])
40
- get_dynamic_queues.should == {'baz' => ['boo'], 'default' => ['*']}
41
- set_dynamic_queue('baz', nil)
42
- get_dynamic_queues.should == {'default' => ['*']}
43
- end
44
-
45
- end
46
-
47
- context "priority attributes" do
48
-
49
- it "can lookup a default priority" do
50
- get_priority_buckets.should == [{'pattern' => 'default'}]
51
- end
52
-
53
- it "can set priorities" do
54
- set_priority_buckets [{'pattern' => 'foo', 'fairly' => 'false'}]
55
- get_priority_buckets.should == [{'pattern' => 'foo', 'fairly' => 'false'},
56
- {'pattern' => 'default'}]
57
- end
58
-
59
- it "can set priorities including default" do
60
- set_priority_buckets [{'pattern' => 'foo', 'fairly' => false},
61
- {'pattern' => 'default', 'fairly' => false},
62
- {'pattern' => 'bar', 'fairly' => true}]
63
- get_priority_buckets.should == [{'pattern' => 'foo', 'fairly' => false},
64
- {'pattern' => 'default', 'fairly' => false},
65
- {'pattern' => 'bar', 'fairly' => true}]
66
- end
8
+ Qmore.configuration = Qmore::Configuration.new
67
9
 
10
+ @real_queues = ["high_x", "foo", "high_y", "superhigh_z"]
68
11
  end
69
12
 
70
13
  context "basic queue patterns" do
71
-
72
14
  it "can specify simple queues" do
73
15
  expand_queues(["foo"], @real_queues).should == ["foo"]
74
16
  expand_queues(["foo", "bar"], @real_queues).should == ["bar", "foo"]
@@ -92,23 +34,21 @@ describe "Attributes" do
92
34
  it "can blacklist queues with pattern" do
93
35
  expand_queues(["*", "!*high*"], @real_queues).should == ["foo"]
94
36
  end
95
-
96
37
  end
97
38
 
98
- context "redis backed queues" do
99
-
39
+ context "expanding queues" do
100
40
  it "can dynamically lookup queues" do
101
- set_dynamic_queue("mykey", ["foo", "bar"])
41
+ Qmore.configuration.dynamic_queues = {"mykey" => ["foo", "bar"]}
102
42
  expand_queues(["@mykey"], @real_queues).should == ["bar", "foo"]
103
43
  end
104
44
 
105
45
  it "can blacklist dynamic queues" do
106
- set_dynamic_queue("mykey", ["foo"])
46
+ Qmore.configuration.dynamic_queues["mykey"] = ["foo"]
107
47
  expand_queues(["*", "!@mykey"], @real_queues).should == ["high_x", "high_y", "superhigh_z"]
108
48
  end
109
49
 
110
50
  it "can blacklist dynamic queues with negation" do
111
- set_dynamic_queue("mykey", ["!foo", "high_x"])
51
+ Qmore.configuration.dynamic_queues["mykey"] = ["!foo", "high_x"]
112
52
  expand_queues(["!@mykey"], @real_queues).should == ["foo"]
113
53
  end
114
54
 
@@ -119,18 +59,19 @@ describe "Attributes" do
119
59
  end
120
60
 
121
61
  it "uses hostname as default key in dynamic queues" do
122
- host = `hostname`.chomp
123
- set_dynamic_queue(host, ["foo", "bar"])
62
+ host = Socket.gethostname
63
+ Qmore.configuration.dynamic_queues[host] = ["foo", "bar"]
64
+
124
65
  expand_queues(["@"], @real_queues).should == ["bar", "foo"]
125
66
  end
126
67
 
127
68
  it "can use wildcards in dynamic queues" do
128
- set_dynamic_queue("mykey", ["*high*", "!high_y"])
69
+ Qmore.configuration.dynamic_queues["mykey"] = ["*high*", "!high_y"]
129
70
  expand_queues(["@mykey"], @real_queues).should == ["high_x", "superhigh_z"]
130
71
  end
131
72
 
132
73
  it "falls back to default queues when missing" do
133
- set_dynamic_queue("default", ["foo", "bar"])
74
+ Qmore.configuration.dynamic_queues["default"] = ["foo", "bar"]
134
75
  expand_queues(["@mykey"], @real_queues).should == ["bar", "foo"]
135
76
  end
136
77
 
@@ -143,11 +84,9 @@ describe "Attributes" do
143
84
  @real_queues << "bar"
144
85
  expand_queues(["@mykey"], @real_queues).should == ["bar", "foo", "high_x", "high_y", "superhigh_z"]
145
86
  end
146
-
147
87
  end
148
88
 
149
89
  context "queue priorities" do
150
-
151
90
  it "should pick up all queues with default priority" do
152
91
  priority_buckets = [{'pattern' => 'default', 'fairly' => false}]
153
92
  prioritize_queues(priority_buckets, @real_queues).should == ["high_x", "foo", "high_y", "superhigh_z"]
@@ -229,7 +168,5 @@ describe "Attributes" do
229
168
  queues[5..-1].should == ["high_x", "foo", "high_y", "superhigh_z"]
230
169
  queues.should_not == others.sort + ["high_x", "foo", "high_y", "superhigh_z"]
231
170
  end
232
-
233
171
  end
234
-
235
172
  end
@@ -0,0 +1,112 @@
1
+ require "spec_helper"
2
+
3
+ describe "Qmore::LegacyConfiguration" do
4
+ it "should always have a fallback pattern" do
5
+ configuration = Qmore::LegacyConfiguration.new(Qmore.persistence)
6
+
7
+ configuration.dynamic_queues['default'].should == ['*']
8
+ end
9
+
10
+ it "should load the latest dynamic queues from persistence" do
11
+ configuration = Qmore::LegacyConfiguration.new(Qmore.persistence)
12
+ expected_configuration = Qmore::Configuration.new
13
+
14
+ expected_configuration.dynamic_queues["foo"] = ["bar"]
15
+ expected_configuration.dynamic_queues["baz"] = ["biz"]
16
+ Qmore.persistence.write(expected_configuration)
17
+ configuration.dynamic_queues.should == expected_configuration.dynamic_queues
18
+
19
+ expected_configuration.dynamic_queues["baz"] = []
20
+ Qmore.persistence.write(expected_configuration)
21
+ # Ensuring baz was removed
22
+ configuration.dynamic_queues.should == {"foo" => ["bar"], "default" => ["*"]}
23
+ configuration.dynamic_queues.should == expected_configuration.dynamic_queues
24
+ end
25
+
26
+ it "should load the latest priority buckets from persistence" do
27
+ configuration = Qmore::LegacyConfiguration.new(Qmore.persistence)
28
+ expected_configuration = Qmore::Configuration.new
29
+ expected_configuration.priority_buckets = [{'pattern' => 'foo', 'fairly' => 'false'}]
30
+
31
+ Qmore.persistence.write(expected_configuration)
32
+
33
+ configuration.priority_buckets.should == expected_configuration.priority_buckets
34
+ end
35
+ end
36
+
37
+ describe "Qmore::Configuration" do
38
+ before(:each) do
39
+ Qmore.client.redis.flushall
40
+ end
41
+
42
+ context "dynamic queues" do
43
+ it "should always have a fallback pattern" do
44
+ configuration = Qmore::Configuration.new
45
+
46
+ configuration.dynamic_queues['default'].should == ['*']
47
+ end
48
+
49
+ it "should default any unspecified keys to default pattern" do
50
+ configuration = Qmore::Configuration.new
51
+ configuration.dynamic_queues['foo'].should == ['*']
52
+
53
+ configuration.dynamic_queues[Qmore::Configuration::DYNAMIC_FALLBACK_KEY] = ["foo", "bar"]
54
+ configuration.dynamic_queues['foo'].should == ["foo", "bar"]
55
+ end
56
+
57
+ it "should allow setting single patterns" do
58
+ configuration = Qmore::Configuration.new
59
+
60
+ configuration.dynamic_queues['foo'].should == ['*']
61
+ configuration.dynamic_queues['foo'] = ['bar']
62
+ configuration.dynamic_queues['foo'].should == ['bar']
63
+ end
64
+
65
+ it "should allow changing the pattern of the fallback key" do
66
+ configuration = Qmore::Configuration.new
67
+ configuration.dynamic_queues[Qmore::Configuration::DYNAMIC_FALLBACK_KEY].should == ['*']
68
+ configuration.dynamic_queues[Qmore::Configuration::DYNAMIC_FALLBACK_KEY] = ['foo', 'bar']
69
+ configuration.dynamic_queues[Qmore::Configuration::DYNAMIC_FALLBACK_KEY].should == ['foo', 'bar']
70
+ end
71
+
72
+ it "should ignore mappings when setting empty value" do
73
+ configuration = Qmore::Configuration.new
74
+ configuration.dynamic_queues = {'foo' => ['bar'], 'baz' => ['boo']}
75
+ configuration.dynamic_queues.should == {'default' => ['*'], 'foo' => ['bar'], 'baz' => ['boo']}
76
+
77
+ configuration.dynamic_queues = {'foo' => [], 'baz' => ['boo']}
78
+ configuration.dynamic_queues.should == {'default' => ['*'], 'baz' => ['boo']}
79
+ configuration.dynamic_queues = {'baz' => nil}
80
+ configuration.dynamic_queues.should == {'default' => ['*']}
81
+
82
+ configuration.dynamic_queues = {'foo' => ['bar'], 'baz' => ['boo']}
83
+ configuration.dynamic_queues['foo'] = []
84
+ configuration.dynamic_queues.should == {'default' => ["*"], 'baz' => ['boo']}
85
+ configuration.dynamic_queues['baz'] = nil
86
+ configuration.dynamic_queues.should == {'default' => ['*']}
87
+ end
88
+ end
89
+
90
+ context "priority attributes" do
91
+ it "should have a default priority" do
92
+ configuration = Qmore::Configuration.new
93
+ configuration.priority_buckets.should == [{'pattern' => 'default'}]
94
+ end
95
+
96
+ it "can set priorities" do
97
+ expected_priority_buckets = [{'pattern' => 'foo', 'fairly' => 'false'},{'pattern' => 'default'}]
98
+ configuration = Qmore::Configuration.new
99
+ configuration.priority_buckets = [{'pattern' => 'foo', 'fairly' => 'false'}]
100
+ configuration.priority_buckets.should == expected_priority_buckets
101
+ end
102
+
103
+ it "can set priorities including default" do
104
+ expected_priority_buckets = [{'pattern' => 'foo', 'fairly' => false},
105
+ {'pattern' => 'default', 'fairly' => false},
106
+ {'pattern' => 'bar', 'fairly' => true}]
107
+ configuration = Qmore::Configuration.new
108
+ configuration.priority_buckets = expected_priority_buckets
109
+ configuration.priority_buckets.should == expected_priority_buckets
110
+ end
111
+ end
112
+ end
@@ -5,6 +5,7 @@ describe "JobReserver" do
5
5
 
6
6
  before(:each) do
7
7
  Qmore.client.redis.flushall
8
+ Qmore.configuration = Qmore::Configuration.new
8
9
  end
9
10
 
10
11
  context "multiple qless server environment" do
@@ -13,8 +14,8 @@ describe "JobReserver" do
13
14
  qless2 = Qless::Client.new(:redis => Redis.connect(:port => 6380))
14
15
  queue_a = qless1.queues["a"]
15
16
  queue_b = qless2.queues["b"]
16
- queue_a.put(SomeJob, [])
17
- queue_b.put(SomeJob, [])
17
+ queue_a.put(SomeJob, {})
18
+ queue_b.put(SomeJob, {})
18
19
 
19
20
  queue_a.length.should == 1
20
21
  queue_b.length.should == 1
@@ -39,12 +40,88 @@ describe "JobReserver" do
39
40
  end
40
41
 
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
+
42
119
  it "can reserve from multiple queues" do
43
120
  high_queue = Qmore.client.queues['high']
44
121
  critical_queue = Qmore.client.queues['critical']
45
122
 
46
- high_queue.put(SomeJob, [])
47
- critical_queue.put(SomeJob, [])
123
+ high_queue.put(SomeJob, {})
124
+ critical_queue.put(SomeJob, {})
48
125
 
49
126
  reserver = Qmore::JobReserver.new([critical_queue, high_queue])
50
127
 
@@ -55,8 +132,8 @@ describe "JobReserver" do
55
132
  it "can work on multiple queues" do
56
133
  high_queue = Qmore.client.queues['high']
57
134
  critical_queue = Qmore.client.queues['critical']
58
- high_queue.put(SomeJob, [])
59
- critical_queue.put(SomeJob, [])
135
+ high_queue.put(SomeJob, {})
136
+ critical_queue.put(SomeJob, {})
60
137
 
61
138
  high_queue.length.should == 1
62
139
  critical_queue.length.should == 1
@@ -75,7 +152,7 @@ describe "JobReserver" do
75
152
  queues = []
76
153
  ['high', 'critical', 'blahblah'].each do |q|
77
154
  queue = Qmore.client.queues[q]
78
- queue.put(SomeJob, [])
155
+ queue.put(SomeJob, {})
79
156
  queue.length.should == 1
80
157
  queues << queue
81
158
  end
@@ -91,7 +168,7 @@ describe "JobReserver" do
91
168
  end
92
169
 
93
170
  it "handles priorities" do
94
- set_priority_buckets [{'pattern' => 'foo*', 'fairly' => false},
171
+ Qmore.configuration.priority_buckets = [{'pattern' => 'foo*', 'fairly' => false},
95
172
  {'pattern' => 'default', 'fairly' => false},
96
173
  {'pattern' => 'bar', 'fairly' => true}]
97
174
 
@@ -99,7 +176,7 @@ describe "JobReserver" do
99
176
  queues = []
100
177
  ['other', 'blah', 'foobie', 'bar', 'foo'].each do |q|
101
178
  queue = Qmore.client.queues[q]
102
- queue.put(SomeJob, [])
179
+ queue.put(SomeJob, {})
103
180
  queue.length.should == 1
104
181
  queues << queue
105
182
  end