qtrix 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,18 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+ profiles/
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in qtrix.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 Joshua Flanagan
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,317 @@
1
+ # Qtrix
2
+
3
+ Qtrix is a means by which to intelligently pick prioritized queues for background workers. It supports the following:
4
+
5
+ * The concept of a global worker pool by which we can specifically configure individual workers across any number of servers.
6
+ * Queue prioritization based on queue weightings.
7
+ * Intelligent generation of queue lists for each worker in the global pool.
8
+ * Every queue will be at the head of the list of at least one worker.
9
+ * Queues with a higher priority will appear higher in the list of queues across the global worker pool than queues with a lower priority.
10
+ * Real time tweaking of queue prioritization that is propagated across the global worker pool without further effort.
11
+ * Overrides to specifically call out any number of workers to process a user-specified list of queues.
12
+ * Multiple configuration sets that can be switched to on-the-fly to handle shifting requirements of the global worker pool (day, night, weekend, etc...). As above, this is propagated across the global worker pool without further effort.
13
+ * CLI to allow for scriptability of changes in queue prioritization or configuration sets.
14
+ * Easy API to allow for other interfaces to be developed.
15
+
16
+ ## CLI
17
+ The CLI is provided by the ```qtrix``` executable. It provides several sub-commands to perform different operations within qtrix as follows:
18
+
19
+ * **config_sets**: Interact with configuration sets.
20
+ * **queues**: Interact with queues within a configuration set.
21
+ * **overrides**: Interact with the overrides within a configuration set.
22
+
23
+ Using this, you can manipulate the global worker pool's resources so that they are directed to handle any scenario in real time. It can be run manually on any machine that can establish a connection to the redis instance backing the global worker pool configuration. It can be scripted via cron or other means to react to changing queue prioritization/resource needs.
24
+
25
+ ### Common Options
26
+ The following options are common to all/most commands:
27
+
28
+ |option|description|
29
+ |------|-----------|
30
+ |--help|View help information about the command|
31
+ |-c |Direct the operation to a specific configuration set|
32
+ |-h |The redis host to connect to|
33
+ |-p |The redis port to connect to|
34
+ |-n |The redis database number to operate on|
35
+
36
+ ### Interacting with Configuration Sets
37
+ To see all configuration sets:
38
+
39
+ ```bash
40
+ bundle exec qtrix config_sets -l
41
+ ```
42
+
43
+ To see the current configuration set:
44
+ ```bash
45
+ bundle exec qtrix config_sets -c
46
+ ```
47
+
48
+ To add a configuration set:
49
+ ```bash
50
+ bundle exec qtrix config_sets --create night
51
+ ```
52
+
53
+ To remove a configuration set:
54
+ ```bash
55
+ bundle exec qtrix config_sets -d night
56
+ ```
57
+
58
+ ### Viewing or Specifying Queue Priority
59
+ To view the current queue priority:
60
+
61
+ ```bash
62
+ bundle exec qtrix queues -l
63
+ ```
64
+
65
+ To specify the current queue weightings by inline string:
66
+
67
+ ```bash
68
+ bundle exec qtrix queues -w A:40,B:30,C:20,D:10
69
+ ```
70
+
71
+ To specify the current queue weightings by an evaluated yaml file:
72
+
73
+ ```bash
74
+ bundle exec qtrix queues -y ~/my_really_cool.yml
75
+ ```
76
+
77
+ ### Viewing or Modifying Overrides
78
+ To view all current queue list overrides and what host has claimed them:
79
+
80
+ ```bash
81
+ bundle exec qtrix overrides -l
82
+ ```
83
+
84
+ To add a single queue list override:
85
+
86
+ ```bash
87
+ bundle exec qtrix overrides -a -q A,B,C
88
+ ```
89
+
90
+ To add 10 overrides for the same queue list:
91
+
92
+ ```bash
93
+ bundle exec qtrix overrides -a -q X,Y,Z -w 10
94
+ ```
95
+
96
+ To remove a single queue list override:
97
+
98
+ ```bash
99
+ bundle exec qtrix overrides -d -q A,B,C
100
+ ```
101
+
102
+ To remove 10 overrides for the same queue list:
103
+
104
+ ```bash
105
+ bundle exec qtrix overrides -d -q X,Y,Z -w 10
106
+ ```
107
+
108
+ ## API
109
+
110
+ The entry point to qtrix is the Qtrix module -- a facade contain operations to handle the above functionality. This is the way anything outside of qtrix interacts with it. If you use classes underneath the facade, there will be no guarantees of concurrency safety.
111
+
112
+ ### Operations
113
+ The following operations are defined in the Qtrix module
114
+
115
+ ```ruby
116
+ pry(main)> load 'lib/qtrix.rb'
117
+ => true
118
+
119
+ pry(main)> Qtrix.operations
120
+ => [:connection_config,
121
+ :operations,
122
+ :configuration_sets,
123
+ :create_configuration_set,
124
+ :remove_configuration_set!,
125
+ :current_configuration_set,
126
+ :activate_configuration_set!,
127
+ :desired_distribution,
128
+ :map_queue_weights,
129
+ :add_override,
130
+ :remove_override,
131
+ :overrides,
132
+ :queues_for!,
133
+ :clear!]
134
+ ```
135
+
136
+ ### Configuration Sets
137
+ Several of the operations work on configuration sets as follows:
138
+
139
+ ```ruby
140
+ pry(main)> Qtrix.configuration_sets
141
+ => [:default]
142
+
143
+ pry(main)> Qtrix.current_configuration_set
144
+ => :default
145
+
146
+ pry(main)> Qtrix.create_configuration_set :night
147
+ => true
148
+
149
+ pry(main)> Qtrix.configuration_sets
150
+ => [:night, :default]
151
+
152
+ pry(main)> Qtrix.activate_configuration_set! :night
153
+ => "OK"
154
+
155
+ pry(main)> Qtrix.current_configuration_set
156
+ => :night
157
+
158
+ pry(main)> Qtrix.activate_configuration_set! :default
159
+ => "OK"
160
+
161
+ pry(main)> Qtrix.remove_configuration_set! :night
162
+ => true
163
+ ```
164
+
165
+ Other operations are targettable to configuraiton sets, but will default to the current configuration set.
166
+
167
+ ### Queue Weightings
168
+ You can specify the mappings of weights to queues as follows:
169
+
170
+ ```ruby
171
+ Qtrix.map_queue_weights \
172
+ A: 40,
173
+ B: 30,
174
+ C: 20,
175
+ D: 10
176
+ ```
177
+
178
+ By default, this will be targetted at the current configuration set. You can also specify the configuration set you want to change the mappings for as follows:
179
+
180
+ ```ruby
181
+ Qtrix.map_queue_weights :night, D: 40, C: 30, B: 20, A: 10
182
+ ```
183
+
184
+ ### Desired Distribution of Queues
185
+ The desired distribution of queues can be obtained with the ```Qtrix#desired_distribution``` method. This is a list of objects encapsulating the queue name, the namespace, and their weight sorted according to weight.
186
+
187
+ ```ruby
188
+ pry(main)> Qtrix.desired_distribution
189
+ => [#<Qtrix::Queue:0x0000010c0cf758
190
+ @name=:A,
191
+ @namespace=:current,
192
+ @weight=40.0>,
193
+ #<Qtrix::Queue:0x0000010c0cf6e0
194
+ @name=:B,
195
+ @namespace=:current,
196
+ @weight=30.0>,
197
+ #<Qtrix::Queue:0x0000010c0cf668
198
+ @name=:C,
199
+ @namespace=:current,
200
+ @weight=20.0>,
201
+ #<Qtrix::Queue:0x0000010c0cf5f0
202
+ @name=:D,
203
+ @namespace=:current,
204
+ @weight=10.0>]
205
+ ```
206
+
207
+ This is again targettable towards a configuration set.
208
+
209
+ ```ruby
210
+ pry(main)> Qtrix.desired_distribution :night
211
+ => [#<Qtrix::Queue:0x007fdec6e73518
212
+ @name=:D,
213
+ @namespace=:night,
214
+ @weight=40.0>,
215
+ #<Qtrix::Queue:0x007fdec6e73400
216
+ @name=:C,
217
+ @namespace=:night,
218
+ @weight=30.0>,
219
+ #<Qtrix::Queue:0x007fdec6e73360
220
+ @name=:B,
221
+ @namespace=:night,
222
+ @weight=20.0>,
223
+ #<Qtrix::Queue:0x007fdec6e73298
224
+ @name=:A,
225
+ @namespace=:night,
226
+ @weight=10.0>]
227
+ ```
228
+
229
+ ### Overrides
230
+ Several operations are to interact with overrides -- excplicit configuration of a queue list for some number of worker processes
231
+
232
+ ```ruby
233
+ pry(main)> Qtrix.overrides
234
+ => []
235
+
236
+ pry(main)> Qtrix.add_override [:X, :Y, :Z], 1
237
+ => true
238
+
239
+ pry(main)> Qtrix.overrides
240
+ => [#<Qtrix::Override:0x007fdec6ba89f0 @host=nil, @queues=[:X, :Y, :Z]>]
241
+
242
+ pry(main)> Qtrix.add_override [:I, :J, :K], 2
243
+ => true
244
+
245
+ pry(main)> Qtrix.overrides
246
+ => [#<Qtrix::Override:0x007fdec6f54428 @host=nil, @queues=[:X, :Y, :Z]>,
247
+ #<Qtrix::Override:0x007fdec6f51ed0 @host=nil, @queues=[:I, :J, :K]>,
248
+ #<Qtrix::Override:0x007fdec6f569d0 @host=nil, @queues=[:I, :J, :K]>]
249
+
250
+ pry(main)> Qtrix.remove_override([:I, :J, :K], 1)
251
+ => true
252
+
253
+ pry(main)> Qtrix.overrides
254
+ => [#<Qtrix::Override:0x007fdec4e40a98 @host=nil, @queues=[:X, :Y, :Z]>,
255
+ #<Qtrix::Override:0x007fdec4e46808 @host=nil, @queues=[:I, :J, :K]>]
256
+
257
+ pry(main)> Qtrix.remove_override([:I, :J, :K], 1)
258
+ => true
259
+
260
+ pry(main)> Qtrix.overrides
261
+ => [#<Qtrix::Override:0x007fdec6aad370 @host=nil, @queues=[:X, :Y, :Z]>]
262
+ ```
263
+
264
+ Again, this is all targettable to a config set.
265
+
266
+ ```ruby
267
+ pry(main)> Qtrix.add_override :night, [:I, :J, :K], 1
268
+ => true
269
+ pry(main)> Qtrix.overrides :night
270
+ => [#<Qtrix::Override:0x007fdec6cb2508 @host=nil, @queues=[:I, :J, :K]>]
271
+ ```
272
+
273
+ ### Choosing Queues
274
+ Anytime a worker host needs some queues for its workers, it should call the Qtrix#queues_for! method, passing its hostname and the number of workers to obtain queues for. Overrides will be claimed first, then any additional queues will be obtained from the system that manages the desired distribution.
275
+
276
+ ```ruby
277
+ pry(main)> Qtrix.map_queue_weights A: 40, B: 30, C: 20, D: 10
278
+ => 0
279
+
280
+ pry(main)> Qtrix.add_override [:X, :Y], 1
281
+ => true
282
+
283
+ pry(main)> Qtrix.queues_for!("host1", 3)
284
+ => [[:X, :Y], [:A, :B, :C, :D], [:B, :C, :D, :A]]
285
+
286
+ pry(main)> Qtrix.queues_for!("host2", 2)
287
+ => [[:C, :D, :A, :B],[:D, :A, :B, :C]]
288
+ ```
289
+
290
+ Subsequent calls will return the same list, unless the configuration has changed, in which case new queue lists will be returned according to the new configuration.
291
+
292
+ ```ruby
293
+ pry(main)> Qtrix.queues_for!("host1", 3)
294
+ => [[:X, :Y], [:A, :B, :C, :D], [:B, :C, :D, :A]]
295
+
296
+ pry(main)> Qtrix.queues_for!("host2", 2)
297
+ => [[:C, :D, :A, :B],[:D, :A, :B, :C]]
298
+
299
+ pry(main)> Qtrix.map_queue_weights A: 10, B: 20, C: 30, D: 40
300
+ => 0
301
+
302
+ pry(main)> Qtrix.queues_for!("host1", 3)
303
+ => [[:X, :Y], [:D, :C, :B, :A], [:C, :B, :A, :D]]
304
+
305
+ pry(main)> Qtrix.queues_for!("host2", 2)
306
+ => [[:B, :A, :D, :C], [:A, :D, :C, :B]]
307
+ ```
308
+
309
+ This operation is currently not targettable to a configuration set. It always operates on the current configuration set.
310
+
311
+ ## Contributing
312
+
313
+ 1. Fork it
314
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
315
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
316
+ 4. Push to the branch (`git push origin my-new-feature`)
317
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
data/bin/qtrix ADDED
@@ -0,0 +1,15 @@
1
+ #!/usr/bin/env ruby
2
+ $LOAD_PATH.unshift File.expand_path('../../lib', __FILE__)
3
+ require 'orchestrator'
4
+ require 'orchestrator/cli'
5
+
6
+ begin
7
+ command_class = Orchestrator::CLI.get_command_class(ARGV.shift)
8
+ command = command_class.new
9
+ command.parse_options
10
+ command.exec
11
+ rescue KeyError, NoMethodError
12
+ default = Orchestrator::CLI::Default.new
13
+ default.parse_options
14
+ puts default.opt_parser
15
+ end
data/lib/qtrix.rb ADDED
@@ -0,0 +1,178 @@
1
+ require 'qtrix/version'
2
+ require 'qtrix/namespacing'
3
+ require 'qtrix/queue'
4
+ require 'qtrix/override'
5
+ require 'qtrix/matrix'
6
+
7
+ ##
8
+ # Facade into a dynamically adjusting global worker pool that auto
9
+ # balances workers according to a desired distribution of resources
10
+ # for each queue.
11
+ #
12
+ # The desired distribution can be modified in real time, and the
13
+ # workers throughout our global pool across all servers should morph
14
+ # to reflect the new desired distribution. Further details on how
15
+ # desired distribution is achieved can be found in the
16
+ # lib/qtrix/matrix.rb comments.
17
+ #
18
+ # Overrides should be able to be specified, so that we can say
19
+ # out of all of our workers, N should specifically service this list
20
+ # of queues. This is for flood event handling -- a queue gets flooded
21
+ # and we need to direct resources to it to help process the jobs faster.
22
+ #
23
+ # Different configuration sets should be supported, with one being
24
+ # active at a time. A configuration set is a namespaced desired
25
+ # distribution and set of overrides.
26
+ #
27
+ # This is the primary entry point to the system, a GUI, CLI or script
28
+ # meant to interact with the system should probably work through this
29
+ # module
30
+
31
+ module Qtrix
32
+ include Namespacing
33
+ ##
34
+ # Specifies the redis connection configuration options as per the
35
+ # redis gem.
36
+
37
+ def self.connection_config(opts={})
38
+ Namespacing::Manager.instance.connection_config(opts)
39
+ end
40
+
41
+ ##
42
+ # Returns the public operations of the facade. Useful when tinkering
43
+ # in a REPL.
44
+ def self.operations
45
+ self.public_methods - Namespacing.methods - Namespacing.instance_methods
46
+ end
47
+
48
+ ##
49
+ # Returns the list of all configuration sets that have been
50
+ # created
51
+
52
+ def self.configuration_sets
53
+ Namespacing::Manager.instance.namespaces
54
+ end
55
+
56
+ ##
57
+ # Creates a configuration set for use in the system, which
58
+ # can have its own desired distribution and overrides.
59
+
60
+ def self.create_configuration_set(namespace)
61
+ Namespacing::Manager.instance.add_namespace(namespace)
62
+ end
63
+
64
+ ##
65
+ # Removes a configuration set from the system, it will
66
+ # no longer be able to be activated to change the behavior
67
+ # of the system as a whole.
68
+
69
+ def self.remove_configuration_set!(namespace)
70
+ Namespacing::Manager.instance.remove_namespace!(namespace)
71
+ end
72
+
73
+ ##
74
+ # Returns the current configuration set in use by the system.
75
+
76
+ def self.current_configuration_set
77
+ Namespacing::Manager.instance.current_namespace
78
+ end
79
+
80
+ ##
81
+ # Specifies the current configuration set. The namespace must identify
82
+ # a configuration set created with create_configuration_set.
83
+
84
+ def self.activate_configuration_set!(namespace)
85
+ Namespacing::Manager.instance.change_current_namespace(namespace)
86
+ end
87
+
88
+ ##
89
+ # Returns a list of objects that define the desired distribution
90
+ # of workers for the current configuration set. Each element
91
+ # will contain the queue name, weight, and resource_percentage
92
+ # (weight / total weight of all queues). By default, this operated
93
+ # on the current configuration set.
94
+
95
+ def self.desired_distribution(config_set=:current)
96
+ Queue.all_queues(config_set)
97
+ end
98
+
99
+ ##
100
+ # Specifies the queue/weight mapping table for a configuration
101
+ # set. This will be used to generate the queue list for workers
102
+ # and thus the desired distribution of resources to queues. Args
103
+ # can be:
104
+ #
105
+ # config_set: optional, defaults to current.
106
+ # map: the queue-to-weight mappings as a hash of queue names to
107
+ # float values.
108
+
109
+ def self.map_queue_weights(*args)
110
+ config_set, map = extract_args(1, *args)
111
+ Qtrix::Queue.map_queue_weights(config_set, map)
112
+ end
113
+
114
+ ##
115
+ # Add a list of queue names to use as an override for a number
116
+ # of worker processes in a configuration set. The number of
117
+ # worker processes will be removed from the desired distribution
118
+ # and start working the list of queues in the override. args
119
+ # should be:
120
+ #
121
+ # configuration_set: optional, defaults to :current.
122
+ # queues: Array of queue names.
123
+ # processes: Integer specifying the number of workers
124
+ # to override queues for.
125
+
126
+ def self.add_override(*args)
127
+ config_set, queues, processes = extract_args(2, *args)
128
+ Qtrix::Override.add(config_set, queues, processes)
129
+ true
130
+ end
131
+
132
+ ##
133
+ # Removes an override from a current configuration set. That
134
+ # number of worker processes will quit servicing the queues in the
135
+ # override and be brought back into servicing the desired distribution.
136
+ # Args can be:
137
+ #
138
+ # configuration_set: optional, defaults to :current.
139
+ # queues: Array of queues in the override.
140
+ # processes: Number of processes to remove from overriding.
141
+
142
+ def self.remove_override(*args)
143
+ config_set, queues, processes = extract_args(2, *args)
144
+ Qtrix::Override.remove(config_set, queues, processes)
145
+ true
146
+ end
147
+
148
+ ##
149
+ # Retrieves all currently defined overrides within a config set.
150
+ # Defaults to use the current config set.
151
+
152
+ def self.overrides(config_set=:current)
153
+ Qtrix::Override.all(config_set)
154
+ end
155
+
156
+ ##
157
+ # Retrieves lists of queues as appropriate to the overall system balance
158
+ # for the number of workers specified for the given +hostname+.
159
+
160
+ def self.queues_for!(hostname, workers)
161
+ overrides_queues = Qtrix::Override.overrides_for(hostname, workers)
162
+ delta = workers - overrides_queues.size
163
+ matrix_queues = delta > 0 ? Matrix.queues_for!(hostname, delta) : []
164
+ overrides_queues + matrix_queues.map(&append_orchestrated_flag)
165
+ end
166
+
167
+ ##
168
+ # Clears redis of all information related to the orchestration system
169
+
170
+ def self.clear!
171
+ Matrix.clear!
172
+ end
173
+
174
+ private
175
+ def self.append_orchestrated_flag
176
+ lambda {|queue_lists| queue_lists << :__orchestrated__}
177
+ end
178
+ end