resque-pool-dynamic 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,6 @@
1
+ *~
2
+ *.gem
3
+ .bundle
4
+ Gemfile.lock
5
+ pkg/*
6
+ .yardoc/
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in resque-pool-dynamic.gemspec
4
+ gemspec
data/README.md ADDED
@@ -0,0 +1,242 @@
1
+ # Dynamic Resque Pool
2
+
3
+ [Resque pool](https://github.com/nevans/resque-pool) by Nicholas Evans
4
+ is a library for managing a pool of
5
+ [resque](http://github.com/defunkt/resque) workers. Given a a config
6
+ file, it manages your workers for you, starting up the appropriate
7
+ number of workers for each worker type.
8
+
9
+ While the resque pool is convenient for a permanent process
10
+ overlooking a fairly constant set of workers, it is less than
11
+ convenient to use for more dynamic tasks - one-time long running batch
12
+ processing worker families that need to be started and supervised
13
+ manually, and often require adjusting number of worker processes for
14
+ maximum performance.
15
+
16
+ Workflow for this kind of task would be:
17
+
18
+ * prepare a yaml file with number of workers
19
+ * start resque-pool
20
+ * `tail -f` the log file on the other console
21
+ * look into `pstree` (or `ps faxuw` on Linux)
22
+ * babysit the process, check the system load whether number of workers
23
+ needs to be adjusted
24
+ * edit the yaml file
25
+ * `kill -HUP` the resque pool master (hope you remembered the PID from
26
+ pstree!)
27
+ * rinse, repeat
28
+
29
+ Or you can add `gem "resque-pool-dynamic"` to your Gemfile, `require
30
+ 'resque/pool/dynamic/tasks'` to your Rakefile, and start an
31
+ interactive CLI interface that controls the resque-pool-master.
32
+
33
+ ## Example resque-pool-manager session
34
+
35
+ $ bundle exec rake resque:pool:dynamic WORKERS=foo=2:bar=2:\*=1
36
+ resque-pool-manager[21134]: started manager
37
+
38
+ Status: running, pid: 21134
39
+ Configuration:
40
+ bar: 2
41
+ "*": 1
42
+ foo: 2
43
+ Process tree:
44
+ -+= 21134 japhy resque-pool-master: managing [21136, 21135, 21137, 21138, 21141]
45
+ |--- 21135 japhy resque-1.20.0: Waiting for bar
46
+ |--- 21136 japhy resque-1.20.0: Waiting for bar
47
+ |--- 21137 japhy resque-1.20.0: Waiting for *
48
+ |--- 21138 japhy resque-1.20.0: Waiting for foo
49
+ \--- 21141 japhy resque-1.20.0: Waiting for foo
50
+
51
+ >> log.tail
52
+ resque-pool-manager[21134]: Pool contains worker PIDs: [21136, 21135, 21137, 21138, 21141]
53
+ resque-pool-worker[21135]: Starting worker portinari.local:21135:bar
54
+ resque-pool-worker[21137]: Starting worker portinari.local:21137:*
55
+
56
+ resque-pool-worker[21136]: Starting worker portinari.local:21136:bar
57
+ resque-pool-worker[21141]: Starting worker portinari.local:21141:foo
58
+ resque-pool-worker[21138]: Starting worker portinari.local:21138:foo
59
+
60
+ (log.tail is following the logfile like `tail -f` until you break with ^C)
61
+
62
+ ^C>> config :foo => 1, :bar => 4
63
+ Reloading resque-pool-master 21134 configuration
64
+ => {"bar"=>4, "*"=>1, "foo"=>1}
65
+ >> status
66
+
67
+ Status: running, pid: 21134
68
+ Configuration:
69
+ bar: 4
70
+ "*": 1
71
+ foo: 1
72
+ Process tree:
73
+ -+= 21134 japhy resque-pool-master: managing [21176, 21178, 21162, 21163, 21180, 21181]
74
+ |--- 21162 japhy resque-1.20.0: Waiting for bar
75
+ |--- 21163 japhy resque-1.20.0: Waiting for bar
76
+ |--- 21176 japhy resque-1.20.0: Waiting for bar
77
+ |--- 21178 japhy resque-1.20.0: Waiting for bar
78
+ |--- 21180 japhy resque-1.20.0: Waiting for *
79
+ \--- 21181 japhy resque-1.20.0: Waiting for foo
80
+
81
+ >> log.tail
82
+ resque-pool-manager[21134]: HUP: reload config file and reload logfiles
83
+ resque-pool-manager[21134]: Flushing logs
84
+ resque-pool-manager[21134]: HUP: gracefully shutdown old children (which have old logfiles open)
85
+ resque-pool-manager[21134]: HUP: new children will inherit new logfiles
86
+ resque-pool-worker[21163]: Starting worker portinari.local:21163:bar
87
+ resque-pool-worker[21162]: Starting worker portinari.local:21162:bar
88
+ resque-pool-manager[21134]: Reaped resque worker[21136] (status: 0) queues: bar
89
+ resque-pool-manager[21134]: Reaped resque worker[21135] (status: 0) queues: bar
90
+ resque-pool-worker[21176]: Starting worker portinari.local:21176:bar
91
+ resque-pool-worker[21178]: Starting worker portinari.local:21178:bar
92
+ resque-pool-manager[21134]: Reaped resque worker[21141] (status: 0) queues: foo
93
+ resque-pool-manager[21134]: Reaped resque worker[21138] (status: 0) queues: foo
94
+ resque-pool-worker[21180]: Starting worker portinari.local:21180:*
95
+ resque-pool-worker[21181]: Starting worker portinari.local:21181:foo
96
+ ^C>> exit
97
+ Bye!
98
+ Shutting down resque-pool-master 21134
99
+ resque-pool-manager[21134]: INT: immediate shutdown (graceful worker shutdown)
100
+ resque-pool-manager[21134]: manager finished
101
+ $ _
102
+
103
+ ## Command Line Interface
104
+
105
+ The CLI is a slightly customized Ripl shell (an IRB replacement)
106
+ started in context of a `Resque::Pool::Dynamic` instance. You can use
107
+ all Ruby you want, and call all the methods for controlling the resque
108
+ pool. A simple built-in help for the custom methods is included:
109
+
110
+ >> help
111
+ Known commands:
112
+ config - "WORKERS", as interpreted by the #parse_config_string method.
113
+ config_path - Path to the temporary config file.
114
+ exit - Finish work
115
+ has_log? - True if we have an open log file.
116
+ kill! - Send signal to a running resque-pool.
117
+ log - Open log of resque-pool process.
118
+ log.ff - Fast forward until end of file (see IO::Tail#backward).
119
+ log.rew - Rewind until beginning of file (see IO::Tail#forward).
120
+ log.rewind - Rewind until beginning of file (see IO::Tail#forward).
121
+ log.tail - Show last n lines of the file, or follow the file.
122
+ log.tail_to_eof - Print contents of the file until current end of file.
123
+ log.tail_until - Follow the file until a line matches regexp.
124
+ log_path - Logfile path.
125
+ pid - PID of the resque-pool process, or nil if it's not running.
126
+ pstree - Show child process tree by calling `pstree` system command.
127
+ reload - Reload resque-pool configuration.
128
+ start - Start resque-pool, showing startup logs.
129
+ start! - Fork a child process for resque-pool master.
130
+ status - Show current status.
131
+ stop - Stop running resque-pool, show shutdown logs.
132
+ stop! - Stop running resque-pool.
133
+ write_config - Write temporary configuration file.
134
+ For details, type: help command
135
+ >> help config
136
+ -------------------------------- Method: #config (Resque::Pool::Dynamic)
137
+ (Defined in: lib/resque/pool/dynamic/process.rb)
138
+ dynamic.config -> Hash
139
+ dynamic.config(opts) -> Hash
140
+ ------------------------------------------------------------------------
141
+ Note: Default configuration is taken from environment variable
142
+ "WORKERS", as interpreted by the #parse_config_string method.
143
+ Examples:
144
+ ---------
145
+ # config
146
+ #=> {"foo"=>1, "bar"=>2}
147
+ config :foo => 2, :baz => 7
148
+ #=> {"foo"=>2, "baz"=>7, "bar"=>2}
149
+ config :bar => 0
150
+ #=> {"foo"=>2, "baz"=>7}
151
+ Overloads:
152
+ ----------
153
+ ------------------------------------------------------------------------
154
+ dynamic.config -> Hash
155
+ ------------------------------------------------------------------------
156
+ Show current workers configuration
157
+ Returns:
158
+ --------
159
+ (Hash) -
160
+ ------------------------------------------------------------------------
161
+ dynamic.config(opts) -> Hash
162
+ ------------------------------------------------------------------------
163
+ Update workers configuration. If configuration has change and
164
+ resque-pool is running, it is reloaded.
165
+ Parameters:
166
+ -----------
167
+ (Hash) opts - Dictionary of worker process counts to update
168
+ (pass 0 or nil as value to delete all workers for a queue from pool)
169
+ Returns:
170
+ --------
171
+ (Hash) - Updated config
172
+ >> _
173
+
174
+ The built-in help is autogenerated from Yard documentation on methods;
175
+ if you're curious, look into the Rakefile for details.
176
+
177
+ ## Rake task documentation
178
+
179
+ $ bundle exec rake -D resque:pool:dynamic
180
+ rake resque:pool:dynamic
181
+ Starts a dynamic pool of Resque worker processes.
182
+
183
+ Variables:
184
+ WORKERS=worker spec - specify initial numbers of workers
185
+ (look below for format description)
186
+ NO_START=1 - don't start resque-pool master automatically
187
+ (default is to start)
188
+
189
+ Workers spec format:
190
+ queue_name=process_number[:queue_name=process_number[:...]]
191
+
192
+ queue_name can be whatever resque:work will accept as QUEUE=
193
+ parameter.
194
+
195
+ Example:
196
+ $ rake resque:pool:dynamic WORKERS=foo=1:bar=1:baz,quux,xyzzy=5:*=2
197
+ will start:
198
+ - one worker for queue 'foo'
199
+ - one worker for queue 'bar'
200
+ - five workers for queues baz,quux,xyzzy
201
+ - two workers for all queues
202
+
203
+ ## API and code structure
204
+
205
+ The Rake task, defined in `resque/pool/dynamic/tasks.rb`, is a
206
+ one-liner calling out to `Resque::Pool::Dynamic.shell`, which starts
207
+ the command line interface.
208
+
209
+ The CLI itself, defined in `resque/pool/dynamic/shell.rb`, is a thin
210
+ layer (8 lines of code + Ripl UI tweaks) over an instance of the
211
+ `Resque::Pool::Dynamic` class.
212
+
213
+ The workhorse `Resque::Pool::Dynamic` class is defined in
214
+ `resque/pool/dynamic.rb`. It can be easily instantiated in the code
215
+ independent from the CLI; the CLI commands are just instance methods
216
+ you can call then.
217
+
218
+ ### Ideas
219
+
220
+ Besides interactive, supervised work, `Resque::Pool::Dynamic` can be
221
+ used separately from the UI for dynamic, reactive management of
222
+ workers, implementing some kind of autoscaling and load distribution
223
+ logic.
224
+
225
+ It can be used during load tests to automatically optimize best number
226
+ of workers for maximum processing speed, while keeping load average
227
+ and memory or CPU usage goals.
228
+
229
+ It may also react to queue depths, e.g. temporarily reallocating idle
230
+ workers to overloaded queues or shutting them down to conserve memory,
231
+ or actively managing sudden short peak usage by stopping other workers
232
+ and switching them to peaking queue.
233
+
234
+ It may also simply allocate workers dynamically based on time of day,
235
+ to e.g. have more workers performing UI-related task in the day, which
236
+ increases perceived responsiveness, and switch them to process
237
+ long-running reports overnight when there are less users.
238
+
239
+ ## Detailed documentation and source code
240
+
241
+ - http://rubydoc.info/github/mpasternacki/resque-pool-dynamic
242
+ - http://github.com/mpasternacki/resque-pool-dynamic
data/Rakefile ADDED
@@ -0,0 +1,42 @@
1
+ require "bundler/gem_tasks"
2
+ require 'yard'
3
+
4
+ YARD::Rake::YardocTask.new do |t|
5
+ # t.files = ['lib/**/*.rb', OTHER_PATHS] # optional
6
+ t.options = ['--default-return', 'void', '--hide-void-return']
7
+ end
8
+
9
+ # Precompute JSON file with help
10
+
11
+ file 'lib/resque/pool/help.json' => Dir['lib/**/*.rb'] do
12
+ require 'yard'
13
+ require 'json'
14
+
15
+ YARD.parse('lib/**/*.rb')
16
+
17
+ rpd = YARD::Registry.at('Resque::Pool::Dynamic')
18
+ cli = YARD::Registry.all(:method).select { |o|
19
+ o.has_tag?(:api) && o.tag(:api).text == 'cli'
20
+ }
21
+
22
+ docs = Hash[cli.map{ |o| [
23
+ rpd.relative_path(o).sub('Logfile#', 'log.').sub('#', ''),
24
+ { :summary => o.docstring.summary,
25
+ :full => o.format.lines.grep(/\S/).join } ] }]
26
+
27
+ docs.update(
28
+ :exit => { :summary => 'Finish work', :full => 'Finish work (AKA quit)' })
29
+
30
+ File.open('lib/resque/pool/help.json', 'w') do |f|
31
+ f.write(JSON::dump(docs))
32
+ end
33
+ end
34
+
35
+ desc "Generate online help for the CLI"
36
+ task :gen_help => 'lib/resque/pool/help.json'
37
+
38
+ task :default => [ :gen_help, :yard ]
39
+
40
+ task 'build' => :gen_help
41
+ task 'install' => :gen_help
42
+ task 'release' => :gen_help