bluth 0.5.3 → 0.6.0
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/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
|