qtrix 0.0.1

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