rq-ruby1.8 3.4.3
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile +22 -0
- data/Gemfile.lock +22 -0
- data/INSTALL +166 -0
- data/LICENSE +10 -0
- data/Makefile +6 -0
- data/README +1183 -0
- data/Rakefile +37 -0
- data/TODO +24 -0
- data/TUTORIAL +230 -0
- data/VERSION +1 -0
- data/bin/rq +902 -0
- data/bin/rqmailer +865 -0
- data/example/a.rb +7 -0
- data/extconf.rb +198 -0
- data/gemspec.rb +40 -0
- data/install.rb +210 -0
- data/lib/rq.rb +155 -0
- data/lib/rq/arrayfields.rb +371 -0
- data/lib/rq/backer.rb +31 -0
- data/lib/rq/configfile.rb +82 -0
- data/lib/rq/configurator.rb +40 -0
- data/lib/rq/creator.rb +54 -0
- data/lib/rq/cron.rb +144 -0
- data/lib/rq/defaultconfig.txt +5 -0
- data/lib/rq/deleter.rb +51 -0
- data/lib/rq/executor.rb +40 -0
- data/lib/rq/feeder.rb +527 -0
- data/lib/rq/ioviewer.rb +48 -0
- data/lib/rq/job.rb +51 -0
- data/lib/rq/jobqueue.rb +947 -0
- data/lib/rq/jobrunner.rb +110 -0
- data/lib/rq/jobrunnerdaemon.rb +193 -0
- data/lib/rq/lister.rb +47 -0
- data/lib/rq/locker.rb +43 -0
- data/lib/rq/lockfile.rb +564 -0
- data/lib/rq/logging.rb +124 -0
- data/lib/rq/mainhelper.rb +189 -0
- data/lib/rq/orderedautohash.rb +39 -0
- data/lib/rq/orderedhash.rb +240 -0
- data/lib/rq/qdb.rb +733 -0
- data/lib/rq/querier.rb +98 -0
- data/lib/rq/rails.rb +80 -0
- data/lib/rq/recoverer.rb +28 -0
- data/lib/rq/refresher.rb +80 -0
- data/lib/rq/relayer.rb +283 -0
- data/lib/rq/resource.rb +22 -0
- data/lib/rq/resourcemanager.rb +40 -0
- data/lib/rq/resubmitter.rb +100 -0
- data/lib/rq/rotater.rb +98 -0
- data/lib/rq/sleepcycle.rb +46 -0
- data/lib/rq/snapshotter.rb +40 -0
- data/lib/rq/sqlite.rb +286 -0
- data/lib/rq/statuslister.rb +48 -0
- data/lib/rq/submitter.rb +113 -0
- data/lib/rq/toucher.rb +182 -0
- data/lib/rq/updater.rb +94 -0
- data/lib/rq/usage.rb +1222 -0
- data/lib/rq/util.rb +304 -0
- data/rdoc.sh +17 -0
- data/rq-ruby1.8.gemspec +120 -0
- data/test/.gitignore +1 -0
- data/test/test_rq.rb +145 -0
- data/white_box/crontab +2 -0
- data/white_box/joblist +8 -0
- data/white_box/killrq +18 -0
- data/white_box/rq_killer +27 -0
- metadata +208 -0
data/lib/rq/backer.rb
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
unless defined? $__rq_backer__
|
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
|
+
#
|
10
|
+
# a Backer object makes an (optionally) timestamped hot backup/snapshot of a
|
11
|
+
# queue using a timestamp of milli second resolution.
|
12
|
+
#
|
13
|
+
class Backer < MainHelper
|
14
|
+
#--{{{
|
15
|
+
def backup
|
16
|
+
#--{{{
|
17
|
+
set_q
|
18
|
+
bak = @argv.shift
|
19
|
+
bak ||= "#{ @qpath }.#{ Util::timestamp.gsub(/[:\s\.-]/,'_') }.bak"
|
20
|
+
raise "<#{ bak }> exists" if bak and test(?e, bak)
|
21
|
+
debug{ "bak <#{ bak }>" }
|
22
|
+
@q.lock{ FileUtils::cp_r @qpath, bak }
|
23
|
+
info{ "created backup <#{ bak }>" }
|
24
|
+
#--}}}
|
25
|
+
end
|
26
|
+
#--}}}
|
27
|
+
end # class Backer
|
28
|
+
#--}}}
|
29
|
+
end # module RQ
|
30
|
+
$__rq_backer__ = __FILE__
|
31
|
+
end
|
@@ -0,0 +1,82 @@
|
|
1
|
+
unless defined? $__rq_configfile__
|
2
|
+
module RQ
|
3
|
+
#--{{{
|
4
|
+
LIBDIR = File::dirname(File::expand_path(__FILE__)) + File::SEPARATOR unless
|
5
|
+
defined? LIBDIR
|
6
|
+
|
7
|
+
require 'yaml'
|
8
|
+
|
9
|
+
#
|
10
|
+
# the ConfigFile class is a thin class that munges yaml input and populates
|
11
|
+
# itself
|
12
|
+
#
|
13
|
+
class ConfigFile < ::Hash
|
14
|
+
#--{{{
|
15
|
+
DEFAULT_CONFIG = LIBDIR + 'defaultconfig.txt'
|
16
|
+
|
17
|
+
class << self
|
18
|
+
def gen_template(arg = nil)
|
19
|
+
#--{{{
|
20
|
+
@data ||= IO::read(DEFAULT_CONFIG)
|
21
|
+
case arg
|
22
|
+
when IO
|
23
|
+
arg.write @data
|
24
|
+
when String
|
25
|
+
open(arg, 'w'){|f| f.write @data}
|
26
|
+
else
|
27
|
+
STDOUT.write @data
|
28
|
+
end
|
29
|
+
self
|
30
|
+
#--}}}
|
31
|
+
end
|
32
|
+
def load_default
|
33
|
+
#--{{{
|
34
|
+
@data ||= IO::read(DEFAULT_CONFIG)
|
35
|
+
@default ||= YAML::load(munge(@data)) || {}
|
36
|
+
#--}}}
|
37
|
+
end
|
38
|
+
def any(basename, *dirnames)
|
39
|
+
#--{{{
|
40
|
+
config = nil
|
41
|
+
dirnames.each do |dirname|
|
42
|
+
path = File::join dirname, basename
|
43
|
+
if test ?e, path
|
44
|
+
config = self::new(path)
|
45
|
+
break
|
46
|
+
end
|
47
|
+
end
|
48
|
+
config || self::new('default')
|
49
|
+
#--}}}
|
50
|
+
end
|
51
|
+
def munge buf
|
52
|
+
#--{{{
|
53
|
+
buf.gsub(%r/\t/o,' ')
|
54
|
+
#--}}}
|
55
|
+
end
|
56
|
+
end
|
57
|
+
attr :path
|
58
|
+
def initialize path
|
59
|
+
#--{{{
|
60
|
+
@path = nil
|
61
|
+
yaml = nil
|
62
|
+
if path.nil? or path and path =~ /^\s*default/io
|
63
|
+
yaml = self.class.load_default
|
64
|
+
@path = 'DEFAULT'
|
65
|
+
else path
|
66
|
+
yaml = YAML::load(self.class.munge(open(path).read))
|
67
|
+
@path = path
|
68
|
+
end
|
69
|
+
self.update yaml
|
70
|
+
#--}}}
|
71
|
+
end
|
72
|
+
def to_hash
|
73
|
+
#--{{{
|
74
|
+
{}.update self
|
75
|
+
#--}}}
|
76
|
+
end
|
77
|
+
#--}}}
|
78
|
+
end # class ConfigFile
|
79
|
+
#--}}}
|
80
|
+
end # module RQ
|
81
|
+
$__rq_configfile__ = __FILE__
|
82
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
unless defined? $__rq_configurator__
|
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
|
+
#
|
10
|
+
# a Configurator adds key/value pairs to a queue's configuration. these
|
11
|
+
# key/value pairs are not currently used, but will be in a future release
|
12
|
+
#
|
13
|
+
class Configurator < MainHelper
|
14
|
+
#--{{{
|
15
|
+
#--}}}
|
16
|
+
def configure
|
17
|
+
#--{{{
|
18
|
+
set_q
|
19
|
+
attributes = {}
|
20
|
+
unless @argv.empty?
|
21
|
+
kv_pat = %r/^\s*([^\s]+)\s*=+\s*([^\s]+)\s*$/o
|
22
|
+
@q.transaction do
|
23
|
+
@argv.each do |arg|
|
24
|
+
match = kv_pat.match arg
|
25
|
+
if match
|
26
|
+
k, v = match[1], match[2]
|
27
|
+
@q[k] = v
|
28
|
+
end
|
29
|
+
end
|
30
|
+
attributes = @q.attributes
|
31
|
+
end
|
32
|
+
end
|
33
|
+
y attributes
|
34
|
+
#--}}}
|
35
|
+
end
|
36
|
+
end # class Configurator
|
37
|
+
#--}}}
|
38
|
+
end # module RQ
|
39
|
+
$__rq_configurator__ = __FILE__
|
40
|
+
end
|
data/lib/rq/creator.rb
ADDED
@@ -0,0 +1,54 @@
|
|
1
|
+
unless defined? $__rq_creator__
|
2
|
+
module RQ
|
3
|
+
#--{{{
|
4
|
+
LIBDIR = File::dirname(File::expand_path(__FILE__)) + File::SEPARATOR unless
|
5
|
+
defined? LIBDIR
|
6
|
+
|
7
|
+
require 'fileutils'
|
8
|
+
require LIBDIR + 'mainhelper'
|
9
|
+
|
10
|
+
#
|
11
|
+
# a queue is a directory
|
12
|
+
#
|
13
|
+
# the Creator class is responsible for initializing the queue directory and
|
14
|
+
# all supporting files. these include:
|
15
|
+
# * the sqlite database (binary)
|
16
|
+
# * the sqlite database schema description file (text)
|
17
|
+
# * the empty sentinel file used for locking (text - empty)
|
18
|
+
#
|
19
|
+
# it is an error to attempt to initialize a queue which already exists
|
20
|
+
#
|
21
|
+
class Creator < MainHelper
|
22
|
+
#--{{{
|
23
|
+
def create
|
24
|
+
#--{{{
|
25
|
+
raise "q <#{ @qpath }> exists!" if test ?e, @qpath
|
26
|
+
@q = JobQueue::create @qpath, 'logger' => @logger
|
27
|
+
|
28
|
+
unless quiet?
|
29
|
+
puts '---'
|
30
|
+
puts "q: #{ @q.path }"
|
31
|
+
puts "db: #{ @q.db.path }"
|
32
|
+
puts "schema: #{ @q.db.schema }"
|
33
|
+
puts "lock: #{ @q.db.lockfile }"
|
34
|
+
puts "bin: #{ @q.bin }"
|
35
|
+
puts "stdin: #{ @q.stdin }"
|
36
|
+
puts "stdout: #{ @q.stdout }"
|
37
|
+
puts "stderr: #{ @q.stderr }"
|
38
|
+
puts "data: #{ @q.data }"
|
39
|
+
end
|
40
|
+
|
41
|
+
rqmailer = File.join(File.dirname(@program), 'rqmailer')
|
42
|
+
if test ?e, rqmailer
|
43
|
+
FileUtils.cp rqmailer, @q.bin
|
44
|
+
end
|
45
|
+
|
46
|
+
self
|
47
|
+
#--}}}
|
48
|
+
end
|
49
|
+
#--}}}
|
50
|
+
end # class Creator
|
51
|
+
#--}}}
|
52
|
+
end # module RQ
|
53
|
+
$__rq_creator__ = __FILE__
|
54
|
+
end
|
data/lib/rq/cron.rb
ADDED
@@ -0,0 +1,144 @@
|
|
1
|
+
unless defined? $__rq_cron__
|
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
|
+
#
|
10
|
+
# a class for managing crontab entries and to start/stop rq
|
11
|
+
#
|
12
|
+
class Cron < MainHelper
|
13
|
+
#--{{{
|
14
|
+
def initialize *a, &b
|
15
|
+
#--{{{
|
16
|
+
super
|
17
|
+
ruby = Util::which_ruby
|
18
|
+
this = Util::realpath( File.expand_path( $0 ) )
|
19
|
+
q = qpath
|
20
|
+
|
21
|
+
@cmd = "#{ ruby } #{ this } #{ q }"
|
22
|
+
@md5 = lambda{|mode| Digest::MD5::hexdigest "#{ @cmd } #{ mode }" }
|
23
|
+
#--}}}
|
24
|
+
end
|
25
|
+
def cron
|
26
|
+
#--{{{
|
27
|
+
which = @argv.shift || 'start'
|
28
|
+
which = which.strip.downcase
|
29
|
+
#abort "arg not add|start|shutdown|stop" unless %w( start shutdown stop ).include? which
|
30
|
+
msg = "cron_#{ which }"
|
31
|
+
begin
|
32
|
+
send msg
|
33
|
+
rescue NoMethodError
|
34
|
+
raise ArgumentError, which
|
35
|
+
end
|
36
|
+
self
|
37
|
+
#--}}}
|
38
|
+
end
|
39
|
+
def cron_add
|
40
|
+
#--{{{
|
41
|
+
lines = `crontab -l`.split "\n"
|
42
|
+
|
43
|
+
found = nil
|
44
|
+
|
45
|
+
re = %r/###\s*md5:#{ @md5[:start] }/
|
46
|
+
|
47
|
+
lines.each do |line|
|
48
|
+
line.strip!
|
49
|
+
next if line[ %r/^\s*#/ ]
|
50
|
+
min, hour, dom, mon, dow, entry = line.split %r/\s+/, 6
|
51
|
+
next unless entry
|
52
|
+
entry.strip!
|
53
|
+
entry.gsub! %r/#[^'"]$/, ''
|
54
|
+
entry.strip!
|
55
|
+
found = re.match entry
|
56
|
+
break if found
|
57
|
+
end
|
58
|
+
|
59
|
+
unless found
|
60
|
+
opts = @options.map{|kv| "'--#{ kv.join('=') }'" }.join(' ')
|
61
|
+
entries = [
|
62
|
+
"*/15 * * * * #{ @cmd } start #{ opts } ###md5:#{ @md5[:start] }\n",
|
63
|
+
"0 0 * * * #{ @cmd } rotate ###md5:#{ @md5[:start] }\n",
|
64
|
+
]
|
65
|
+
tmp = Tempfile::new Process::pid.to_s
|
66
|
+
lines.each{|line| tmp << "#{ line }\n"}
|
67
|
+
entries.each do |entry|
|
68
|
+
tmp << entry
|
69
|
+
end
|
70
|
+
tmp.close
|
71
|
+
system("crontab #{ tmp.path }") or abort("failed to cronify!")
|
72
|
+
tmp.close!
|
73
|
+
entries.each do |entry|
|
74
|
+
puts entry
|
75
|
+
end
|
76
|
+
end
|
77
|
+
#--}}}
|
78
|
+
end
|
79
|
+
def cron_tab
|
80
|
+
#--{{{
|
81
|
+
opts = @options.map{|kv| "'--#{ kv.join('=') }'" }.join(' ')
|
82
|
+
entries = [
|
83
|
+
"*/15 * * * * #{ @cmd } start #{ opts } ###md5:#{ @md5[:start] }\n",
|
84
|
+
"0 0 * * * #{ @cmd } rotate ###md5:#{ @md5[:start] }\n",
|
85
|
+
]
|
86
|
+
puts entries
|
87
|
+
#--}}}
|
88
|
+
end
|
89
|
+
def cron_start
|
90
|
+
#--{{{
|
91
|
+
cron_add
|
92
|
+
#main.start
|
93
|
+
#--}}}
|
94
|
+
end
|
95
|
+
def cron_delete
|
96
|
+
#--{{{
|
97
|
+
lines = `crontab -l`.split "\n"
|
98
|
+
|
99
|
+
re = %r/###\s*md5:(#{ @md5[:start] })/
|
100
|
+
found = []
|
101
|
+
|
102
|
+
lines.each_with_index do |line, idx|
|
103
|
+
line.strip!
|
104
|
+
next if line[ %r/^\s*#/ ]
|
105
|
+
min, hour, dom, mon, dow, entry = line.split %r/\s+/, 6
|
106
|
+
next unless entry
|
107
|
+
entry.strip!
|
108
|
+
entry.gsub! %r/#[^'"]$/, ''
|
109
|
+
entry.strip!
|
110
|
+
found << idx if(re.match entry)
|
111
|
+
end
|
112
|
+
|
113
|
+
p found
|
114
|
+
|
115
|
+
unless found.empty?
|
116
|
+
deleted = []
|
117
|
+
found.each{|idx| deleted << lines[idx]; lines.delete_at(idx)}
|
118
|
+
tmp = Tempfile::new Process::pid.to_s
|
119
|
+
lines.each{|line| tmp << "#{ line }\n"}
|
120
|
+
tmp.close
|
121
|
+
system("crontab #{ tmp.path }") or abort("failed to cronify!")
|
122
|
+
tmp.close!
|
123
|
+
puts deleted
|
124
|
+
end
|
125
|
+
#--}}}
|
126
|
+
end
|
127
|
+
def cron_shutdown
|
128
|
+
#--{{{
|
129
|
+
cron_delete
|
130
|
+
main.shutdown
|
131
|
+
#--}}}
|
132
|
+
end
|
133
|
+
def cron_stop
|
134
|
+
#--{{{
|
135
|
+
cron_delete
|
136
|
+
main.stop
|
137
|
+
#--}}}
|
138
|
+
end
|
139
|
+
#--}}}
|
140
|
+
end # class Cron
|
141
|
+
#--}}}
|
142
|
+
end # module RQ
|
143
|
+
$__rq_cron__ = __FILE__
|
144
|
+
end
|
data/lib/rq/deleter.rb
ADDED
@@ -0,0 +1,51 @@
|
|
1
|
+
unless defined? $__rq_deleter__
|
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
|
+
#
|
10
|
+
# the Deleter class reads the command line, stdin, or an infile to determine
|
11
|
+
# the job ids (jid) of jobs to be deleted from the queue. jids may be
|
12
|
+
# specified on the command line or parsed from stdin or the infile. any
|
13
|
+
# input line matching 'jid : number' or 'number' is taken to be a line
|
14
|
+
# indicating a jid to delete.
|
15
|
+
#
|
16
|
+
class Deleter < MainHelper
|
17
|
+
#--{{{
|
18
|
+
def delete
|
19
|
+
#--{{{
|
20
|
+
set_q
|
21
|
+
|
22
|
+
whats = @argv
|
23
|
+
|
24
|
+
if whats.empty? and stdin?
|
25
|
+
pat = %r/^(?:\s*jid\s*:)?\s*(\d+)\s*$|^\s*(all)\s*$/io
|
26
|
+
while((line = stdin.gets))
|
27
|
+
match = pat.match line
|
28
|
+
next unless match
|
29
|
+
whats << (match[1] || match[2])
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
#whats.map!{|what| what =~ %r/^\s*\d+\s*$/o ? Integer(what) : what}
|
34
|
+
|
35
|
+
raise "nothing to delete" if whats.empty?
|
36
|
+
|
37
|
+
if @options['quiet']
|
38
|
+
@q.delete(*whats)
|
39
|
+
else
|
40
|
+
@q.delete(*whats, &dumping_yaml_tuples)
|
41
|
+
end
|
42
|
+
|
43
|
+
@q.vacuum
|
44
|
+
#--}}}
|
45
|
+
end
|
46
|
+
#--}}}
|
47
|
+
end # class Deleter
|
48
|
+
#--}}}
|
49
|
+
end # module RQ
|
50
|
+
$__rq_deleter__ = __FILE__
|
51
|
+
end
|
data/lib/rq/executor.rb
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
unless defined? $__rq_executor__
|
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
|
+
#
|
10
|
+
# the Executor is for expert use only and executes arbitrary sql on the
|
11
|
+
# queue's db. the reason one should not do this directly with the sqlite
|
12
|
+
# command line program is that it will not respect the locking subsystem
|
13
|
+
# used in RQ - the Executor method will
|
14
|
+
#
|
15
|
+
class Executor < MainHelper
|
16
|
+
#--{{{
|
17
|
+
def execute
|
18
|
+
#--{{{
|
19
|
+
set_q
|
20
|
+
sql = @argv.join ' '
|
21
|
+
if sql.empty? and stdin?
|
22
|
+
debug{ "reading sql from stdin" }
|
23
|
+
while((buf = stdin.gets))
|
24
|
+
buf.strip!
|
25
|
+
buf.gsub! %r/#.*$/o, ''
|
26
|
+
next if buf.empty?
|
27
|
+
sql << "#{ buf } "
|
28
|
+
end
|
29
|
+
end
|
30
|
+
abort "no sql to execute" if sql.empty?
|
31
|
+
@q.qdb.transaction_retries = 0
|
32
|
+
@q.transaction{@q.execute(sql, &dumping_yaml_tuples)}
|
33
|
+
#--}}}
|
34
|
+
end
|
35
|
+
#--}}}
|
36
|
+
end # class Executor
|
37
|
+
#--}}}
|
38
|
+
end # module RQ
|
39
|
+
$__rq_executor__ = __FILE__
|
40
|
+
end
|