rq 0.1.7

Sign up to get free protection for your applications and to get access to all the features.
@@ -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