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