rq 0.1.7

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,33 @@
1
+ unless defined? $__rq_queryier__
2
+ module RQ
3
+ #{{{
4
+ LIBDIR = File::dirname(File::expand_path(__FILE__)) + File::SEPARATOR unless
5
+ defined? LIBDIR
6
+
7
+ require LIBDIR + 'mainhelper'
8
+
9
+ class Querier < MainHelper
10
+ #{{{
11
+ def query
12
+ #{{{
13
+ set_q
14
+ where_clause = @argv.join ' '
15
+ if where_clause.empty? or not STDIN.tty?
16
+ debug{ "reading where_clause from STDIN" }
17
+ while((buf = STDIN.gets))
18
+ buf.strip!
19
+ buf.gsub! %r/#.*$/o, ''
20
+ next if buf.empty?
21
+ where_clause << "#{ buf } "
22
+ end
23
+ end
24
+ @q.qdb.transaction_retries = 1
25
+ @q.query where_clause
26
+ #}}}
27
+ end
28
+ #}}}
29
+ end # class Queryier
30
+ #}}}
31
+ end # module RQ
32
+ $__rq_queryier__ = __FILE__
33
+ end
@@ -0,0 +1,72 @@
1
+ unless defined? $__rq_refresher__
2
+ module RQ
3
+ #{{{
4
+ LIBDIR = File::dirname(File::expand_path(__FILE__)) + File::SEPARATOR unless
5
+ defined? LIBDIR
6
+
7
+ class Refresher
8
+ #{{{
9
+ SIGNALS = %w(SIGTERM SIGINT SIGKILL)
10
+ attr :path
11
+ attr :pid
12
+ attr :refresh_rate
13
+ def initialize path, refresh_rate = 8
14
+ #{{{
15
+ @path = path
16
+ File::stat path
17
+ @refresh_rate = Float refresh_rate
18
+ @pipe = IO::pipe
19
+ if((@pid = Util::fork))
20
+ @pipe.last.close
21
+ @pipe = @pipe.first
22
+ @thread = Thread::new{loop{@pipe.gets}}
23
+ Process::detach @pid
24
+ else
25
+ begin
26
+ pid = Process::pid
27
+ ppid = Process::ppid
28
+ $0 = "#{ path }.refresher.#{ pid }"
29
+ SIGNALS.each{|sig| trap(sig){ raise }}
30
+ @pipe.first.close
31
+ @pipe = @pipe.last
32
+ loop do
33
+ FileUtils::touch @path
34
+ sleep @refresh_rate
35
+ Process::kill 0, ppid
36
+ @pipe.puts pid
37
+ end
38
+ rescue Exception => e
39
+ exit!
40
+ end
41
+ end
42
+ #}}}
43
+ end
44
+ def kill
45
+ #{{{
46
+ begin
47
+ @thread.kill rescue nil
48
+ @pipe.close rescue nil
49
+ SIGNALS.each{|sig| Process::kill sig, @pid rescue nil}
50
+ ensure
51
+ =begin
52
+ n = 42
53
+ dead = false
54
+ begin
55
+ n.times do |i|
56
+ Process::kill 0, @pid
57
+ sleep 1
58
+ end
59
+ rescue Errno::ESRCH
60
+ dead = true
61
+ end
62
+ raise "runaway refresher <#{ @pid }> must be killed!" unless dead
63
+ =end
64
+ end
65
+ #}}}
66
+ end
67
+ #}}}
68
+ end # class Refresher
69
+ #}}}
70
+ end # module RQ
71
+ $__rq_refresher__ = __FILE__
72
+ end
@@ -0,0 +1,46 @@
1
+ unless defined? $__rq_sleepcycle__
2
+ module RQ
3
+ #{{{
4
+ LIBDIR = File::dirname(File::expand_path(__FILE__)) + File::SEPARATOR unless
5
+ defined? LIBDIR
6
+
7
+ #
8
+ # the sleepcycle class provides timeouts for better than average polling
9
+ # performance
10
+ #
11
+ class SleepCycle < Array
12
+ #{{{
13
+ attr :min
14
+ attr :max
15
+ attr :range
16
+ attr :inc
17
+ def initialize min, max, inc
18
+ #{{{
19
+ @min, @max, @inc = Float(min), Float(max), Float(inc)
20
+ @range = @max - @min
21
+ raise RangeError, "max < min" if @max < @min
22
+ raise RangeError, "inc > range" if @inc > @range
23
+ s = @min
24
+ push(s) and s += @inc while(s <= @max)
25
+ self[-1] = @max if self[-1] < @max
26
+ reset
27
+ #}}}
28
+ end
29
+ def next
30
+ #{{{
31
+ ret = self[@idx]
32
+ @idx = ((@idx + 1) % self.size)
33
+ ret
34
+ #}}}
35
+ end
36
+ def reset
37
+ #{{{
38
+ @idx = 0
39
+ #}}}
40
+ end
41
+ #}}}
42
+ end # class SleepCycle
43
+ #}}}
44
+ end # module RQ
45
+ $__rq_sleepcycle__ = __FILE__
46
+ end
@@ -0,0 +1,25 @@
1
+ unless defined? $__rq_snapshotter__
2
+ module RQ
3
+ #{{{
4
+ LIBDIR = File::dirname(File::expand_path(__FILE__)) + File::SEPARATOR unless
5
+ defined? LIBDIR
6
+
7
+ require LIBDIR + 'mainhelper'
8
+
9
+ class Snapshotter < MainHelper
10
+ #{{{
11
+ def snapshot
12
+ #{{{
13
+ set_q
14
+ qtmp = @argv.shift
15
+ raise "<#{ qtmp }> exists" if qtmp and test(?e, qtmp)
16
+ qss = @q.snapshot qtmp, @options['retries']
17
+ info{ "created q snapshot <#{ qtmp }>" }
18
+ #}}}
19
+ end
20
+ #}}}
21
+ end # class Snapshotter
22
+ #}}}
23
+ end # module RQ
24
+ $__rq_snapshotter__ = __FILE__
25
+ end
@@ -0,0 +1,22 @@
1
+ unless defined? $__rq_statuslister__
2
+ module RQ
3
+ #{{{
4
+ LIBDIR = File::dirname(File::expand_path(__FILE__)) + File::SEPARATOR unless
5
+ defined? LIBDIR
6
+
7
+ require LIBDIR + 'mainhelper'
8
+
9
+ class StatusLister < MainHelper
10
+ #{{{
11
+ def statuslist
12
+ #{{{
13
+ set_q
14
+ @q.status(*@argv)
15
+ #}}}
16
+ end
17
+ #}}}
18
+ end # class StatusLister
19
+ #}}}
20
+ end # module RQ
21
+ $__rq_statuslister__ = __FILE__
22
+ end
@@ -0,0 +1,90 @@
1
+ unless defined? $__rq_submitter__
2
+ module RQ
3
+ #{{{
4
+ LIBDIR = File::dirname(File::expand_path(__FILE__)) + File::SEPARATOR unless
5
+ defined? LIBDIR
6
+
7
+ require LIBDIR + 'mainhelper'
8
+
9
+ class Submitter < MainHelper
10
+ #{{{
11
+ def submit
12
+ #{{{
13
+ set_q
14
+
15
+ priority = @options['priority'] || 0
16
+ debug{ "priority <#{ priority }>" }
17
+
18
+ tag = @options['tag']
19
+ debug{ "tag <#{ tag }>" }
20
+
21
+ infile = @options['infile']
22
+ debug{ "infile <#{ infile }>" }
23
+
24
+ jobs = []
25
+
26
+ unless @argv.empty?
27
+ job = Job::new
28
+ job['command'] = @argv.join(' ')
29
+ job['priority'] = priority
30
+ job['tag'] = tag
31
+ jobs << job
32
+ end
33
+
34
+ loadio = lambda do |io, path|
35
+ while((line = io.gets))
36
+ if line =~ %r/^---\s*$/o
37
+ loaded = YAML::load io
38
+ raise "no jobs in <#{ path }>" unless
39
+ Array === loaded and
40
+ Hash === loaded.first and Hash === loaded.last
41
+ loaded.each{|job| jobs << job}
42
+ loaded = nil
43
+ else
44
+ line.gsub!(%r/(?:^\s+)|(?:\s+$)|(?:#.*$)/o, '')
45
+ next if line.empty?
46
+ job = Job::new
47
+ job['command'] = line
48
+ job['priority'] = priority
49
+ job['tag'] = tag
50
+ jobs << job
51
+ end
52
+ end
53
+ end
54
+
55
+ if infile
56
+ open(infile) do |f|
57
+ debug{ "reading jobs from <#{ infile }>" }
58
+ loadio.call f, infile
59
+ end
60
+ end
61
+
62
+ if jobs.empty? or not STDIN.tty?
63
+ debug{ "reading jobs from <stdin>" }
64
+ loadio.call STDIN, 'stdin'
65
+ end
66
+
67
+ raise "no jobs specified!" if jobs.empty?
68
+
69
+ if @options['quiet']
70
+ @q.submit(*jobs)
71
+ else
72
+ puts '---'
73
+ fields = @q.db.fields
74
+ @q.submit(*jobs) do |tuple|
75
+ puts '-'
76
+ fields.each{|f| puts " #{ f }: #{ tuple[ f ] }"}
77
+ end
78
+ end
79
+
80
+ jobs = nil
81
+
82
+ self
83
+ #}}}
84
+ end
85
+ #}}}
86
+ end # class Submitter
87
+ #}}}
88
+ end # module RQ
89
+ $__rq_submitter__ = __FILE__
90
+ end
@@ -0,0 +1,95 @@
1
+ unless defined? $__rq_updater__
2
+ module RQ
3
+ #{{{
4
+ LIBDIR = File::dirname(File::expand_path(__FILE__)) + File::SEPARATOR unless
5
+ defined? LIBDIR
6
+
7
+ require LIBDIR + 'mainhelper'
8
+
9
+ class Updater < MainHelper
10
+ #{{{
11
+ def update
12
+ #{{{
13
+ set_q
14
+ jids = []
15
+ kvs = {}
16
+ #
17
+ # scan argv for jids to update
18
+ #
19
+ while((arg = @argv.first))
20
+ case arg
21
+ when %r/^\d+$/o
22
+ jids << Integer(arg)
23
+ when %r/^jid\s*=\s*(\d+)$/io
24
+ jids << Integer($1)
25
+ when %r/^p(?:ending)$/o
26
+ jids << 'pending'
27
+ else
28
+ break
29
+ end
30
+ @argv.shift
31
+ end
32
+ #
33
+ # scan argv for key=val pairs
34
+ #
35
+ cmdline = @argv.join(' ')
36
+ keyeqpat = %r/\s*([^\s=]+)\s*=\s*/
37
+ keyvals = cmdline.split keyeqpat
38
+ keyvals.shift
39
+ loop do
40
+ k = keyvals.shift
41
+ v = keyvals.shift
42
+ break if k.nil? and v.nil?
43
+ raise "syntax error in update <#{ cmdline }> @ <#{ k }>" unless
44
+ k and v
45
+ k.strip!
46
+ v.strip!
47
+ kvs[k] = v
48
+ end
49
+ raise "no updates" if kvs.empty?
50
+ #
51
+ # scan stdin for jids to update if in pipeline
52
+ #
53
+ #if jids.empty? or not STDIN.tty?
54
+ if not STDIN.tty?
55
+ #pat = %r/^(?:\s*jid\s*:)?\s*(\d+)\s*$/io
56
+ while((line = STDIN.gets))
57
+ case line
58
+ when %r/^(?:\s*jid\s*:)?\s*(\d+)\s*$/io
59
+ jids << Integer($1)
60
+ when %r/^\s*p(?:ending)\s*$/io
61
+ jids << 'pending'
62
+ else
63
+ next
64
+ end
65
+ end
66
+ end
67
+ #jids.map!{|jid| jid =~ %r/^\s*\d+\s*$/o ? Integer(jid) : jid}
68
+ #raise "no jids" if jids.empty?
69
+ #
70
+ # if no jids were specified simply update all pending jobs
71
+ #
72
+ jids << 'pending' if jids.empty?
73
+ #
74
+ # apply the update
75
+ #
76
+ if @options['quiet']
77
+ @q.update(kvs,*jids)
78
+ else
79
+ puts '---'
80
+ tuples = @q.update(kvs,*jids)
81
+ fields = nil
82
+ tuples.each do |tuple|
83
+ puts '-'
84
+ fields ||= tuple.fields
85
+ fields.each{|f| puts " #{ f }: #{ tuple[f] }" }
86
+ end
87
+ end
88
+ #}}}
89
+ end
90
+ #}}}
91
+ end # class Updater
92
+ #}}}
93
+ end # module RQ
94
+ $__rq_updater__ = __FILE__
95
+ end
@@ -0,0 +1,609 @@
1
+ unless defined? $__rq_usage__
2
+ module RQ
3
+ #{{{
4
+ LIBDIR = File::dirname(File::expand_path(__FILE__)) + File::SEPARATOR unless
5
+ defined? LIBDIR
6
+
7
+ require LIBDIR + 'util'
8
+
9
+ module Usage
10
+ #{{{
11
+ def cget const
12
+ #{{{
13
+ begin
14
+ klass::const_get const
15
+ rescue NameError
16
+ nil
17
+ end
18
+ #}}}
19
+ end
20
+ def usage opts = {}
21
+ #{{{
22
+ port = getopt 'port', opts
23
+ long = getopt 'long', opts
24
+
25
+ port = STDERR if port.nil?
26
+
27
+ if(long and (txt = cget 'USAGE'))
28
+ port << txt << "\n"
29
+ elsif((txt = cget 'USAGE_BANNER'))
30
+ port << txt << "\n"
31
+ else
32
+ port << "#{ $0 } [options]* [args]*" << "\n"
33
+ end
34
+
35
+ if((optspec = cget 'OPTSPEC'))
36
+ port << 'OPTIONS' << "\n"
37
+ optspec.each do |os|
38
+ a, b, c = os
39
+ long, short, desc = nil
40
+ [a,b,c].each do |word|
41
+ next unless word
42
+ word.strip!
43
+ case word
44
+ when %r/^--[^-]/o
45
+ long = word
46
+ when %r/^-[^-]/o
47
+ short = word
48
+ else
49
+ desc = word
50
+ end
51
+ end
52
+ spec = ((long and short) ? [long, short] : [long])
53
+ if spec
54
+ port << columnize(spec.join(', '), 80, 2)
55
+ port << "\n"
56
+ end
57
+ if desc
58
+ port << columnize(desc, 80, 8)
59
+ port << "\n"
60
+ end
61
+ end
62
+ port << "\n"
63
+ end
64
+
65
+ if((txt = cget 'EXAMPLES'))
66
+ port << txt << "\n"
67
+ end
68
+
69
+ port
70
+ #}}}
71
+ end
72
+ module_function :usage
73
+ public :usage
74
+
75
+ PROGNAM = 'rq'
76
+
77
+ USAGE_BANNER =
78
+ #{{{
79
+ <<-usage_banner
80
+ NAME
81
+ #{ PROGNAM } v#{ VERSION }
82
+
83
+ SYNOPSIS
84
+ #{ PROGNAM } (queue | export RQ_Q=q) mode [mode_args]* [options]*
85
+ usage_banner
86
+ #}}}
87
+
88
+ USAGE =
89
+ #{{{
90
+ <<-usage
91
+ #{ USAGE_BANNER }
92
+
93
+ DESCRIPTION
94
+ ruby queue (rq) is a tool used to create instant linux clusters by managing
95
+ sqlite databases as nfs mounted priority work queues. multiple instances of
96
+ #{ PROGNAM } running from multiples hosts can work from these queues to
97
+ distribute processing load to n nodes - bringing many dozens of otherwise
98
+ powerful cpus to their knees with a single blow. clearly this software should
99
+ be kept out of the hands of free radicals, seti enthusiasts, and j. safran.
100
+
101
+ the central concept of #{ PROGNAM } is that n nodes work in isolation to pull
102
+ jobs from an central nfs mounted work priority work queue in a synchronized
103
+ fashion. the nodes have absolutely no knowledge of each other and all
104
+ communication if done via the queue meaning that, so long as the queue is
105
+ available via nfs and a single node is running jobs from it, the system will
106
+ continue to process jobs. there is no centralized process whatsoever - all
107
+ nodes work to take jobs from the queue and run them as fast as possible. this
108
+ creates a system which load balances automatically and is robust in face of
109
+ node failures.
110
+
111
+ the first argument to any #{ PROGNAM } command is the name of the queue. this
112
+ name may be omitted if, and only if, the environment variable RQ_Q has been
113
+ set to contain the absolute path of target queue.
114
+
115
+ #{ PROGNAM } operates in one of the modes create, submit, list, status,
116
+ delete, update, query, execute, configure, snapshot, lock, backup, help, or
117
+ feed. depending on the mode of operation and the options used the meaning of
118
+ 'mode_args' may change.
119
+
120
+ MODES
121
+
122
+ the following mode abbreviations exist
123
+
124
+ c => create
125
+ s => submit
126
+ l => list
127
+ ls => list
128
+ t => status
129
+ d => delete
130
+ rm => delete
131
+ u => update
132
+ q => query
133
+ e => execute
134
+ C => configure
135
+ S => snapshot
136
+ L => lock
137
+ b => backup
138
+ h => help
139
+ f => feed
140
+
141
+ create, c :
142
+
143
+ create a queue. the queue must be located on an nfs mounted file system
144
+ visible from all nodes intended to run jobs from it.
145
+
146
+ examples :
147
+
148
+ 0) to create a queue
149
+ ~ > #{ PROGNAM } /path/to/nfs/mounted/q create
150
+ or simply
151
+ ~ > #{ PROGNAM } /path/to/nfs/mounted/q c
152
+
153
+
154
+ submit, s :
155
+
156
+ submit jobs to a queue to be proccesed by a feeding node. any 'mode_args'
157
+ are taken as the command to run. note that 'mode_args' are subject to shell
158
+ expansion - if you don't understand what this means do not use this feature
159
+ and pass jobs on stdin.
160
+
161
+ when running in submit mode a file may by specified as a list of commands to
162
+ run using the '--infile, -i' option. this file is taken to be a newline
163
+ separated list of commands to submit, blank lines and comments (#) are
164
+ allowed. if submitting a large number of jobs the input file method is
165
+ MUCH, more efficient. if no commands are specified on the command line #{ PROGNAM }
166
+ automatically reads them from STDIN. yaml formatted files are also allowed
167
+ as input (http://www.yaml.org/) - note that the output of nearly all #{ PROGNAM }
168
+ commands is valid yaml and may, therefore, be piped as input into the submit
169
+ command.
170
+
171
+ when submitting the '--priority, -p' option can be used here to determine
172
+ the priority of jobs. priorities may be any whole number - zero is the
173
+ default. note that submission of a high priority job will NOT supplant
174
+ currently running low priority jobs, but higher priority jobs WILL always
175
+ migrate above lower priority jobs in the queue in order that they be run as
176
+ soon as possible. constant submission of high priority jobs may create a
177
+ starvation situation whereby low priority jobs are never allowed to run.
178
+ avoiding this situation is the responsibility of the user. the only
179
+ guaruntee #{ PROGNAM } makes regarding job execution is that jobs are
180
+ executed in an 'oldest highest priority' order and that running jobs are
181
+ never supplanted.
182
+
183
+ examples :
184
+
185
+ 0) submit the job ls to run on some feeding host
186
+
187
+ ~ > #{ PROGNAM } q s ls
188
+
189
+ 1) submit the job ls to run on some feeding host, at priority 9
190
+
191
+ ~ > #{ PROGNAM } -p9 q s ls
192
+
193
+ 2) submit 42000 jobs (quietly) from a command file.
194
+
195
+ ~ > wc -l cmdfile
196
+ 42000
197
+ ~ > #{ PROGNAM } q s -q < cmdfile
198
+
199
+ 3) submit 42 priority 9 jobs from a command file.
200
+
201
+ ~ > wc -l cmdfile
202
+ 42
203
+ ~ > #{ PROGNAM } -p9 q s < cmdfile
204
+
205
+ 4) submit 42 priority 9 jobs from a command file, marking them as
206
+ 'important' using the '--tag, -t' option.
207
+
208
+ ~ > wc -l cmdfile
209
+ 42
210
+ ~ > #{ PROGNAM } -p9 -timportant q s < cmdfile
211
+
212
+ 5) re-submit all the 'important' jobs (see 'query' section below)
213
+
214
+ ~ > #{ PROGNAM } q query tag=important | #{ PROGNAM } q s
215
+
216
+ 6) re-submit all jobs which are already finished (see 'list' section
217
+ below)
218
+
219
+ ~ > #{ PROGNAM } q l f | #{ PROGNAM } q s
220
+
221
+
222
+ list, l, ls :
223
+
224
+ list mode lists jobs of a certain state or job id. state may be one of
225
+ pending, running, finished, dead, or all. any 'mode_args' that are numbers
226
+ are taken to be job id's to list.
227
+
228
+ states may be abbreviated to uniqueness, therefore the following shortcuts
229
+ apply :
230
+
231
+ p => pending
232
+ r => running
233
+ f => finished
234
+ d => dead
235
+ a => all
236
+
237
+ examples :
238
+
239
+ 0) show everything in q
240
+ ~ > #{ PROGNAM } q list all
241
+ or
242
+ ~ > #{ PROGNAM } q l all
243
+ or
244
+ ~ > export RQ_Q=q
245
+ ~ > #{ PROGNAM } l
246
+
247
+ 1) show q's pending jobs
248
+ ~ > #{ PROGNAM } q list pending
249
+
250
+ 2) show q's running jobs
251
+ ~ > #{ PROGNAM } q list running
252
+
253
+ 3) show q's finished jobs
254
+ ~ > #{ PROGNAM } q list finshed
255
+
256
+ 4) show job id 42
257
+ ~ > #{ PROGNAM } q l 42
258
+
259
+
260
+ status, t :
261
+
262
+ status mode shows the global state the queue. there are no 'mode_args'.
263
+ the meaning of each state is as follows:
264
+
265
+ pending => no feeder has yet taken this job
266
+ running => a feeder has taken this job
267
+ finished => a feeder has finished this job
268
+ dead => #{ PROGNAM } died while running a job, has restarted, and moved
269
+ this job to the dead state
270
+
271
+ note that #{ PROGNAM } cannot move jobs into the dead state unless it has
272
+ been restarted. this is because no node has any knowledge of other nodes
273
+ and cannot possibly know if a job was started on a node that died, or is
274
+ simply taking a very long time. only the node that dies, upon restart, can
275
+ determine that is has jobs that 'were started before it started' and move
276
+ these jobs into the dead state. normally only a machine crash would cause a
277
+ job to be placed into the dead state. dead jobs are never automatically
278
+ restarted, this is the responsibility of an operator.
279
+
280
+ examples :
281
+
282
+ 0) show q's status
283
+
284
+ ~ > #{ PROGNAM } q t
285
+
286
+
287
+ delete, d :
288
+
289
+ delete combinations of pending, running, finished, dead, or jobs specified
290
+ by jid. the delete mode is capable of parsing the output of list and query
291
+ modes, making it possible to create custom filters to delete jobs meeting
292
+ very specific conditions.
293
+
294
+ 'mode_args' are the same as for list. note that while it is possible to
295
+ delete a running job, but there is no way to actually STOP it mid execution
296
+ since the node doing the deleteing has no way to communicate this
297
+ information to the (probably) remote execution node. therefore you should
298
+ use the 'delete running' feature with care and only for housekeeping
299
+ purposes or to prevent future jobs from being scheduled.
300
+
301
+ examples :
302
+
303
+ 0) delete all pending, running, and finished jobs from a queue
304
+
305
+ ~ > #{ PROGNAM } q d all
306
+
307
+ 1) delete all pending jobs from a queue
308
+
309
+ ~ > #{ PROGNAM } q d p
310
+
311
+ 2) delete all finished jobs from a queue
312
+
313
+ ~ > #{ PROGNAM } q d f
314
+
315
+ 3) delete jobs via hand crafted filter program
316
+
317
+ ~ > #{ PROGNAM } q list | yaml_filter_prog | #{ PROGNAM } q d
318
+
319
+
320
+ update, u :
321
+
322
+ update assumes all leading arguments are jids to update with subsequent
323
+ key=value pairs. currently only the 'command', 'priority', and 'tag' fields
324
+ of pending jobs can be updated.
325
+
326
+ examples:
327
+
328
+ 0) update the priority of job 42
329
+
330
+ ~ > #{ PROGNAM } q update 42 priority=7
331
+
332
+ 1) update the priority of all pending jobs
333
+
334
+ ~ > #{ PROGNAM } q update pending priority=7
335
+
336
+ 2) query jobs with a command matching 'foobar' and update their command
337
+ to be 'barfoo'
338
+
339
+ ~ > #{ PROGNAM } q q "command like '%foobar%'" |\\
340
+ #{ PROGNAM } q u command=barfoo
341
+
342
+
343
+ query, q :
344
+
345
+ query exposes the database more directly the user, evaluating the where
346
+ clause specified on the command line (or from STDIN). this feature can be
347
+ used to make a fine grained slection of jobs for reporting or as input into
348
+ the delete command. you must have a basic understanding of SQL syntax to
349
+ use this feature, but it is fairly intuitive in this limited capacity.
350
+
351
+ examples:
352
+
353
+ 0) show all jobs submitted within a specific 10 minute range
354
+
355
+ ~ > #{ PROGNAM } q query "started >= '2004-06-29 22:51:00' and started < '2004-06-29 22:51:10'"
356
+
357
+ 1) shell quoting can be tricky here so input on STDIN is also allowed to
358
+ avoid shell expansion
359
+
360
+ ~ > cat constraints.txt
361
+ started >= '2004-06-29 22:51:00' and
362
+ started < '2004-06-29 22:51:10'
363
+
364
+ ~ > #{ PROGNAM } q query < contraints.txt
365
+ or (same thing)
366
+
367
+ ~ > cat contraints.txt| #{ PROGNAM } q query
368
+
369
+ ** in general all but numbers will need to be surrounded by single quotes **
370
+
371
+ 2) this query output might then be used to delete those jobs
372
+
373
+ ~ > cat contraints.txt | #{ PROGNAM } q q | #{ PROGNAM } q d
374
+
375
+ 3) show all jobs which are either finished or dead
376
+
377
+ ~ > #{ PROGNAM } q q "state='finished' or state='dead'"
378
+
379
+ 4) show all jobs which have non-zero exit status
380
+
381
+ ~ > #{ PROGNAM } q query exit_status!=0
382
+
383
+ 5) if you plan to query groups of jobs with some common feature consider
384
+ using the '--tag, -t' feature of the submit mode which allows a user to
385
+ tag a job with a user defined string which can then be used to easily
386
+ query that job group
387
+
388
+ ~ > #{ PROGNAM } q submit --tag=my_jobs < joblist
389
+ ~ > #{ PROGNAM } q query tag=my_jobs
390
+
391
+
392
+ execute, e :
393
+
394
+ execute mode is to be used by expert users with a knowledge of sql syntax
395
+ only. it follows the locking protocol used by #{ PROGNAM } and then allows
396
+ the user to execute arbitrary sql on the queue. unlike query mode a write
397
+ lock on the queue is obtained allowing a user to definitively shoot
398
+ themselves in the foot. for details on a queue's schema the file
399
+ 'db.schema' in the queue directory should be examined.
400
+
401
+ examples :
402
+
403
+ 0) list all jobs
404
+
405
+ ~ > #{ PROGNAM } q execute 'select * from jobs'
406
+
407
+
408
+ configure, C :
409
+
410
+ this mode is not supported yet.
411
+
412
+
413
+ snapshot, p :
414
+
415
+ snapshot provides a means of taking a snapshot of the q. use this feature
416
+ when many queries are going to be run; for example when attempting to figure
417
+ out a complex pipeline command your test queries will not compete with the
418
+ feeders for the queue's lock. you should use this option whenever possible
419
+ to avoid lock competition.
420
+
421
+ examples:
422
+
423
+ 0) take a snapshot using default snapshot naming, which is made via the
424
+ basename of the q plus '.snapshot'
425
+
426
+ ~ > #{ PROGNAM } /path/to/nfs/q snapshot
427
+
428
+ 1) use this snapshot to chceck status
429
+
430
+ ~ > #{ PROGNAM } ./q.snapshot status
431
+
432
+ 2) use the snapshot to see what's running on which host
433
+
434
+ ~ > #{ PROGNAM } ./q.snapshot list running | grep `hostname`
435
+
436
+ note that there is also a snapshot option - this option is not the same as
437
+ the snapshot command. the option can be applied to ANY command. if in
438
+ effect then that command will be run on a snapshot of the database and the
439
+ snapshot then immediately deleted. this is really only useful if one were
440
+ to need to run a command against a very heavily loaded queue and did not
441
+ wish to wait to obtain the lock. eg.
442
+
443
+ 0) get the status of a heavily loaded queue
444
+
445
+ ~ > #{ PROGNAM } q t --snapshot
446
+
447
+ 1) same as above
448
+
449
+ ~ > #{ PROGNAM } q t -s
450
+
451
+
452
+ lock, L :
453
+
454
+ lock the queue and then execute an arbitrary shell command. lock mode uses
455
+ the queue's locking protocol to safely obtain a lock of the specified type
456
+ and execute a command on the user's behalf. lock type must be one of
457
+
458
+ (r)ead | (sh)ared | (w)rite | (ex)clusive
459
+
460
+ examples :
461
+
462
+ 0) get a read lock on the queue and make a backup
463
+
464
+ ~ > #{ PROGNAM } q L read -- cp -r q q.bak
465
+
466
+ (the '--' is needed to tell #{ PROGNAM } to stop parsing command line
467
+ options which allows the '-r' to be passed to the 'cp' command)
468
+
469
+
470
+ backup, b :
471
+
472
+ backup mode is exactly the same as getting a read lock on the queue and
473
+ making a copy of it. this mode is provided as a convenience.
474
+
475
+ 0) make a backup of the queue using default naming ( qname + timestamp + .bak )
476
+
477
+ ~ > #{ PROGNAM } q b
478
+
479
+ 1) make a backup of the queue as 'q.bak'
480
+
481
+ ~ > #{ PROGNAM } q b q.bak
482
+
483
+ help, h :
484
+
485
+ this message
486
+
487
+ examples :
488
+
489
+ 0) get this message
490
+
491
+ ~> #{ PROGNAM } q help
492
+ or
493
+ ~> #{ PROGNAM } help
494
+
495
+
496
+ feed, f :
497
+
498
+ take jobs from the queue and run them on behalf of the submitter as quickly
499
+ as possible. jobs are taken from the queue in an 'oldest highest priority'
500
+ first order.
501
+
502
+ feeders can be run from any number of nodes allowing you to harness the CPU
503
+ power of many nodes simoultaneously in order to more effectively clobber
504
+ your network, anoy your sysads, and set output raids on fire.
505
+
506
+ the most useful method of feeding from a queue is to do so in daemon mode so
507
+ that if the process loses it's controling terminal it will not exit when you
508
+ exit your terminal session. use the '--daemon, -d' option to accomplish
509
+ this. by default only one feeding process per host per queue is allowed to
510
+ run at any given moment. because of this it is acceptable to start a feeder
511
+ at some regular interval from a cron entry since, if a feeder is alreay
512
+ running, the process will simply exit and otherwise a new feeder will be
513
+ started. in this way you may keep feeder processing running even acroess
514
+ machine reboots without requiring sysad intervention to add an entry to the
515
+ machine's startup tasks.
516
+
517
+
518
+ examples :
519
+
520
+ 0) feed from a queue verbosely for debugging purposes, using a minimum and
521
+ maximum polling time of 2 and 4 respectively. you would NEVER specify
522
+ polling times this brief except for debugging purposes!!!
523
+
524
+ ~ > #{ PROGNAM } q feed -v4 -m2 -M4
525
+
526
+ 1) same as above, but viewing the executed sql as it is sent to the
527
+ database
528
+
529
+ ~ > RQ_SQL_DEBUG=1 #{ PROGNAM } q f -v4 -m2 -M4
530
+
531
+ 2) feed from a queue in daemon mode - logging to /home/ahoward/rq.log
532
+
533
+ ~ > #{ PROGNAM } q f -d -l/home/ahoward/rq.log
534
+
535
+ log rolling in daemon mode is automatic so your logs should never need
536
+ to be deleted to prevent disk overflow.
537
+
538
+ 3) use something like this sample crontab entry to keep a feeder running
539
+ forever - it attempts to (re)start every fifteen minutes but exits if
540
+ another process is already feeding.
541
+
542
+ #
543
+ # your crontab file - sample only
544
+ #
545
+
546
+ */15 * * * * /full/path/to/bin/rq /full/path/to/nfs/mounted/q f -d -l/home/username/cfq.log -q
547
+
548
+ the '--quiet, -q' here tells #{ PROGNAM } to exit quietly (no STDERR)
549
+ when another process is found to already be feeding so that no cron
550
+ message would be sent under these conditions.
551
+
552
+
553
+ NOTES
554
+ - realize that your job is going to be running on a remote host and this has
555
+ implications. paths, for example, should be absolute, not relative.
556
+ specifically the submitted job script must be visible from all hosts
557
+ currently feeding from a queue as must be the input and output
558
+ files/directories.
559
+
560
+ - jobs are currently run under the bash shell using the --login option.
561
+ therefore any settings in your .bashrc will apply - specifically your PATH
562
+ setting. you should not, however, rely on jobs running with any given
563
+ environment.
564
+
565
+ - you need to consider __CAREFULLY__ what the ramifications of having multiple
566
+ instances of your program all potentially running at the same time will be.
567
+ for instance, it is beyond the scope of #{ PROGNAM } to ensure multiple
568
+ instances of a given program will not overwrite each others output files.
569
+ coordination of programs is left entirely to the user.
570
+
571
+ - the list of finished jobs will grow without bound unless you sometimes
572
+ delete some (all) of them. the reason for this is that #{ PROGNAM } cannot
573
+ know when the user has collected the exit_status of a given job, and so
574
+ keeps this information in the queue forever until instructed to delete it.
575
+ if you have collected the exit_status of you job(s) it is not an error to
576
+ then delete that job from the finished list - the information is kept for
577
+ your informational purposes only. in a production system it would be normal
578
+ to periodically save, and then delete, all finished jobs.
579
+
580
+ ENVIRONMENT
581
+ RQ_Q: set to the full path of nfs mounted queue
582
+
583
+ the queue argument to all commands may be omitted if, and only if, the
584
+ environment variable 'RQ_Q' contains the full path to the q. eg.
585
+
586
+ ~ > export RQ_Q=/full/path/to/my/q
587
+
588
+ this feature can save a considerable amount of typing for those weak of
589
+ wrist.
590
+
591
+ DIAGNOSTICS
592
+ success : $? == 0
593
+ failure : $? != 0
594
+
595
+ AUTHOR
596
+ #{ AUTHOR }
597
+
598
+ BUGS
599
+ 0 < bugno && bugno <= 42
600
+
601
+ reports to #{ AUTHOR }
602
+ usage
603
+ #}}}
604
+ #}}}
605
+ end # module Usage
606
+ #}}}
607
+ end # module RQ
608
+ $__rq_usage__ = __FILE__
609
+ end