woodhouse 0.1.1
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/.gitignore +5 -0
- data/.travis.yml +5 -0
- data/Gemfile +7 -0
- data/Guardfile +4 -0
- data/MIT-LICENSE +21 -0
- data/PROGRESS-NOTES.txt +5 -0
- data/README.markdown +152 -0
- data/Rakefile +35 -0
- data/THOUGHTS +84 -0
- data/doc/example/script-woodhouse +9 -0
- data/doc/example/woodhouse-initializer.rb +12 -0
- data/lib/generators/woodhouse_generator.rb +38 -0
- data/lib/woodhouse/dispatcher.rb +37 -0
- data/lib/woodhouse/dispatchers/bunny_dispatcher.rb +48 -0
- data/lib/woodhouse/dispatchers/common_amqp_dispatcher.rb +28 -0
- data/lib/woodhouse/dispatchers/hot_bunnies_dispatcher.rb +48 -0
- data/lib/woodhouse/dispatchers/local_dispatcher.rb +13 -0
- data/lib/woodhouse/dispatchers/local_pool_dispatcher.rb +25 -0
- data/lib/woodhouse/dispatchers.rb +19 -0
- data/lib/woodhouse/extension.rb +24 -0
- data/lib/woodhouse/extensions/new_relic/instrumentation_middleware.rb +10 -0
- data/lib/woodhouse/extensions/new_relic.rb +23 -0
- data/lib/woodhouse/extensions/progress.rb +165 -0
- data/lib/woodhouse/job.rb +76 -0
- data/lib/woodhouse/job_execution.rb +60 -0
- data/lib/woodhouse/layout.rb +290 -0
- data/lib/woodhouse/layout_builder.rb +55 -0
- data/lib/woodhouse/layout_serializer.rb +82 -0
- data/lib/woodhouse/middleware/airbrake_exceptions.rb +12 -0
- data/lib/woodhouse/middleware/assign_logger.rb +10 -0
- data/lib/woodhouse/middleware/log_dispatch.rb +21 -0
- data/lib/woodhouse/middleware/log_jobs.rb +22 -0
- data/lib/woodhouse/middleware.rb +16 -0
- data/lib/woodhouse/middleware_stack.rb +35 -0
- data/lib/woodhouse/mixin_registry.rb +27 -0
- data/lib/woodhouse/node_configuration.rb +80 -0
- data/lib/woodhouse/process.rb +41 -0
- data/lib/woodhouse/queue_criteria.rb +52 -0
- data/lib/woodhouse/rails.rb +46 -0
- data/lib/woodhouse/rails2.rb +21 -0
- data/lib/woodhouse/registry.rb +12 -0
- data/lib/woodhouse/runner.rb +60 -0
- data/lib/woodhouse/runners/bunny_runner.rb +60 -0
- data/lib/woodhouse/runners/dummy_runner.rb +11 -0
- data/lib/woodhouse/runners/hot_bunnies_runner.rb +79 -0
- data/lib/woodhouse/runners.rb +16 -0
- data/lib/woodhouse/scheduler.rb +113 -0
- data/lib/woodhouse/server.rb +80 -0
- data/lib/woodhouse/trigger_set.rb +19 -0
- data/lib/woodhouse/version.rb +3 -0
- data/lib/woodhouse/worker.rb +86 -0
- data/lib/woodhouse.rb +99 -0
- data/spec/integration/bunny_worker_process_spec.rb +32 -0
- data/spec/layout_builder_spec.rb +55 -0
- data/spec/layout_spec.rb +143 -0
- data/spec/middleware_stack_spec.rb +56 -0
- data/spec/mixin_registry_spec.rb +15 -0
- data/spec/node_configuration_spec.rb +22 -0
- data/spec/progress_spec.rb +40 -0
- data/spec/queue_criteria_spec.rb +11 -0
- data/spec/scheduler_spec.rb +41 -0
- data/spec/server_spec.rb +72 -0
- data/spec/shared_contexts.rb +70 -0
- data/spec/worker_spec.rb +28 -0
- data/woodhouse.gemspec +37 -0
- metadata +285 -0
@@ -0,0 +1,79 @@
|
|
1
|
+
#
|
2
|
+
# A Runner implementation that uses hot_bunnies, a JRuby AMQP client using the
|
3
|
+
# Java client for RabbitMQ. This class can be loaded if hot_bunnies is not
|
4
|
+
# available, but it will fail upon initialization. If you want to use this
|
5
|
+
# runner (it's currently the only one that works very well), make sure to
|
6
|
+
# add
|
7
|
+
#
|
8
|
+
# gem 'hot_bunnies'
|
9
|
+
#
|
10
|
+
# to your Gemfile. This runner will automatically be used in JRuby.
|
11
|
+
#
|
12
|
+
class Woodhouse::Runners::HotBunniesRunner < Woodhouse::Runner
|
13
|
+
begin
|
14
|
+
require 'hot_bunnies'
|
15
|
+
rescue LoadError => err
|
16
|
+
define_method(:initialize) {|*args|
|
17
|
+
raise err
|
18
|
+
}
|
19
|
+
end
|
20
|
+
|
21
|
+
def subscribe
|
22
|
+
client = HotBunnies.connect(@config.server_info)
|
23
|
+
channel = client.create_channel
|
24
|
+
channel.prefetch = 1
|
25
|
+
queue = channel.queue(@worker.queue_name)
|
26
|
+
exchange = channel.exchange(@worker.exchange_name, :type => :headers)
|
27
|
+
queue.bind(exchange, :arguments => @worker.criteria.amqp_headers)
|
28
|
+
worker = Celluloid.current_actor
|
29
|
+
queue.subscribe(:ack => true).each(:blocking => false) do |headers, payload|
|
30
|
+
begin
|
31
|
+
job = make_job(headers, payload)
|
32
|
+
if can_service_job?(job)
|
33
|
+
if service_job(job)
|
34
|
+
headers.ack
|
35
|
+
else
|
36
|
+
headers.reject
|
37
|
+
end
|
38
|
+
else
|
39
|
+
@config.logger.error("Cannot service job #{job.describe} in queue for #{@worker.describe}")
|
40
|
+
headers.reject
|
41
|
+
end
|
42
|
+
rescue => err
|
43
|
+
begin
|
44
|
+
@config.logger.error("Error bubbled up out of worker. This shouldn't happen. #{err.message}")
|
45
|
+
err.backtrace.each do |btr|
|
46
|
+
@config.logger.error(" #{btr}")
|
47
|
+
end
|
48
|
+
headers.reject
|
49
|
+
ensure
|
50
|
+
worker.bail_out(err)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
wait :spin_down
|
55
|
+
ensure
|
56
|
+
client.close
|
57
|
+
end
|
58
|
+
|
59
|
+
def spin_down
|
60
|
+
signal :spin_down
|
61
|
+
end
|
62
|
+
|
63
|
+
def bail_out(err)
|
64
|
+
raise Woodhouse::BailOut, "#{err.class}: #{err.message}"
|
65
|
+
end
|
66
|
+
|
67
|
+
private
|
68
|
+
|
69
|
+
def make_job(message, payload)
|
70
|
+
Woodhouse::Job.new(@worker.worker_class_name, @worker.job_method) do |job|
|
71
|
+
job.arguments = message.properties.headers.inject({}) {|h,(k,v)|
|
72
|
+
h[k.to_s] = v.to_s
|
73
|
+
h
|
74
|
+
}
|
75
|
+
job.payload = payload
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module Woodhouse::Runners
|
2
|
+
|
3
|
+
def self.guess
|
4
|
+
if defined? ::JRUBY_VERSION
|
5
|
+
Woodhouse::Runners::HotBunniesRunner
|
6
|
+
else
|
7
|
+
Woodhouse::Runners::BunnyRunner
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
end
|
12
|
+
|
13
|
+
require 'woodhouse/runner'
|
14
|
+
require 'woodhouse/runners/bunny_runner'
|
15
|
+
require 'woodhouse/runners/hot_bunnies_runner'
|
16
|
+
require 'woodhouse/runners/dummy_runner'
|
@@ -0,0 +1,113 @@
|
|
1
|
+
class Woodhouse::Scheduler
|
2
|
+
include Woodhouse::Util
|
3
|
+
include Celluloid
|
4
|
+
|
5
|
+
class SpunDown < StandardError
|
6
|
+
|
7
|
+
end
|
8
|
+
|
9
|
+
class WorkerSet
|
10
|
+
include Woodhouse::Util
|
11
|
+
include Celluloid
|
12
|
+
|
13
|
+
attr_reader :worker
|
14
|
+
trap_exit :worker_died
|
15
|
+
|
16
|
+
def initialize(scheduler, worker, config)
|
17
|
+
expect_arg_or_nil :worker, Woodhouse::Layout::Worker, worker
|
18
|
+
@scheduler = scheduler
|
19
|
+
@worker_def = worker
|
20
|
+
@config = config
|
21
|
+
@threads = []
|
22
|
+
spin_up
|
23
|
+
end
|
24
|
+
|
25
|
+
def spin_down
|
26
|
+
@spinning_down = true
|
27
|
+
@threads.each_with_index do |thread, idx|
|
28
|
+
@config.logger.debug "Spinning down thread #{idx} for worker #{@worker_def.describe}"
|
29
|
+
thread.spin_down
|
30
|
+
thread.terminate
|
31
|
+
end
|
32
|
+
@scheduler.remove_worker(@worker_def)
|
33
|
+
signal :spun_down
|
34
|
+
end
|
35
|
+
|
36
|
+
def wait_until_done
|
37
|
+
wait :spun_down
|
38
|
+
end
|
39
|
+
|
40
|
+
private
|
41
|
+
|
42
|
+
def worker_died(actor, reason)
|
43
|
+
if reason
|
44
|
+
@config.logger.info "Worker died (#{reason.class}: #{reason.message}). Spinning down."
|
45
|
+
@threads.delete actor
|
46
|
+
raise Woodhouse::BailOut
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def spin_up
|
51
|
+
@worker_def.threads.times do |idx|
|
52
|
+
@config.logger.debug "Spinning up thread #{idx} for worker #{@worker_def.describe}"
|
53
|
+
worker = @config.runner_type.new_link(@worker_def, @config)
|
54
|
+
@threads << worker
|
55
|
+
worker.subscribe!
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
end
|
60
|
+
|
61
|
+
trap_exit :worker_set_died
|
62
|
+
|
63
|
+
def initialize(config)
|
64
|
+
@config = config
|
65
|
+
@worker_sets = {}
|
66
|
+
end
|
67
|
+
|
68
|
+
def start_worker(worker)
|
69
|
+
@config.logger.debug "Starting worker #{worker.describe}"
|
70
|
+
unless @worker_sets.has_key?(worker)
|
71
|
+
@worker_sets[worker] = WorkerSet.new_link(Celluloid.current_actor, worker, @config)
|
72
|
+
true
|
73
|
+
else
|
74
|
+
false
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
def stop_worker(worker, wait = false)
|
79
|
+
if set = @worker_sets[worker]
|
80
|
+
@config.logger.debug "Spinning down worker #{worker.describe}"
|
81
|
+
set.spin_down
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
def running_worker?(worker)
|
86
|
+
@worker_sets.has_key?(worker)
|
87
|
+
end
|
88
|
+
|
89
|
+
def spin_down
|
90
|
+
@spinning_down = true
|
91
|
+
@config.logger.debug "Spinning down all workers"
|
92
|
+
@worker_sets.each do |worker, set|
|
93
|
+
set.spin_down
|
94
|
+
set.terminate
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
def remove_worker(worker)
|
99
|
+
@worker_sets.delete(worker)
|
100
|
+
end
|
101
|
+
|
102
|
+
def worker_set_died(actor, reason)
|
103
|
+
if reason
|
104
|
+
@config.logger.info "Worker set died (#{reason.class}: #{reason.message}). Spinning down."
|
105
|
+
begin
|
106
|
+
spin_down
|
107
|
+
ensure
|
108
|
+
raise reason
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
module Woodhouse
|
2
|
+
|
3
|
+
class Server
|
4
|
+
include Celluloid
|
5
|
+
include Woodhouse::Util
|
6
|
+
|
7
|
+
attr_reader :layout, :node
|
8
|
+
attr_accessor :configuration
|
9
|
+
|
10
|
+
trap_exit :scheduler_died
|
11
|
+
|
12
|
+
def initialize(keyw = {})
|
13
|
+
self.layout = keyw[:layout]
|
14
|
+
self.node = keyw[:node]
|
15
|
+
self.configuration = keyw[:configuration] || Woodhouse.global_configuration
|
16
|
+
end
|
17
|
+
|
18
|
+
def layout=(value)
|
19
|
+
expect_arg_or_nil :value, Woodhouse::Layout, value
|
20
|
+
@previous_layout = @layout
|
21
|
+
@layout = value ? value.frozen_clone : nil
|
22
|
+
end
|
23
|
+
|
24
|
+
def node=(value)
|
25
|
+
@node = value || :default
|
26
|
+
end
|
27
|
+
|
28
|
+
def start
|
29
|
+
# TODO: don't pass global config
|
30
|
+
@scheduler ||= Woodhouse::Scheduler.new_link(configuration)
|
31
|
+
return false unless ready_to_start?
|
32
|
+
configuration.triggers.trigger :server_start
|
33
|
+
dispatch_layout_changes
|
34
|
+
true
|
35
|
+
end
|
36
|
+
|
37
|
+
def reload
|
38
|
+
dispatch_layout_changes!
|
39
|
+
end
|
40
|
+
|
41
|
+
def ready_to_start?
|
42
|
+
@node and @layout and @layout.node(@node)
|
43
|
+
end
|
44
|
+
|
45
|
+
# TODO: do this better
|
46
|
+
def shutdown
|
47
|
+
@scheduler.spin_down
|
48
|
+
@scheduler.terminate
|
49
|
+
configuration.triggers.trigger :server_end
|
50
|
+
signal :shutdown
|
51
|
+
end
|
52
|
+
|
53
|
+
private
|
54
|
+
|
55
|
+
def scheduler_died(actor, reason)
|
56
|
+
signal :shutdown
|
57
|
+
end
|
58
|
+
|
59
|
+
def dispatch_layout_changes
|
60
|
+
if @layout.nil?
|
61
|
+
shutdown
|
62
|
+
else
|
63
|
+
apply_layout_changes @layout.changes_from(@previous_layout, @node)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def apply_layout_changes(changes)
|
68
|
+
if @scheduler
|
69
|
+
changes.adds.each do |add|
|
70
|
+
@scheduler.start_worker(add)
|
71
|
+
end
|
72
|
+
changes.drops.each do |drop|
|
73
|
+
@scheduler.stop_worker(drop)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
end
|
79
|
+
|
80
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
class Woodhouse::TriggerSet
|
2
|
+
|
3
|
+
def initialize
|
4
|
+
@triggers = {}
|
5
|
+
end
|
6
|
+
|
7
|
+
def add(event_name, &blk)
|
8
|
+
@triggers[event_name.to_sym] ||= []
|
9
|
+
@triggers[event_name.to_sym] << blk
|
10
|
+
end
|
11
|
+
|
12
|
+
|
13
|
+
def trigger(event_name, *args)
|
14
|
+
(@triggers[event_name.to_sym] || []).each do |trigger|
|
15
|
+
trigger.call(*args)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
@@ -0,0 +1,86 @@
|
|
1
|
+
#
|
2
|
+
# Classes which include this module become automatically visible to
|
3
|
+
# MixinRegistry (the default way of finding jobs in Woodhouse).
|
4
|
+
# All public methods of the class are automatically made available as
|
5
|
+
# jobs.
|
6
|
+
#
|
7
|
+
# Classes including Woodhouse::Worker also get access to the +logger+
|
8
|
+
# method, which will be the same logger globally configured for the current
|
9
|
+
# Layout.
|
10
|
+
#
|
11
|
+
# Classes including Woodhouse::Worker also have convenience shortcuts
|
12
|
+
# for dispatching jobs. Any job defined on the class can be dispatched
|
13
|
+
# asynchronously by calling ClassName.async_job_name(options).
|
14
|
+
#
|
15
|
+
# == Example
|
16
|
+
#
|
17
|
+
# class PamPoovey
|
18
|
+
# include Woodhouse::Worker
|
19
|
+
#
|
20
|
+
# # This is available as the job PamPoovey#do_hr
|
21
|
+
# def do_hr(options)
|
22
|
+
# logger.info "Out comes the dolphin puppet"
|
23
|
+
# end
|
24
|
+
#
|
25
|
+
# private
|
26
|
+
#
|
27
|
+
# # This is not picked up as a job
|
28
|
+
# def fight_club
|
29
|
+
# # ...
|
30
|
+
# end
|
31
|
+
# end
|
32
|
+
#
|
33
|
+
# # later ...
|
34
|
+
#
|
35
|
+
# Woodhouse::MixinRegistry.new[:PamPoovey] # => PamPoovey
|
36
|
+
# PamPoovey.async_do_hr(:employee => "Lana")
|
37
|
+
#
|
38
|
+
module Woodhouse::Worker
|
39
|
+
|
40
|
+
def self.included(into)
|
41
|
+
into.extend ClassMethods
|
42
|
+
into.set_worker_name into.name unless into.name.nil?
|
43
|
+
end
|
44
|
+
|
45
|
+
# The current Woodhouse logger. Set by the runner. Don't expect it to be set
|
46
|
+
# if you create the object yourself. If you want to be able to run job methods
|
47
|
+
# directly, you should account for setting +logger+.
|
48
|
+
attr_accessor :logger
|
49
|
+
|
50
|
+
module ClassMethods
|
51
|
+
|
52
|
+
def worker_name
|
53
|
+
@worker_name
|
54
|
+
end
|
55
|
+
|
56
|
+
# Sets the name for this worker class if not already set (i.e., if it's
|
57
|
+
# an anonymous class). The first time the name for the worker is set,
|
58
|
+
# it becomes registered with MixinRegistry. After that, attempting to
|
59
|
+
# change the worker class will raise ArgumentError.
|
60
|
+
def set_worker_name(name)
|
61
|
+
if @worker_name
|
62
|
+
raise ArgumentError, "cannot change worker name"
|
63
|
+
else
|
64
|
+
if name and !name.empty?
|
65
|
+
@worker_name = name.to_sym
|
66
|
+
Woodhouse::MixinRegistry.register self
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
# You can dispatch a job +baz+ on class +FooBar+ by calling FooBar.async_baz.
|
72
|
+
def method_missing(method, *args, &block)
|
73
|
+
if method.to_s =~ /^asynch?_(.*)/
|
74
|
+
if instance_methods(false).detect{|meth| meth.to_s == $1 }
|
75
|
+
Woodhouse.dispatch(@worker_name, $1, args.first)
|
76
|
+
else
|
77
|
+
super
|
78
|
+
end
|
79
|
+
else
|
80
|
+
super
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
end
|
85
|
+
|
86
|
+
end
|
data/lib/woodhouse.rb
ADDED
@@ -0,0 +1,99 @@
|
|
1
|
+
module Woodhouse
|
2
|
+
WoodhouseError = Class.new(StandardError)
|
3
|
+
WorkerNotFoundError = Class.new(WoodhouseError)
|
4
|
+
ConnectionError = Class.new(WoodhouseError)
|
5
|
+
ConfigurationError = Class.new(WoodhouseError)
|
6
|
+
FatalError = Class.new(WoodhouseError)
|
7
|
+
BailOut = Class.new(FatalError)
|
8
|
+
|
9
|
+
module Util
|
10
|
+
|
11
|
+
private
|
12
|
+
|
13
|
+
def expect_arg(name, klass, value)
|
14
|
+
unless value.kind_of?(klass)
|
15
|
+
raise ArgumentError, "expected #{name} to be a #{klass.name}, got #{value.class}"
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def expect_arg_or_nil(name, klass, value)
|
20
|
+
expect_arg(name, klass, value) unless value.nil?
|
21
|
+
end
|
22
|
+
|
23
|
+
# Cheap knockoff, suffices for my simple purposes
|
24
|
+
def camelize(string)
|
25
|
+
string.split(/_/).map{ |word| word.capitalize }.join('')
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
29
|
+
|
30
|
+
# TODO: hate keeping global state in this class. I need to push
|
31
|
+
# some of this down into NodeConfiguration or something like it.
|
32
|
+
module GlobalMethods
|
33
|
+
|
34
|
+
def logger
|
35
|
+
global_configuration.logger
|
36
|
+
end
|
37
|
+
|
38
|
+
def global_configuration
|
39
|
+
@global_configuration ||= Woodhouse::NodeConfiguration.default
|
40
|
+
end
|
41
|
+
|
42
|
+
def configure
|
43
|
+
@global_configuration ||= Woodhouse::NodeConfiguration.default
|
44
|
+
yield @global_configuration
|
45
|
+
end
|
46
|
+
|
47
|
+
def global_layout
|
48
|
+
@global_layout ||= Woodhouse::Layout.default
|
49
|
+
end
|
50
|
+
|
51
|
+
def layout
|
52
|
+
@global_layout ||= Woodhouse::Layout.new
|
53
|
+
yield Woodhouse::LayoutBuilder.new(Woodhouse.global_configuration, @global_layout)
|
54
|
+
end
|
55
|
+
|
56
|
+
# Returns +true+ on JRuby, Rubinius, or MRI 1.9. +false+ otherwise.
|
57
|
+
def threading_safe?
|
58
|
+
RUBY_VERSION.to_f >= 1.9 or %w[jruby rbx].include?(RUBY_ENGINE)
|
59
|
+
end
|
60
|
+
|
61
|
+
def dispatch(*a)
|
62
|
+
global_configuration.dispatcher.dispatch(*a)
|
63
|
+
end
|
64
|
+
|
65
|
+
def update_job(*a)
|
66
|
+
global_configuration.dispatcher.update_job(*a)
|
67
|
+
end
|
68
|
+
|
69
|
+
end
|
70
|
+
|
71
|
+
extend GlobalMethods
|
72
|
+
|
73
|
+
end
|
74
|
+
|
75
|
+
require 'fiber18'
|
76
|
+
require 'celluloid'
|
77
|
+
require 'woodhouse/job'
|
78
|
+
require 'woodhouse/layout'
|
79
|
+
require 'woodhouse/layout_builder'
|
80
|
+
require 'woodhouse/scheduler'
|
81
|
+
require 'woodhouse/server'
|
82
|
+
require 'woodhouse/queue_criteria'
|
83
|
+
require 'woodhouse/node_configuration'
|
84
|
+
require 'woodhouse/registry'
|
85
|
+
require 'woodhouse/mixin_registry'
|
86
|
+
require 'woodhouse/worker'
|
87
|
+
require 'woodhouse/job_execution'
|
88
|
+
require 'woodhouse/runners'
|
89
|
+
require 'woodhouse/dispatchers'
|
90
|
+
require 'woodhouse/middleware_stack'
|
91
|
+
require 'woodhouse/middleware'
|
92
|
+
require 'woodhouse/rails'
|
93
|
+
require 'woodhouse/process'
|
94
|
+
require 'woodhouse/layout_serializer'
|
95
|
+
require 'woodhouse/trigger_set'
|
96
|
+
|
97
|
+
require 'woodhouse/extension'
|
98
|
+
require 'woodhouse/extensions/progress'
|
99
|
+
require 'woodhouse/extensions/new_relic'
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require 'woodhouse'
|
2
|
+
require File.dirname(File.expand_path(__FILE__)) + '/../shared_contexts'
|
3
|
+
|
4
|
+
describe Woodhouse::Runners::BunnyRunner do
|
5
|
+
it_should_behave_like "common"
|
6
|
+
|
7
|
+
let(:scheduler) {
|
8
|
+
common_config.runner_type = :bunny
|
9
|
+
Woodhouse::Scheduler.new(common_config)
|
10
|
+
}
|
11
|
+
|
12
|
+
let(:worker) {
|
13
|
+
Woodhouse::Layout::Worker.new(:FooBarWorker, :foo, :only => { :orz => "*happy campers*" })
|
14
|
+
}
|
15
|
+
|
16
|
+
it "should pull jobs off a queue" do
|
17
|
+
scheduler.start_worker worker
|
18
|
+
sleep 0.5
|
19
|
+
# TODO: this should use the bunny dispatcher, once I write it
|
20
|
+
bunny = Bunny.new
|
21
|
+
bunny.start
|
22
|
+
exchange = bunny.exchange(worker.exchange_name, :type => :headers)
|
23
|
+
exchange.publish("hi", :headers => { :orz => "*happy campers*" })
|
24
|
+
exchange.publish("hi", :headers => { :orz => "*silly cows*" })
|
25
|
+
bunny.stop
|
26
|
+
sleep 0.2
|
27
|
+
FakeWorker.jobs.should_not be_empty
|
28
|
+
FakeWorker.jobs.last[:orz].should == "*happy campers*"
|
29
|
+
scheduler.spin_down
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
require 'woodhouse'
|
2
|
+
require File.dirname(File.expand_path(__FILE__)) + '/shared_contexts'
|
3
|
+
|
4
|
+
describe Woodhouse::LayoutBuilder do
|
5
|
+
it_should_behave_like "common"
|
6
|
+
|
7
|
+
it "should provide a DSL to set up layouts" do
|
8
|
+
registry = {
|
9
|
+
:Pam => FakeWorker,
|
10
|
+
:Cyril => FakeWorker,
|
11
|
+
:Ray => FakeWorker,
|
12
|
+
:Lana => FakeWorker,
|
13
|
+
}
|
14
|
+
common_config.registry = registry
|
15
|
+
builder = Woodhouse::LayoutBuilder.new(common_config) do |layout|
|
16
|
+
layout.node(:default) do |default|
|
17
|
+
# Eight workers...
|
18
|
+
default.all_workers :threads => 2
|
19
|
+
# Six workers...
|
20
|
+
default.remove :Cyril
|
21
|
+
# Five workers...
|
22
|
+
default.remove :Ray, :foo
|
23
|
+
# Six workers.
|
24
|
+
default.add :Ray, :bar, :only => { :baz => "bat" }
|
25
|
+
end
|
26
|
+
layout.node(:odin) do |odin|
|
27
|
+
# Two workers.
|
28
|
+
odin.add :Lana, :threads => 2
|
29
|
+
# Still two workers
|
30
|
+
odin.add :Lana, :threads => 5
|
31
|
+
end
|
32
|
+
end
|
33
|
+
layout = builder.layout
|
34
|
+
layout.nodes.should have(2).nodes
|
35
|
+
default = layout.node(:default)
|
36
|
+
default.workers.should have(6).workers
|
37
|
+
default.workers.first.threads.should == 2
|
38
|
+
default.workers.map(&:worker_class_name).should_not include(:Cyril)
|
39
|
+
default.workers.map(&:worker_class_name).should include(:Ray)
|
40
|
+
default.workers.select{|wk|
|
41
|
+
wk.worker_class_name == :Ray
|
42
|
+
}.map(&:job_method).should_not include(:foo)
|
43
|
+
ray = default.workers.detect{|wk|
|
44
|
+
wk.worker_class_name == :Ray && wk.criteria.criteria
|
45
|
+
}
|
46
|
+
ray.should_not be_nil
|
47
|
+
ray.criteria.matches?("baz" => "bat").should be_true
|
48
|
+
odin = layout.node(:odin)
|
49
|
+
odin.workers.should have(2).workers
|
50
|
+
odin.workers.first.threads.should == 5
|
51
|
+
|
52
|
+
Woodhouse::Layout.load(layout.dump).dump.should == layout.dump
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|