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.
data/lib/qtrix/cli.rb ADDED
@@ -0,0 +1,80 @@
1
+ require 'mixlib/cli'
2
+ require 'yaml'
3
+
4
+ module Qtrix
5
+ module CLI
6
+ class Base
7
+ include Mixlib::CLI
8
+ attr_reader :stdout, :stderr
9
+
10
+ def initialize(stdout=STDOUT, stderr=STDERR)
11
+ super()
12
+ @stdout = stdout
13
+ @stderr = stderr
14
+ end
15
+
16
+ def exec
17
+ Qtrix.connection_config(config)
18
+ unless exec_behavior
19
+ msg = "no appropriate combination of options.\n\n"
20
+ msg += "Type 'bundle exec [command] --help' for usage"
21
+ error(msg)
22
+ end
23
+ rescue StandardError => e
24
+ error("Failure: #{e}")
25
+ end
26
+
27
+ private
28
+ def to_args(*args)
29
+ args.compact
30
+ end
31
+
32
+ def write(msg)
33
+ stdout.write("#{msg}\n")
34
+ true
35
+ end
36
+
37
+ def error(msg)
38
+ stderr.write("Failure: #{msg}\n")
39
+ false
40
+ end
41
+ end
42
+
43
+ class Default
44
+ include Mixlib::CLI
45
+
46
+ banner <<-EOS
47
+
48
+ Usage: bundle exec qtrix [sub-command] [options]
49
+
50
+ Where available sub-commands are:
51
+
52
+ config-sets: Perform operations on configuration sets.
53
+ queues: Perform operations on queues within a
54
+ configuration set.
55
+ overrides: Perform operations on overrides within a
56
+ configuration set.
57
+
58
+ For more information about the subcommands, try:
59
+
60
+ bundle exec qtrix [subcommand] --help
61
+
62
+ EOS
63
+ end
64
+
65
+ require 'qtrix/cli/config_sets'
66
+ require 'qtrix/cli/queues'
67
+ require 'qtrix/cli/overrides'
68
+
69
+ @commands = {
70
+ config_sets: ConfigSets,
71
+ overrides: Overrides,
72
+ queues: Queues
73
+ }
74
+
75
+ def self.get_command_class(str)
76
+ @commands.fetch(str.downcase.to_sym) if str
77
+ end
78
+ end
79
+ end
80
+
@@ -0,0 +1,87 @@
1
+ module Qtrix
2
+ module CLI
3
+ class ConfigSets < Base
4
+ include Mixlib::CLI
5
+
6
+ banner <<-EOS
7
+
8
+ Usage: bundle exec qtrix config_sets [options]
9
+
10
+ Allows interaction with the configuration sets in the qtrix system.
11
+ With this, you can:
12
+
13
+ * View all configuration sets.
14
+ * View the current configuration set.
15
+ * Add a configuration set.
16
+ * Specify the current configuration set.
17
+ * Remove a configuration set.
18
+
19
+ Options include:
20
+
21
+ EOS
22
+
23
+ option :list,
24
+ short: '-l',
25
+ long: '--list',
26
+ description: 'The list of known config sets'
27
+
28
+ option :current_configuration_set,
29
+ short: '-c',
30
+ long: '--current',
31
+ description: 'List current config set'
32
+
33
+ option :add_configuration_set,
34
+ long: '--create CONFIG_SET_NAME',
35
+ description: 'Create a new config set'
36
+
37
+ option :activate_configuration_set,
38
+ short: '-a CONFIG_SET_NAME',
39
+ long: '--activate CONFIG_SET_NAME',
40
+ description: 'Activate a configuration set'
41
+
42
+ option :remove_configuration_set,
43
+ short: '-d CONFIG_SET_NAME',
44
+ long: '--delete CONFIG_SET_NAME',
45
+ description: 'Delete a non-active config set'
46
+
47
+ option :host,
48
+ short: '-h HOST',
49
+ description: 'The host where redis is located',
50
+ default: 'localhost'
51
+
52
+ option :port,
53
+ short: '-p PORT',
54
+ description: 'The host which redis is listening on',
55
+ default: 6379
56
+
57
+ option :db,
58
+ short: '-n DB',
59
+ description: 'The redis DB where the action should occur',
60
+ default: 0
61
+
62
+ def exec_behavior
63
+ if config[:list]
64
+ config_sets = Qtrix.configuration_sets
65
+ msg = "Known configuration sets: #{config_sets.join(", ")}"
66
+ write(msg)
67
+ elsif config[:current_configuration_set]
68
+ current_config_set = Qtrix.current_configuration_set
69
+ msg = "Current configuration set: #{current_config_set}"
70
+ write(msg)
71
+ elsif config[:add_configuration_set]
72
+ config_set = config[:add_configuration_set]
73
+ Qtrix.create_configuration_set(config_set)
74
+ write("Configuration set created successfully: #{config_set}")
75
+ elsif config[:activate_configuration_set]
76
+ config_set = config[:activate_configuration_set].to_sym
77
+ Qtrix.activate_configuration_set!(config_set)
78
+ write("Activated configuration set successfully: #{config_set}")
79
+ elsif config[:remove_configuration_set]
80
+ config_set = config[:remove_configuration_set]
81
+ Qtrix.remove_configuration_set!(config_set.to_sym)
82
+ write("Configuration set removed successfully: #{config_set}")
83
+ end
84
+ end
85
+ end
86
+ end
87
+ end
@@ -0,0 +1,121 @@
1
+ module Qtrix
2
+ module CLI
3
+ class Overrides < Base
4
+ banner <<-EOS
5
+
6
+ Usage: bundle exec qtrix overrides [options]
7
+
8
+ Allows interaction with the overrides in a configuration set. With this
9
+ you can:
10
+
11
+ * List all the overrides in the config set.
12
+ * Add overrides for a list of queues and number of workers.
13
+ * Remove overrides for a list of queues and number of workers.
14
+
15
+ Options include:
16
+
17
+ EOS
18
+
19
+ option :list_overrides,
20
+ short: '-l',
21
+ long: '--list',
22
+ description: 'List the queue overrides'
23
+
24
+ option :add_overrides,
25
+ short: '-a',
26
+ long: '--add',
27
+ description: 'Add a queue list override'
28
+
29
+ option :delete_overrides,
30
+ short: '-d',
31
+ long: '--delete',
32
+ description: 'Delete a queue list override, be sure to add -q <queue names>'
33
+
34
+ option :queue_list,
35
+ short: '-q QUEUE_LIST',
36
+ long: '--queues QUEUE_LIST',
37
+ description: 'Specify the list of queues for the override'
38
+
39
+ option :workers,
40
+ short: '-w WORKER_COUNT',
41
+ long: '--workers WORKER_COUNT',
42
+ description: 'Specify the list of workers for the override',
43
+ default: '1'
44
+
45
+ option :config_set,
46
+ short: '-c CONFIG_SET_NAME',
47
+ long: '--config-set CONFIG_SET_NAME',
48
+ description: 'Specify the config set being operated on'
49
+
50
+ option :host,
51
+ short: '-h HOST',
52
+ description: 'The host where redis is located',
53
+ default: 'localhost'
54
+
55
+ option :port,
56
+ short: '-p PORT',
57
+ description: 'The host which redis is listening on',
58
+ default: 6379
59
+
60
+ option :db,
61
+ short: '-n DB',
62
+ description: 'The redis DB where the action should occur',
63
+ default: 0
64
+
65
+ def exec_behavior
66
+ if config[:list_overrides]
67
+ msg = "Current Overrides:\n"
68
+ msg += overrides.map(&stringify).join("\n")
69
+ write(msg)
70
+ elsif add_overrides_params_provided?
71
+ Qtrix.add_override *to_args(config_set, queue_list, workers)
72
+ write("Added #{workers} overrides for #{queue_list}")
73
+ elsif delete_overrides_params_provided?
74
+ Qtrix.remove_override *to_args(config_set, queue_list, workers)
75
+ write("Deleted #{workers} overrides for #{queue_list}")
76
+ end
77
+ end
78
+
79
+ private
80
+ def config_set
81
+ config[:config_set]
82
+ end
83
+
84
+ def stringify
85
+ lambda {|override| string_value_of(override)}
86
+ end
87
+
88
+ def overrides
89
+ Qtrix.overrides config[:config_set]
90
+ end
91
+
92
+ def string_value_of(override)
93
+ " #{hostname_of(override)}: #{override.queues.join(',')}"
94
+ end
95
+
96
+ def hostname_of(override)
97
+ override.host || 'Unclaimed'
98
+ end
99
+
100
+ def add_overrides_params_provided?
101
+ config[:add_overrides] && queues_and_workers?
102
+ end
103
+
104
+ def delete_overrides_params_provided?
105
+ config[:delete_overrides] && queues_and_workers?
106
+ end
107
+
108
+ def queues_and_workers?
109
+ config[:queue_list] && config[:workers]
110
+ end
111
+
112
+ def queue_list
113
+ config[:queue_list].split(",").map(&:strip)
114
+ end
115
+
116
+ def workers
117
+ config[:workers].to_i
118
+ end
119
+ end
120
+ end
121
+ end
@@ -0,0 +1,97 @@
1
+ module Qtrix
2
+ module CLI
3
+ class Queues < Base
4
+ banner <<-EOS
5
+
6
+ Usage: bundle exec qtrix queues [options]
7
+
8
+ Allows observance and manipulation of queue priority within a
9
+ configuration set. With this you can:
10
+
11
+ * View the current queue priority in the global worker pool.
12
+ * Specify the weightings for all queues inline or via yaml
13
+
14
+ Options include:
15
+
16
+ EOS
17
+
18
+ option :queue_weights,
19
+ short: '-w',
20
+ long: '--weights QUEUE_WEIGHT_LIST',
21
+ description: 'Specifies the queue-weight mappings as queue:weight,queue:weight,...'
22
+
23
+ option :queue_weights_yaml,
24
+ short: '-y',
25
+ long: '--yaml PATH_TO_YAML',
26
+ description: 'Path to a yaml containing a hash of queue names to weights'
27
+
28
+ option :desired_distribution,
29
+ short: '-l',
30
+ long: '--list',
31
+ description: 'Lists the queues by their desired distribution'
32
+
33
+ option :config_set,
34
+ short: '-c CONFIG_SET_NAME',
35
+ long: '--config-set CONFIG_SET_NAME',
36
+ description: 'Specify the config set being operated on'
37
+
38
+ option :host,
39
+ short: '-h HOST',
40
+ description: 'The host where redis is located',
41
+ default: 'localhost'
42
+
43
+ option :port,
44
+ short: '-p PORT',
45
+ description: 'The host which redis is listening on',
46
+ default: 6379
47
+
48
+ option :db,
49
+ short: '-n DB',
50
+ description: 'The redis DB where the action should occur',
51
+ default: 0
52
+
53
+ def exec_behavior
54
+ if config[:desired_distribution]
55
+ msg = "Queues:\n"
56
+ msg += Qtrix.desired_distribution.map(&stringify).join("\n")
57
+ write(msg)
58
+ elsif queue_weights
59
+ map_queue_weights queue_weights
60
+ elsif queue_weights_yaml
61
+ map_queue_weights queue_weights_yaml
62
+ end
63
+ end
64
+
65
+ private
66
+ def map_queue_weights(weight_map)
67
+ Qtrix.map_queue_weights *to_args(config_set, weight_map)
68
+ write("OK")
69
+ end
70
+
71
+ def stringify
72
+ lambda {|queue| " #{queue.name} (#{queue.weight})"}
73
+ end
74
+
75
+ def string_value_of(queue)
76
+ " #{q.name} (#{q.weight})"
77
+ end
78
+
79
+ def queue_weights
80
+ if config[:queue_weights]
81
+ tuple_list = config[:queue_weights].split(",").map{|kv| kv.split(":")}
82
+ Hash[tuple_list]
83
+ end
84
+ end
85
+
86
+ def queue_weights_yaml
87
+ if config[:queue_weights_yaml]
88
+ YAML.load(File.read(config[:queue_weights_yaml]))
89
+ end
90
+ end
91
+
92
+ def config_set
93
+ config[:config_set]
94
+ end
95
+ end
96
+ end
97
+ end
@@ -0,0 +1,83 @@
1
+ require 'bigdecimal'
2
+ require 'qtrix/matrix/common'
3
+ require 'qtrix/matrix/model'
4
+ require 'qtrix/matrix/queue_picker'
5
+ require 'qtrix/matrix/reader'
6
+ require 'qtrix/matrix/row_builder'
7
+ require 'qtrix/matrix/queue_prioritizer'
8
+ require 'qtrix/matrix/analyzer'
9
+
10
+ module Qtrix
11
+ ##
12
+ # Represents the matrix of queues to workers across the global worker pool,
13
+ # and is used to assign queues to pools based on the following goals:
14
+ #
15
+ # 1. Maintain a desired distribution of worker resources to queues based
16
+ # on their weight within the system as a whole.
17
+ # 2. Ensure that every queue is at the head of at least 1 workers queue
18
+ # list.
19
+ #
20
+ # Given a queue's weight, we calculate its resource percentage as:
21
+ #
22
+ # weight_of(queue) / weight_of(all_queues)
23
+ #
24
+ # To generate the list of queues for a worker, we take a list of queues
25
+ # sorted by current priority. The current priority is calculated in one of
26
+ # two ways. If the queue has not been at the head of the list, it is
27
+ # calculated as:
28
+ #
29
+ # resource_percentage_of(queue) * 1000
30
+ #
31
+ # This inflated value ensures that we will assign all queues to the head of
32
+ # at least one worker's queue list.
33
+ #
34
+ # If a queue has not been assigned to the head of a worker's queue list,
35
+ # the following algortihm is used. Each entry in the matrix has a value,
36
+ # which is generally the likelihood that the queue would be reached given
37
+ # the weight of jobs to its left in a queue list. Mathematically, this is:
38
+ #
39
+ # 1.0 - resource_percentage_of(previous_queues_in_list)
40
+ #
41
+ # Thus the closer to the head of a list a queue's entry is, the higher a
42
+ # value it receives for that entry. The priority is then calculated as:
43
+ #
44
+ # resource_percentage_of(queue) / (1.0 + value_of(entries_for(queue))
45
+ #
46
+ # This ensures that the higher a queue appears in a list, the lower its
47
+ # priority for the next generated list.
48
+
49
+ module Matrix
50
+ include Qtrix::Namespacing
51
+ include Common
52
+
53
+ class << self
54
+ ##
55
+ # Obtain lists of queues for a number of worker processes
56
+ # on a server identified by the hostname. This works
57
+ # within the current namespace only.
58
+ def queues_for!(*args)
59
+ namespace, hostname, workers = extract_args(2, *args)
60
+ QueuePicker.new(namespace, Reader, hostname, workers).pick!
61
+ end
62
+
63
+ ##
64
+ # Returns all of the queues in the table.
65
+ def fetch(namespace=:current)
66
+ Reader.fetch(namespace)
67
+ end
68
+
69
+ ##
70
+ # Fetches a matrix of simple queue names for each entry.
71
+ def to_table(namespace=:current)
72
+ Reader.to_table(namespace)
73
+ end
74
+
75
+ ##
76
+ # Clears the matrix so its rebuilt again when rows are requested.
77
+ def clear!(namespace=:current)
78
+ redis(namespace).del(REDIS_KEY)
79
+ end
80
+ end
81
+ end
82
+ end
83
+