bluth 0.5.3 → 0.6.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGES.txt +13 -0
- data/Rakefile +2 -1
- data/VERSION.yml +2 -2
- data/bin/bluth +99 -0
- data/bluth.gemspec +19 -6
- data/lib/bluth/cli.rb +72 -0
- data/lib/bluth/gob.rb +0 -179
- data/lib/bluth/test_helpers.rb +33 -0
- data/lib/bluth/worker.rb +104 -135
- data/lib/bluth.rb +217 -129
- data/try/15_queue_try.rb +30 -0
- data/try/16_worker_try.rb +23 -0
- data/try/17_gob_try.rb +49 -0
- data/try/18_handler_try.rb +0 -0
- data/try/19_bluth_try.rb +41 -0
- metadata +33 -11
data/CHANGES.txt
CHANGED
@@ -1,5 +1,18 @@
|
|
1
1
|
BLUTH, CHANGES
|
2
2
|
|
3
|
+
#### 0.6.0 (2010-12-30) ###############################
|
4
|
+
|
5
|
+
* CHANGE: Now depends on Familia 0.6
|
6
|
+
* CHANGE: Worker process name now starts with 'bluth'
|
7
|
+
* CHANGE: Bluth.pop is now correctly named Bluth.shift (b/c it's returning
|
8
|
+
the first value in the lists)
|
9
|
+
* ADDED: Bluth::Handler
|
10
|
+
|
11
|
+
|
12
|
+
#### 0.5.3 (2010-12-17) ###############################
|
13
|
+
|
14
|
+
* CHANGE: requires Familia 0.5.3
|
15
|
+
|
3
16
|
#### 0.5.2 (2010-12-10) ###############################
|
4
17
|
|
5
18
|
Initial public release
|
data/Rakefile
CHANGED
@@ -25,8 +25,9 @@ begin
|
|
25
25
|
gem.email = "delano@solutious.com"
|
26
26
|
gem.homepage = "http://github.com/delano/bluth"
|
27
27
|
gem.authors = ["Delano Mandelbaum"]
|
28
|
-
gem.add_dependency("familia", "= 0.
|
28
|
+
gem.add_dependency("familia", "= 0.6.3")
|
29
29
|
gem.add_dependency('sysinfo', '>= 0.7.3')
|
30
|
+
gem.add_dependency('annoy')
|
30
31
|
|
31
32
|
#gem.add_development_dependency("rspec", ">= 1.2.9")
|
32
33
|
#gem.add_development_dependency("mocha", ">= 0.9.8")
|
data/VERSION.yml
CHANGED
data/bin/bluth
ADDED
@@ -0,0 +1,99 @@
|
|
1
|
+
#!/usr/bin/ruby
|
2
|
+
|
3
|
+
# = Bluth: the queuing system.
|
4
|
+
#
|
5
|
+
#
|
6
|
+
# Usage:
|
7
|
+
#
|
8
|
+
# $ bluth -h
|
9
|
+
# $ bluth -Ipath/2/inc -ryourlib start
|
10
|
+
# $ bluth -Ipath/2/inc -ryourlib stop
|
11
|
+
#
|
12
|
+
#--
|
13
|
+
|
14
|
+
# Put our local lib in first place
|
15
|
+
BASE_PATH = File.expand_path File.join(File.dirname(__FILE__), '..')
|
16
|
+
$:.unshift File.join(BASE_PATH, 'lib')
|
17
|
+
|
18
|
+
require 'bluth'
|
19
|
+
require 'bluth/cli'
|
20
|
+
|
21
|
+
autoload :Rye, 'rye'
|
22
|
+
autoload :Annoy, 'annoy'
|
23
|
+
|
24
|
+
# Command-line interface for bin/bluth
|
25
|
+
class Bluth::CLI::Definition
|
26
|
+
extend Drydock
|
27
|
+
|
28
|
+
debug :on
|
29
|
+
|
30
|
+
global :Y, :auto, "Auto confirm"
|
31
|
+
global :d, :database, Integer, "Redis database"
|
32
|
+
global :e, :environment, String
|
33
|
+
global :d, :daemon, "Start as a daemon"
|
34
|
+
global :D, :debug do
|
35
|
+
Familia.debug = true
|
36
|
+
end
|
37
|
+
global :U, :uri, String, "Redis connection URI" do |v|
|
38
|
+
uri = URI.parse v
|
39
|
+
Bluth.uri = uri
|
40
|
+
end
|
41
|
+
global :v, :verbose, "Increase output" do
|
42
|
+
@verbose ||= 0
|
43
|
+
@verbose += 1
|
44
|
+
end
|
45
|
+
global :I, String, "Add a load path" do |v|
|
46
|
+
$LOAD_PATH << v
|
47
|
+
end
|
48
|
+
global :r, String, "Require a library" do |v|
|
49
|
+
$LOAD_PATH << 'lib' if File.exists? 'lib'
|
50
|
+
begin
|
51
|
+
require v
|
52
|
+
rescue LoadError => ex
|
53
|
+
puts ex.message
|
54
|
+
exit
|
55
|
+
end
|
56
|
+
end
|
57
|
+
global :V, :version, "Display version" do
|
58
|
+
puts "Version: #{Bluth::VERSION.to_s}"
|
59
|
+
exit 0
|
60
|
+
end
|
61
|
+
|
62
|
+
before do |obj|
|
63
|
+
obj.global.environment ||= 'dev'
|
64
|
+
end
|
65
|
+
|
66
|
+
#TODO: command :enqueue
|
67
|
+
|
68
|
+
command :start_worker => Bluth::CLI
|
69
|
+
command :start_scheduler => Bluth::CLI
|
70
|
+
|
71
|
+
option :f, :force
|
72
|
+
command :stop_workers => Bluth::CLI
|
73
|
+
option :f, :force
|
74
|
+
command :stop_worker => Bluth::CLI
|
75
|
+
option :f, :force
|
76
|
+
command :stop_scheduler => Bluth::CLI
|
77
|
+
|
78
|
+
command :workers => Bluth::CLI
|
79
|
+
command :schedulers => Bluth::CLI
|
80
|
+
|
81
|
+
#command :flush_workers => Bluth::CLI
|
82
|
+
end
|
83
|
+
|
84
|
+
begin
|
85
|
+
Drydock.run!(ARGV, STDIN) if Drydock.run? && !Drydock.has_run?
|
86
|
+
rescue Drydock::ArgError, Drydock::OptError => ex
|
87
|
+
STDERR.puts ex.message
|
88
|
+
STDERR.puts ex.usage
|
89
|
+
rescue Drydock::InvalidArgument => ex
|
90
|
+
STDERR.puts ex.message
|
91
|
+
rescue Drydock::UnknownCommand => ex
|
92
|
+
STDERR.puts "Unknown command: %s" % ex.name
|
93
|
+
rescue Interrupt
|
94
|
+
puts $/, "Exiting..."
|
95
|
+
exit 1
|
96
|
+
rescue => ex
|
97
|
+
STDERR.puts "ERROR (#{ex.class.to_s}): #{ex.message}"
|
98
|
+
STDERR.puts ex.backtrace #if Familia.debug
|
99
|
+
end
|
data/bluth.gemspec
CHANGED
@@ -5,13 +5,15 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = %q{bluth}
|
8
|
-
s.version = "0.
|
8
|
+
s.version = "0.6.0"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["Delano Mandelbaum"]
|
12
|
-
s.date = %q{2010-12-
|
12
|
+
s.date = %q{2010-12-30}
|
13
|
+
s.default_executable = %q{bluth}
|
13
14
|
s.description = %q{A Redis queuing system built on top of Familia}
|
14
15
|
s.email = %q{delano@solutious.com}
|
16
|
+
s.executables = ["bluth"]
|
15
17
|
s.extra_rdoc_files = [
|
16
18
|
"LICENSE.txt",
|
17
19
|
"README.rdoc"
|
@@ -22,11 +24,19 @@ Gem::Specification.new do |s|
|
|
22
24
|
"README.rdoc",
|
23
25
|
"Rakefile",
|
24
26
|
"VERSION.yml",
|
27
|
+
"bin/bluth",
|
25
28
|
"bluth.gemspec",
|
26
29
|
"lib/bluth.rb",
|
30
|
+
"lib/bluth/cli.rb",
|
27
31
|
"lib/bluth/gob.rb",
|
32
|
+
"lib/bluth/test_helpers.rb",
|
28
33
|
"lib/bluth/worker.rb",
|
29
|
-
"lib/daemonizing.rb"
|
34
|
+
"lib/daemonizing.rb",
|
35
|
+
"try/15_queue_try.rb",
|
36
|
+
"try/16_worker_try.rb",
|
37
|
+
"try/17_gob_try.rb",
|
38
|
+
"try/18_handler_try.rb",
|
39
|
+
"try/19_bluth_try.rb"
|
30
40
|
]
|
31
41
|
s.homepage = %q{http://github.com/delano/bluth}
|
32
42
|
s.rdoc_options = ["--charset=UTF-8"]
|
@@ -40,15 +50,18 @@ Gem::Specification.new do |s|
|
|
40
50
|
s.specification_version = 3
|
41
51
|
|
42
52
|
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
43
|
-
s.add_runtime_dependency(%q<familia>, ["= 0.
|
53
|
+
s.add_runtime_dependency(%q<familia>, ["= 0.6.3"])
|
44
54
|
s.add_runtime_dependency(%q<sysinfo>, [">= 0.7.3"])
|
55
|
+
s.add_runtime_dependency(%q<annoy>, [">= 0"])
|
45
56
|
else
|
46
|
-
s.add_dependency(%q<familia>, ["= 0.
|
57
|
+
s.add_dependency(%q<familia>, ["= 0.6.3"])
|
47
58
|
s.add_dependency(%q<sysinfo>, [">= 0.7.3"])
|
59
|
+
s.add_dependency(%q<annoy>, [">= 0"])
|
48
60
|
end
|
49
61
|
else
|
50
|
-
s.add_dependency(%q<familia>, ["= 0.
|
62
|
+
s.add_dependency(%q<familia>, ["= 0.6.3"])
|
51
63
|
s.add_dependency(%q<sysinfo>, [">= 0.7.3"])
|
64
|
+
s.add_dependency(%q<annoy>, [">= 0"])
|
52
65
|
end
|
53
66
|
end
|
54
67
|
|
data/lib/bluth/cli.rb
ADDED
@@ -0,0 +1,72 @@
|
|
1
|
+
require 'drydock'
|
2
|
+
|
3
|
+
module Bluth
|
4
|
+
class CLI < Drydock::Command
|
5
|
+
|
6
|
+
|
7
|
+
def start_workers
|
8
|
+
start_worker
|
9
|
+
end
|
10
|
+
|
11
|
+
def start_scheduler
|
12
|
+
start_worker Bluth.scheduler
|
13
|
+
end
|
14
|
+
|
15
|
+
def stop_scheduler
|
16
|
+
stop_workers Bluth.scheduler
|
17
|
+
end
|
18
|
+
|
19
|
+
def schedulers
|
20
|
+
workers Bluth.scheduler
|
21
|
+
end
|
22
|
+
|
23
|
+
#def flush_workers
|
24
|
+
# if @global.auto || Annoy.are_you_sure?
|
25
|
+
# Bluth::Worker.prefix
|
26
|
+
# end
|
27
|
+
#end
|
28
|
+
|
29
|
+
def start_worker worker_class=Bluth::Worker
|
30
|
+
if @global.daemon
|
31
|
+
worker = worker_class.new
|
32
|
+
Familia.info "Created: #{worker.rediskey}"
|
33
|
+
worker.daemonize
|
34
|
+
worker.run
|
35
|
+
else
|
36
|
+
Bluth.poptimeout = 3.seconds
|
37
|
+
worker_class.run
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def stop_workers worker_class=Bluth::Worker
|
42
|
+
worker_class.instances.each do |worker|
|
43
|
+
kill_worker worker, worker_class
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def stop_worker wid=nil,worker_class=Bluth::Worker
|
48
|
+
wids = wid ? [wid] : @argv
|
49
|
+
wids.each do |wid|
|
50
|
+
worker = worker_class.from_redis wid
|
51
|
+
kill_worker worker, worker_class
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def workers worker_class=Bluth::Worker
|
56
|
+
Familia.info worker_class.all.collect &:key
|
57
|
+
end
|
58
|
+
|
59
|
+
private
|
60
|
+
|
61
|
+
def kill_worker worker, worker_class=Bluth::Worker
|
62
|
+
if worker.nil?
|
63
|
+
Familia.info "No such worker"
|
64
|
+
exit 1
|
65
|
+
else
|
66
|
+
Familia.info "Killing #{worker.rediskey}"
|
67
|
+
worker.kill @global.auto
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
end
|
72
|
+
end
|
data/lib/bluth/gob.rb
CHANGED
@@ -1,180 +1 @@
|
|
1
1
|
|
2
|
-
|
3
|
-
module Bluth
|
4
|
-
|
5
|
-
class Gob < Storable
|
6
|
-
MAX_ATTEMPTS = 3.freeze unless defined?(Gob::MAX_ATTEMPTS)
|
7
|
-
include Familia
|
8
|
-
prefix :gob
|
9
|
-
ttl 1.hour
|
10
|
-
field :id => Gibbler::Digest
|
11
|
-
field :kind => String
|
12
|
-
field :data => Hash
|
13
|
-
field :messages => Array
|
14
|
-
field :attempts => Integer
|
15
|
-
field :create_time => Float
|
16
|
-
field :stime => Float
|
17
|
-
field :etime => Float
|
18
|
-
field :current_queue => String
|
19
|
-
field :thread_id => Integer
|
20
|
-
field :cpu => Array
|
21
|
-
field :wid => Gibbler::Digest
|
22
|
-
|
23
|
-
def self.inherited(obj)
|
24
|
-
obj.extend Bluth::Gob::ClassMethods
|
25
|
-
obj.prefix [:job, obj.to_s.split('::').last.downcase].join(':')
|
26
|
-
Bluth.handlers << obj
|
27
|
-
end
|
28
|
-
|
29
|
-
module ClassMethods
|
30
|
-
def clear
|
31
|
-
keys.each do |key|
|
32
|
-
Gob.redis.del key
|
33
|
-
end
|
34
|
-
end
|
35
|
-
def enqueue(data={},q=nil)
|
36
|
-
q ||= self.queue
|
37
|
-
job = Gob.create generate_id(data), self, data
|
38
|
-
job.current_queue = q
|
39
|
-
Familia.ld "ENQUEUING: #{self} #{job.id.short} to #{q}"
|
40
|
-
Bluth::Queue.redis.lpush q.key, job.id
|
41
|
-
job.create_time = Time.now.utc.to_f
|
42
|
-
job.attempts = 0
|
43
|
-
job
|
44
|
-
end
|
45
|
-
def queue(name=nil)
|
46
|
-
@queue = name if name
|
47
|
-
@queue || Bluth::High
|
48
|
-
end
|
49
|
-
def generate_id(*args)
|
50
|
-
a = [self, Process.pid, Bluth.sysinfo.hostname, Time.now.to_f, *args]
|
51
|
-
a.gibbler
|
52
|
-
end
|
53
|
-
def all
|
54
|
-
Bluth::Gob.all.select do |job|
|
55
|
-
job.kind == self
|
56
|
-
end
|
57
|
-
end
|
58
|
-
def size
|
59
|
-
all.size
|
60
|
-
end
|
61
|
-
def lock_key
|
62
|
-
Familia.key(prefix, :lock)
|
63
|
-
end
|
64
|
-
def lock!
|
65
|
-
raise Bluth::Buster, "#{self} is already locked!" if locked?
|
66
|
-
Familia.info "Locking #{self}"
|
67
|
-
ret = Bluth::Gob.redis.set lock_key, 1
|
68
|
-
Bluth.locks << lock_key
|
69
|
-
ret == 'OK'
|
70
|
-
end
|
71
|
-
def unlock!
|
72
|
-
Familia.info "Unlocking #{self}"
|
73
|
-
ret = Bluth::Gob.redis.del lock_key
|
74
|
-
Bluth.locks.delete lock_key
|
75
|
-
ret
|
76
|
-
end
|
77
|
-
def locked?
|
78
|
-
Bluth::Gob.redis.exists lock_key
|
79
|
-
end
|
80
|
-
def prepare
|
81
|
-
end
|
82
|
-
|
83
|
-
[:success, :failure, :running].each do |w|
|
84
|
-
define_method "#{w}_key" do # success_key
|
85
|
-
Familia.key(self.prefix, w)
|
86
|
-
end
|
87
|
-
define_method "#{w}!" do |*args| # success!(1)
|
88
|
-
by = args.first || 1
|
89
|
-
Bluth::Gob.redis.incrby send("#{w}_key"), by
|
90
|
-
end
|
91
|
-
define_method "#{w}" do # success
|
92
|
-
Bluth::Gob.redis.get(send("#{w}_key")).to_i
|
93
|
-
end
|
94
|
-
end
|
95
|
-
end
|
96
|
-
|
97
|
-
def id
|
98
|
-
@id = Gibbler::Digest.new(@id) if String === @id
|
99
|
-
end
|
100
|
-
def clear!
|
101
|
-
@attempts = 0
|
102
|
-
@messages = []
|
103
|
-
save
|
104
|
-
end
|
105
|
-
def preprocess
|
106
|
-
@attempts ||= 0
|
107
|
-
@messages ||= []
|
108
|
-
@create_time ||= Time.now.utc.to_f
|
109
|
-
end
|
110
|
-
def attempt?
|
111
|
-
attempts < MAX_ATTEMPTS
|
112
|
-
end
|
113
|
-
def attempt!
|
114
|
-
@attempts = attempts + 1
|
115
|
-
end
|
116
|
-
def current_queue
|
117
|
-
@current_queue
|
118
|
-
end
|
119
|
-
def kind
|
120
|
-
@kind = eval "::#{@kind}" rescue @kind if @kind.is_a?(String)
|
121
|
-
@kind
|
122
|
-
end
|
123
|
-
def kind=(v)
|
124
|
-
@kind = v
|
125
|
-
end
|
126
|
-
def perform
|
127
|
-
@attempts += 1
|
128
|
-
Familia.ld "PERFORM: #{self.to_hash.inspect}"
|
129
|
-
@stime = Time.now.utc.to_f
|
130
|
-
save # update the time
|
131
|
-
self.kind.prepare if self.class.respond_to?(:prepare)
|
132
|
-
self.kind.perform @data
|
133
|
-
@etime = Time.now.utc.to_f
|
134
|
-
save # update the time
|
135
|
-
end
|
136
|
-
def delayed?
|
137
|
-
start = @stime || 0
|
138
|
-
start > Time.now.utc.to_f
|
139
|
-
end
|
140
|
-
def retry!(msg=nil)
|
141
|
-
move! Bluth::High, msg
|
142
|
-
end
|
143
|
-
def failure!(msg=nil)
|
144
|
-
@etime = Time.now.utc.to_i
|
145
|
-
self.kind.failure!
|
146
|
-
move! Bluth::Failed, msg
|
147
|
-
end
|
148
|
-
def success!(msg=nil)
|
149
|
-
@etime = Time.now.utc.to_i
|
150
|
-
self.kind.success!
|
151
|
-
move! Bluth::Successful, msg
|
152
|
-
end
|
153
|
-
def duration
|
154
|
-
return 0 if @stime.nil?
|
155
|
-
et = @etime || Time.now.utc.to_i
|
156
|
-
et - @stime
|
157
|
-
end
|
158
|
-
def dequeue!
|
159
|
-
Familia.ld "Deleting #{self.id} from #{current_queue.key}"
|
160
|
-
Bluth::Queue.redis.lrem current_queue.key, 0, self.id
|
161
|
-
end
|
162
|
-
private
|
163
|
-
def move!(to, msg=nil)
|
164
|
-
@thread_id = $$
|
165
|
-
if to.to_s == current_queue.to_s
|
166
|
-
raise Bluth::Buster, "Cannot move job to the queue it's in: #{to}"
|
167
|
-
end
|
168
|
-
Familia.ld "Moving #{self.id.short} from #{current_queue.key} to #{to.key}"
|
169
|
-
@messages << msg unless msg.nil? || msg.empty?
|
170
|
-
# We push first to make sure we never lose a Gob ID. Instead
|
171
|
-
# there's the small chance of a job ID being in two queues.
|
172
|
-
Bluth::Queue.redis.lpush to.key, @id
|
173
|
-
dequeue!
|
174
|
-
save # update messages
|
175
|
-
@current_queue = to
|
176
|
-
end
|
177
|
-
end
|
178
|
-
|
179
|
-
end
|
180
|
-
|
@@ -0,0 +1,33 @@
|
|
1
|
+
|
2
|
+
|
3
|
+
module ExampleHandler
|
4
|
+
extend Bluth::Handler
|
5
|
+
queue :critical
|
6
|
+
|
7
|
+
# :hostid => String
|
8
|
+
# :force => Boolean (update all monitors)
|
9
|
+
def self.enqueue(opts={})
|
10
|
+
super opts, opts.delete(:queue)
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.perform(data={})
|
14
|
+
begin
|
15
|
+
|
16
|
+
rescue => ex
|
17
|
+
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
class ExampleWorker < Bluth::Worker
|
23
|
+
|
24
|
+
end
|
25
|
+
|
26
|
+
|
27
|
+
Bluth::Worker.onstart do
|
28
|
+
puts "onstart called"
|
29
|
+
end
|
30
|
+
|
31
|
+
Bluth::Worker.onexit do
|
32
|
+
puts "onexit called"
|
33
|
+
end
|