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