qtrix 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,155 @@
1
+ require 'spec_helper'
2
+
3
+ describe Qtrix::Override do
4
+ include Qtrix::Namespacing
5
+ include_context "established default and night namespaces"
6
+
7
+ let(:queues) {[:a, :b, :c]}
8
+ let(:matrix) {Qtrix::Matrix}
9
+ let(:default_overrides) {raw_redis.llen("qtrix:default:overrides")}
10
+ let(:night_overrides) {raw_redis.llen("qtrix:night:overrides")}
11
+
12
+ before do
13
+ raw_redis.del "qtrix:default:overrides"
14
+ raw_redis.del "qtrix:night:overrides"
15
+ end
16
+
17
+ def override_count(ns=:current)
18
+ override = Qtrix::Override.all(ns).detect{|o| o.queues == queues}
19
+ override ? override.processes : 0
20
+ end
21
+
22
+ describe "#add" do
23
+ describe "with no namespace passed" do
24
+ it "should persist the override in the current ns" do
25
+ Qtrix::Override.add(queues, 1)
26
+ default_overrides.should == 1
27
+ end
28
+
29
+ it "should persist multiple processes as multiple rows" do
30
+ Qtrix::Override.add(queues, 1)
31
+ Qtrix::Override.add(queues, 1)
32
+ default_overrides.should == 2
33
+ end
34
+
35
+ it "should raise errors if the processes are zero" do
36
+ expect{Qtrix::Override.add(queues, 0)}.to raise_error
37
+ end
38
+
39
+ it "should blow away the matrix in the current ns" do
40
+ Qtrix::Override.add(queues, 1)
41
+ matrix.to_table.should be_empty
42
+ end
43
+ end
44
+
45
+ describe "with namespace passed" do
46
+ it "should persist the override in the target ns" do
47
+ Qtrix::Override.add(:night, queues, 1)
48
+ night_overrides.should == 1
49
+ default_overrides.should == 0
50
+ end
51
+ end
52
+ end
53
+
54
+ describe "#remove" do
55
+ describe "with no namespace passed" do
56
+ it "should remove the override in the current ns" do
57
+ Qtrix::Override.add(queues, 1)
58
+ Qtrix::Override.remove(queues, 1)
59
+ default_overrides.should == 0
60
+ end
61
+
62
+ it "should blow away the matrix in the current ns" do
63
+ Qtrix::Override.remove(queues, 100)
64
+ matrix.to_table.should be_empty
65
+ end
66
+ end
67
+
68
+ describe "with namespace passed" do
69
+ it "should remove the override in the target ns" do
70
+ Qtrix::Override.add(:night, queues, 2)
71
+ Qtrix::Override.remove(:night, queues, 1)
72
+ night_overrides.should == 1
73
+ default_overrides.should == 0
74
+ end
75
+
76
+ it "should blow away the matrix in the target ns" do
77
+ Qtrix::Override.remove(:night, queues, 100)
78
+ matrix.to_table(:night).should be_empty
79
+ end
80
+ end
81
+ end
82
+
83
+ describe "#clear!" do
84
+ describe "with no namespace passed" do
85
+ it "should drop all override data from redis" do
86
+ Qtrix::Override.add(queues, 1)
87
+ Qtrix::Override.clear!
88
+ redis.exists(Qtrix::Override::REDIS_KEY).should_not == true
89
+ end
90
+
91
+ it "should blow away the matrix" do
92
+ Qtrix::Override.clear!
93
+ matrix.to_table.should be_empty
94
+ end
95
+ end
96
+
97
+ describe "with namespace passed" do
98
+ it "should drop override data from the target namespace" do
99
+ Qtrix::Override.add(:night, queues, 1)
100
+ Qtrix::Override.clear! :night
101
+ raw_redis.llen("qtrix:night:overrides").should == 0
102
+ end
103
+
104
+ it "should blow away the matrix in target namespace" do
105
+ Qtrix::Override.clear!(:night)
106
+ matrix.to_table(:night).should be_empty
107
+ end
108
+ end
109
+ end
110
+
111
+ describe "#overrides_for" do
112
+ describe "with no namespace passed" do
113
+ it "should return an empty list if no overrides are present" do
114
+ Qtrix::Override.overrides_for("host1", 1).should == []
115
+ end
116
+
117
+ it "should return a list of queues from an unclaimed override" do
118
+ Qtrix::Override.add(queues, 1)
119
+ Qtrix::Override.overrides_for("host1", 1).should == [queues]
120
+ end
121
+
122
+ it "should associate the host with the override" do
123
+ Qtrix::Override.add(queues, 1)
124
+ Qtrix::Override.overrides_for("host1", 1)
125
+ result = Qtrix::Override.all.detect{|override| override.host == "host1"}
126
+ result.should_not be_nil
127
+ end
128
+
129
+ it "should return existing claims on subsequent invocations" do
130
+ Qtrix::Override.add(queues, 1)
131
+ expected = Qtrix::Override.overrides_for("host1", 1)
132
+ result = Qtrix::Override.overrides_for("host1", 1)
133
+ result.should == expected
134
+ end
135
+
136
+ it "should not return overrides beyond the current count of overrides" do
137
+ Qtrix::Override.add(queues, 1)
138
+ Qtrix::Override.overrides_for("host1", 3).should == [queues]
139
+ end
140
+
141
+ it "should not return overrides beyond the requested number of overrides" do
142
+ Qtrix::Override.add(queues, 5)
143
+ Qtrix::Override.overrides_for("host1", 1).should == [queues]
144
+ end
145
+ end
146
+
147
+ describe "with namespace passed" do
148
+ it "should limit the operation to the passed namespace" do
149
+ Qtrix::Override.add(:night, queues, 1)
150
+ Qtrix::Override.overrides_for(:current, "host", 1).should == []
151
+ Qtrix::Override.overrides_for(:night, "host", 1).should == [queues]
152
+ end
153
+ end
154
+ end
155
+ end
@@ -0,0 +1,183 @@
1
+ require 'spec_helper'
2
+
3
+ class DummyJob
4
+ @queue = :test_queue
5
+ end
6
+
7
+ describe Qtrix::Queue do
8
+ include Qtrix::Namespacing
9
+ before(:each) do
10
+ Qtrix::Queue.map_queue_weights a: 2, b: 3
11
+ end
12
+ let(:all_queues) {Qtrix::Queue.all_queues}
13
+ let(:queue1) {all_queues[1]}
14
+ let(:queue2) {all_queues[0]}
15
+ let(:matrix) {Qtrix::Matrix}
16
+
17
+ context "comparing two queues with the same name and weights" do
18
+ let(:other_queue) {Qtrix::Queue.new(:current, :a, 2)}
19
+ describe "#==" do
20
+ it "should return true" do
21
+ queue1.should == other_queue
22
+ end
23
+ end
24
+
25
+ describe "#hash" do
26
+ it "should return the same result" do
27
+ queue1.hash.should == other_queue.hash
28
+ end
29
+ end
30
+ end
31
+
32
+ context "comparing two queues with the same name and different weights" do
33
+ let(:other_queue) {other_queue = Qtrix::Queue.new(:current, :a, 3)}
34
+ describe "#==" do
35
+ it "should return false" do
36
+ queue1.should_not == other_queue
37
+ end
38
+ end
39
+
40
+ describe "#hash" do
41
+ it "should return different results" do
42
+ queue1.hash.should_not == other_queue.hash
43
+ end
44
+ end
45
+ end
46
+
47
+ context "comparing two queues with the same weight and different names" do
48
+ let(:other_queue) {other_queue = Qtrix::Queue.new(:default, :b, 2)}
49
+ describe "#==" do
50
+ it "should return false" do
51
+ queue1.should_not == other_queue
52
+ end
53
+ end
54
+
55
+ describe "#hash" do
56
+ it "should return different results" do
57
+ queue1.hash.should_not == other_queue.hash
58
+ end
59
+ end
60
+ end
61
+
62
+ describe "#resource_percentage" do
63
+ it "should equal weight / total weight of all queues" do
64
+ queue1.resource_percentage.should == 0.4
65
+ queue2.resource_percentage.should == 0.6
66
+ end
67
+ end
68
+
69
+ context "class instance methods" do
70
+ include_context "established default and night namespaces"
71
+ describe "#map_queue_weights" do
72
+ context "no namespace specified" do
73
+ it "should remove old queues" do
74
+ Qtrix::Queue.map_queue_weights A: 100
75
+ result = Qtrix::Queue.all_queues.map(&:name)
76
+ result.include?(:B).should_not be_true
77
+ result.include?(:C).should_not be_true
78
+ end
79
+
80
+ it "should add new queues ordered according to weight" do
81
+ results = Qtrix::Queue.all_queues
82
+ results.map(&:name).should == [:A, :B, :C]
83
+ results.map(&:weight).should == [3, 2, 1]
84
+ end
85
+
86
+ it "should error when trying to save empty queue name" do
87
+ expect{Qtrix::Queue.map_queue_weights('' => 1)}.to raise_error
88
+ end
89
+
90
+ it "should error when trying to save nil weight" do
91
+ expect{Qtrix::Queue.map_queue_weights(c: nil)}.to raise_error
92
+ end
93
+
94
+ it "should error when trying to save weight of 0" do
95
+ expect{Qtrix::Queue.map_queue_weights(c: 0)}.to raise_error
96
+ end
97
+
98
+ it "should blow away the matrix" do
99
+ matrix.queues_for!('host1', 1)
100
+ matrix.to_table.should_not be_empty
101
+ Qtrix::Queue.map_queue_weights \
102
+ A: 1,
103
+ B: 2
104
+ matrix.to_table.should be_empty
105
+ end
106
+ end
107
+
108
+ context "namespace specified" do
109
+ it "should affect targetted namespace" do
110
+ Qtrix::Queue.map_queue_weights :night, Z: 10, X: 1, Y: 5
111
+ Qtrix::Queue.all_queues(:night).map(&:name).should == [:Z, :Y, :X]
112
+ end
113
+ end
114
+ end
115
+
116
+ describe "#all_queues" do
117
+ describe "with no namepsace specified" do
118
+ it "should contain all queues in the default ns sorted by weight desc" do
119
+ Qtrix::Queue.all_queues.map(&:name).should == [:A, :B, :C]
120
+ end
121
+ end
122
+
123
+ describe "with namespace specified" do
124
+ it "should contain all queues in the default ns sorted by weight desc" do
125
+ result = Qtrix::Queue.all_queues(:night)
126
+ result.map(&:name).should == [:X, :Y, :Z]
127
+ end
128
+ end
129
+ end
130
+
131
+ describe "#count" do
132
+ context "with no namespace specified" do
133
+ it "should be the number of queues mapped in the current namespace" do
134
+ Qtrix::Queue.count.should == 3
135
+ end
136
+ end
137
+ context "with namespace specifeid" do
138
+ it "should be the number of queues mapped in the target namespace" do
139
+ Qtrix::Queue.map_queue_weights(:night, a: 2)
140
+ Qtrix::Queue.count(:night).should == 1
141
+ end
142
+ end
143
+ end
144
+
145
+ describe "#total_weight" do
146
+ context "with no namespace specified" do
147
+ it "should contain the sum of all weights" do
148
+ Qtrix::Queue.total_weight == 6
149
+ end
150
+ end
151
+
152
+ context "with namespace provided" do
153
+ it "should contain the sum of all weights in the namespace" do
154
+ Qtrix::Queue.total_weight(:night) == 7
155
+ end
156
+ end
157
+ end
158
+
159
+ describe "#clear!" do
160
+ context "with no namespace specified" do
161
+ before(:each) {Qtrix::Queue.clear!}
162
+ it "should remove all existing queue weights" do
163
+ redis.exists(Qtrix::Queue::REDIS_KEY).should_not == true
164
+ end
165
+
166
+ it "should blow away the matrix" do
167
+ matrix.to_table.should be_empty
168
+ end
169
+ end
170
+
171
+ context "with namespace specified" do
172
+ before(:each) {Qtrix::Queue.clear! :night}
173
+ it "should remove all existing queue weights" do
174
+ redis(:night).exists(Qtrix::Queue::REDIS_KEY).should_not == true
175
+ end
176
+
177
+ it "should blow away the matrix" do
178
+ matrix.to_table(:night).should be_empty
179
+ end
180
+ end
181
+ end
182
+ end
183
+ end
@@ -0,0 +1,204 @@
1
+ require 'set'
2
+ require 'spec_helper'
3
+
4
+ describe Qtrix do
5
+
6
+ let(:namespace_manager) {Qtrix::Namespacing::Manager.instance}
7
+
8
+ describe "operations for configuration sets" do
9
+ describe "#configuration_sets" do
10
+ it "should return all namespaces" do
11
+ Qtrix.configuration_sets.should == [:default]
12
+ end
13
+ end
14
+
15
+ describe "#create_configuration_set" do
16
+ it "should create a namespace" do
17
+ Qtrix.create_configuration_set :weekend
18
+ namespace_manager.namespaces.sort.should == [:default, :weekend]
19
+ end
20
+ end
21
+
22
+ describe "#remove_configuration_set!" do
23
+ it "should remove a namespace" do
24
+ namespace_manager.add_namespace :daytime
25
+ Qtrix.remove_configuration_set! :daytime
26
+ namespace_manager.namespaces.should == [:default]
27
+ end
28
+ end
29
+
30
+ describe "#current_configuration_set" do
31
+ it "should return the current namespace" do
32
+ Qtrix.current_configuration_set.should == :default
33
+ end
34
+ end
35
+
36
+ describe "#activate_configuration_set!" do
37
+ it "should set the current namespace" do
38
+ namespace_manager.add_namespace :weekend
39
+ Qtrix.map_queue_weights :weekend, A: 10
40
+ Qtrix.activate_configuration_set! :weekend
41
+ namespace_manager.current_namespace.should == :weekend
42
+ end
43
+ end
44
+ end
45
+
46
+ describe "#desired_distribution" do
47
+ include_context "established default and night namespaces"
48
+ let(:desired_dist) {Qtrix.desired_distribution}
49
+ let(:qtrix_queues) {Set.new(desired_dist.map(&:name)).to_a}
50
+
51
+ it "should include the list of known queues" do
52
+ known_queues = Qtrix::Queue.all_queues
53
+ Qtrix.desired_distribution.should == known_queues
54
+ end
55
+
56
+ it "should be constrainable to a config set." do
57
+ known_queues = Qtrix::Queue.all_queues :night
58
+ Qtrix.desired_distribution(:night).should == known_queues
59
+ end
60
+ end
61
+
62
+ describe "#map_queue_weights" do
63
+ include_context "established default and night namespaces"
64
+ it "should persist the mappings of queues to weights" do
65
+ Qtrix.map_queue_weights \
66
+ B: 0.3,
67
+ A: 0.4,
68
+ D: 0.1,
69
+ C: 0.2
70
+ effects = Qtrix.desired_distribution
71
+ effects.map(&:name).should == [:A, :B, :C, :D]
72
+ effects.map(&:weight).should == [0.4, 0.3, 0.2, 0.1]
73
+ end
74
+
75
+ it "should be constrainable to a config set" do
76
+ Qtrix.map_queue_weights(:night, A: 100)
77
+ known_queues = Qtrix::Queue.all_queues :night
78
+ Qtrix.desired_distribution(:night).should == known_queues
79
+ end
80
+ end
81
+
82
+ describe "override methods" do
83
+ include_context "established default and night namespaces"
84
+ def override_counts
85
+ [Qtrix::Override.all.size, Qtrix::Override.all(:night).size]
86
+ end
87
+
88
+ let(:default_overrides) {Qtrix::Override.all.map{|o| o.queues}}
89
+ let(:night_overrides) {Qtrix::Override.all(:night).map{|o| o.queues}}
90
+ let(:default_size) {Qtrix::Override.all.size}
91
+ let(:night_size) {Qtrix::Override.all(:night).size}
92
+
93
+ describe "#add_override" do
94
+ it "should persist the override to redis" do
95
+ Qtrix.add_override([:a, :b, :c], 2)
96
+ default_overrides[-2..-1].should == [[:a, :b, :c], [:a, :b, :c]]
97
+ end
98
+
99
+ it "should be directable to a config set" do
100
+ default_before, night_before = override_counts
101
+ Qtrix.add_override(:night, [:abc], 2)
102
+ default_after, night_after = override_counts
103
+ default_after.should == default_before
104
+ (night_after > night_before).should == true
105
+ end
106
+ end
107
+
108
+ describe "#remove_override" do
109
+ it "should remove the override from redis" do
110
+ Qtrix.add_override([:abc], 2)
111
+ Qtrix.remove_override([:abc], 2)
112
+ default_overrides.select{|ql| ql == [:abc]}.should be_empty
113
+ end
114
+
115
+ it "should be directable to a config set" do
116
+ Qtrix.add_override(:night, [:abc], 2)
117
+ default_before, night_before = override_counts
118
+ Qtrix.remove_override(:night, [:abc], 2)
119
+ default_after, night_after = override_counts
120
+ default_after.should == default_before
121
+ (night_after < night_before).should == true
122
+ end
123
+ end
124
+
125
+ describe "#overrides" do
126
+ it "should return all overrides from redis" do
127
+ Qtrix.add_override([:a, :b, :c], 1)
128
+ Qtrix.add_override([:x, :y, :z], 1)
129
+ default_overrides[-2..-1].should == [[:a, :b, :c], [:x, :y, :z]]
130
+ end
131
+
132
+ it "should be directable to a config set" do
133
+ Qtrix.add_override(:night, [:abc], 1)
134
+ default_cnt, night_cnt = override_counts
135
+ default_cnt.should == (night_cnt - 1)
136
+ end
137
+ end
138
+ end
139
+
140
+ describe "#queues_for!" do
141
+ before(:each) do
142
+ Qtrix.map_queue_weights \
143
+ A: 4,
144
+ B: 3,
145
+ C: 2,
146
+ D: 1
147
+ end
148
+
149
+ context "no overrides" do
150
+ it "should pick a queue list for a worker from the matrix" do
151
+ result = Qtrix.queues_for!('host1', 1)
152
+ result.should == [[:A, :B, :C, :D, :__orchestrated__]]
153
+ end
154
+ end
155
+
156
+ context "with overrides" do
157
+ before(:each) do
158
+ Qtrix.add_override([:Z], 1)
159
+ end
160
+
161
+ context "when requesting queus for fewer or equal workers as there are overrides" do
162
+ it "should pick a queue list from the overrides" do
163
+ Qtrix.queues_for!('host1', 1).should == [[:Z]]
164
+ end
165
+ end
166
+
167
+ context "when requeues queues for more workers than there are overrides" do
168
+ it "should choose queue lists from both overrides and the matrix" do
169
+ Qtrix.queues_for!('host1', 2)
170
+ .should == [[:Z],[:A,:B,:C,:D,:__orchestrated__]]
171
+ end
172
+ end
173
+ end
174
+
175
+ context "across hosts" do
176
+ it "it should appropriately distribute the queues." do
177
+ first_expected = [[:A, :B, :C, :D, :__orchestrated__],
178
+ [:B, :A, :C, :D, :__orchestrated__]]
179
+ Qtrix.queues_for!('host1', 2).should == first_expected
180
+ second_expected = [[:C, :A, :B, :D, :__orchestrated__],
181
+ [:D, :A, :B, :C, :__orchestrated__]]
182
+ Qtrix.queues_for!('host2', 2).should == second_expected
183
+ end
184
+ end
185
+ end
186
+
187
+ describe "#clear!" do
188
+ let(:queue_key) {Qtrix::Queue::REDIS_KEY}
189
+ let(:override_key) {Qtrix::Override::REDIS_KEY}
190
+ let(:matrix_key) {Qtrix::Matrix::REDIS_KEY}
191
+ before do
192
+ Qtrix.map_queue_weights A: 0.4
193
+ Qtrix::Matrix.queues_for!("localhost", 2)
194
+ Qtrix.add_override([:D, :A, :B, :C], 1)
195
+ end
196
+
197
+ it "should clear redis of all keys related to qtrix" do
198
+ Qtrix.clear!
199
+ Qtrix.redis.keys("#{matrix_key}*").should be_empty
200
+ Qtrix.redis.keys("#{queue_key}*").should_not be_empty
201
+ Qtrix.redis.keys("#{override_key}*").should_not be_empty
202
+ end
203
+ end
204
+ end