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.
- data/DEPENDS +5 -0
- data/HISTORY +26 -0
- data/README +552 -0
- data/TODO +13 -0
- data/VERSION +1 -0
- data/bin/rq +391 -0
- data/bin/rq-0.1.7 +410 -0
- data/install.rb +143 -0
- data/lib/rq-0.1.7.rb +82 -0
- data/lib/rq-0.1.7/backer.rb +27 -0
- data/lib/rq-0.1.7/configfile.rb +78 -0
- data/lib/rq-0.1.7/configurator.rb +36 -0
- data/lib/rq-0.1.7/creator.rb +23 -0
- data/lib/rq-0.1.7/defaultconfig.txt +5 -0
- data/lib/rq-0.1.7/deleter.rb +39 -0
- data/lib/rq-0.1.7/executor.rb +41 -0
- data/lib/rq-0.1.7/feeder.rb +367 -0
- data/lib/rq-0.1.7/job.rb +51 -0
- data/lib/rq-0.1.7/jobqueue.rb +432 -0
- data/lib/rq-0.1.7/jobrunner.rb +63 -0
- data/lib/rq-0.1.7/jobrunnerdaemon.rb +179 -0
- data/lib/rq-0.1.7/lister.rb +22 -0
- data/lib/rq-0.1.7/locker.rb +37 -0
- data/lib/rq-0.1.7/logging.rb +117 -0
- data/lib/rq-0.1.7/mainhelper.rb +53 -0
- data/lib/rq-0.1.7/qdb.rb +634 -0
- data/lib/rq-0.1.7/querier.rb +33 -0
- data/lib/rq-0.1.7/refresher.rb +72 -0
- data/lib/rq-0.1.7/sleepcycle.rb +46 -0
- data/lib/rq-0.1.7/snapshotter.rb +25 -0
- data/lib/rq-0.1.7/statuslister.rb +22 -0
- data/lib/rq-0.1.7/submitter.rb +90 -0
- data/lib/rq-0.1.7/updater.rb +95 -0
- data/lib/rq-0.1.7/usage.rb +609 -0
- data/lib/rq-0.1.7/util.rb +286 -0
- data/lib/rq.rb +84 -0
- data/rdoc.cmd +2 -0
- data/rq +2 -0
- data/rq.gemspec +36 -0
- data/rq.help +552 -0
- data/white_box/crontab +2 -0
- data/white_box/killrq +18 -0
- data/white_box/rq_killer +27 -0
- metadata +126 -0
@@ -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
|