resque-director 1.0.1 → 1.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile +1 -2
- data/README.rdoc +13 -1
- data/VERSION +1 -1
- data/lib/resque/plugins/director/config.rb +7 -1
- data/lib/resque/plugins/director/scaler.rb +8 -4
- data/lib/resque/plugins/director/worker_tracker.rb +4 -0
- data/resque-director.gemspec +5 -8
- data/spec/resque/plugins/director/config_spec.rb +16 -0
- data/spec/resque/plugins/director/scaler_spec.rb +10 -6
- data/spec/resque/plugins/director_spec.rb +0 -1
- metadata +17 -29
data/Gemfile
CHANGED
data/README.rdoc
CHANGED
@@ -6,6 +6,8 @@ Resque Director is a plugin for the Resque queueing system (http://github.com/de
|
|
6
6
|
|
7
7
|
resque-director is mainly useful for when you are managing a large number of workers and don't want to waste resources keeping all of them waiting when they are not being used. Also useful in queues where the influx of jobs can change dramatically from time to time: enabling more workers during the times when the queue is filling up more quickly, and less in the opposite scenario. Different queues can be given different directions as well.
|
8
8
|
|
9
|
+
resque-director does not operate on workers that serve multiple queues, and does not include those workers in any scaling calculations.
|
10
|
+
|
9
11
|
== Usage
|
10
12
|
|
11
13
|
When creating your jobs you should include (make sure to include not extend) Resque::Plugins::Director and add direction options.
|
@@ -33,7 +35,7 @@ For Example:
|
|
33
35
|
|
34
36
|
<b>wait_time</b>:: the time that it will wait after adding or removing a worker before being allowed to add or remove workers again. The default is 60 seconds.
|
35
37
|
|
36
|
-
|
38
|
+
=== Worker Options
|
37
39
|
|
38
40
|
<b>start_override</b>:: This will run exactly what you put in the command override as a system command to start A SINGLE worker, allowing you to customize the starting of a worker fully. The system command "QUEUE=queue_name rake resque:work" is run by default, where queue_name is whatever queue the job is running.
|
39
41
|
|
@@ -53,6 +55,16 @@ For Example:
|
|
53
55
|
|
54
56
|
A worker will be removed if the jobs in the queue fall below half of the <b>max_queue</b>, or if the time it takes for a job to be pulled off of a queue falls below half of the <b>max_time</b>. Workers will be scaled down to the minimum if there are no jobs on the queue.
|
55
57
|
|
58
|
+
=== Logging
|
59
|
+
|
60
|
+
To add director logging you can pass a logger as an option. It will prepend all log messages with "DIRECTORS LOG:" and will log when scaling up or down a worker. It will also log when a worker wants to scale up or down but is unable to do so due to the fact that the maximum or minimum number of workers has been reached for that queue.
|
61
|
+
|
62
|
+
=== Logger Options
|
63
|
+
|
64
|
+
<b>logger</b>:: This will set the logger that will be used. It takes a Logger from the Ruby Standard Library. If this is not set the director will not write to any logs.
|
65
|
+
|
66
|
+
<b>log_level</b>:: This sets the level to log at as a symbol. The default level is :warn.
|
67
|
+
|
56
68
|
=== Special Cases
|
57
69
|
|
58
70
|
* If a max_worker is less than min_worker then the default for max_worker will be used (there will be no maximum).
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
1.0
|
1
|
+
1.1.0
|
@@ -13,7 +13,9 @@ module Resque
|
|
13
13
|
:max_queue => 0,
|
14
14
|
:wait_time => 60,
|
15
15
|
:start_override => nil,
|
16
|
-
:stop_override => nil
|
16
|
+
:stop_override => nil,
|
17
|
+
:logger => nil,
|
18
|
+
:log_level => :warn
|
17
19
|
}
|
18
20
|
|
19
21
|
def reset!
|
@@ -22,6 +24,10 @@ module Resque
|
|
22
24
|
self.instance_variable_set("@#{key.to_s}", default)
|
23
25
|
end
|
24
26
|
end
|
27
|
+
|
28
|
+
def log(message)
|
29
|
+
@logger.send(@log_level, "DIRECTORS LOG: #{message}") if @logger
|
30
|
+
end
|
25
31
|
|
26
32
|
def setup(options={})
|
27
33
|
DEFAULT_OPTIONS.each do |key, value|
|
@@ -7,7 +7,7 @@ module Resque
|
|
7
7
|
def scale_up(number_of_workers=1)
|
8
8
|
number_of_workers = WorkerTracker.new.total_to_add(number_of_workers)
|
9
9
|
scaling(number_of_workers) do
|
10
|
-
number_of_workers
|
10
|
+
start(number_of_workers)
|
11
11
|
end
|
12
12
|
end
|
13
13
|
|
@@ -51,18 +51,22 @@ module Resque
|
|
51
51
|
time_passed >= Config.wait_time
|
52
52
|
end
|
53
53
|
|
54
|
-
def start
|
54
|
+
def start(number_of_workers)
|
55
|
+
Config.log("starting #{number_of_workers} workers on queue:#{Config.queue}") if number_of_workers > 0
|
55
56
|
default_command = "QUEUE=#{Config.queue} rake resque:work &"
|
56
|
-
system(Config.start_override || default_command)
|
57
|
+
number_of_workers.times { system(Config.start_override || default_command) }
|
57
58
|
end
|
58
59
|
|
59
60
|
def stop(tracker, number_of_workers)
|
61
|
+
Config.log("stopping #{number_of_workers} workers on queue:#{Config.queue}") if number_of_workers > 0
|
60
62
|
if Config.stop_override
|
61
63
|
number_of_workers.times { system(Config.stop_override) }
|
62
64
|
else
|
63
65
|
valid_workers = tracker.workers.select{|w| w.hostname == `hostname`.chomp}
|
64
66
|
worker_pids = valid_workers[0...number_of_workers].map(&:pid)
|
65
|
-
worker_pids.each
|
67
|
+
worker_pids.each do |pid|
|
68
|
+
Process.kill("QUIT", pid) rescue nil
|
69
|
+
end
|
66
70
|
end
|
67
71
|
end
|
68
72
|
end
|
@@ -25,12 +25,16 @@ module Resque
|
|
25
25
|
def total_to_add(number_to_start)
|
26
26
|
return number_to_start if Config.max_workers <= 0
|
27
27
|
scale_limit = Config.max_workers - @number_working
|
28
|
+
Config.log("WORKER MAX REACHED: wanted to start #{number_to_start} workers on queue:#{Config.queue}") if scale_limit <= 0
|
28
29
|
number_to_start > scale_limit ? scale_limit : number_to_start
|
29
30
|
end
|
30
31
|
|
31
32
|
def total_to_remove(number_to_stop)
|
32
33
|
min_workers = Config.min_workers <= 0 ? 1 : Config.min_workers
|
33
34
|
scale_limit = @number_working - min_workers
|
35
|
+
if scale_limit <= 0 && Config.min_workers > 0
|
36
|
+
Config.log("WORKER MIN REACHED: wanted to stop #{number_to_stop} workers on queue:#{Config.queue}")
|
37
|
+
end
|
34
38
|
number_to_stop > scale_limit ? scale_limit : number_to_stop
|
35
39
|
end
|
36
40
|
|
data/resque-director.gemspec
CHANGED
@@ -5,11 +5,11 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = %q{resque-director}
|
8
|
-
s.version = "1.0
|
8
|
+
s.version = "1.1.0"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = [%q{Nolan Frausto}]
|
12
|
-
s.date = %q{2011-08-
|
12
|
+
s.date = %q{2011-08-23}
|
13
13
|
s.description = %q{resque plugin for dynamically adding/removing workers to a queue}
|
14
14
|
s.email = %q{nrfrausto@gmail.com}
|
15
15
|
s.extra_rdoc_files = [
|
@@ -50,23 +50,20 @@ Gem::Specification.new do |s|
|
|
50
50
|
s.specification_version = 3
|
51
51
|
|
52
52
|
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
53
|
-
s.add_runtime_dependency(%q<
|
54
|
-
s.add_runtime_dependency(%q<resque>, [">= 0"])
|
53
|
+
s.add_runtime_dependency(%q<resque>, ["~> 1.14.0"])
|
55
54
|
s.add_development_dependency(%q<rspec>, ["~> 2.3.0"])
|
56
55
|
s.add_development_dependency(%q<bundler>, ["~> 1.0.0"])
|
57
56
|
s.add_development_dependency(%q<jeweler>, ["~> 1.6.4"])
|
58
57
|
s.add_development_dependency(%q<rcov>, [">= 0"])
|
59
58
|
else
|
60
|
-
s.add_dependency(%q<
|
61
|
-
s.add_dependency(%q<resque>, [">= 0"])
|
59
|
+
s.add_dependency(%q<resque>, ["~> 1.14.0"])
|
62
60
|
s.add_dependency(%q<rspec>, ["~> 2.3.0"])
|
63
61
|
s.add_dependency(%q<bundler>, ["~> 1.0.0"])
|
64
62
|
s.add_dependency(%q<jeweler>, ["~> 1.6.4"])
|
65
63
|
s.add_dependency(%q<rcov>, [">= 0"])
|
66
64
|
end
|
67
65
|
else
|
68
|
-
s.add_dependency(%q<
|
69
|
-
s.add_dependency(%q<resque>, [">= 0"])
|
66
|
+
s.add_dependency(%q<resque>, ["~> 1.14.0"])
|
70
67
|
s.add_dependency(%q<rspec>, ["~> 2.3.0"])
|
71
68
|
s.add_dependency(%q<bundler>, ["~> 1.0.0"])
|
72
69
|
s.add_dependency(%q<jeweler>, ["~> 1.6.4"])
|
@@ -34,4 +34,20 @@ describe Resque::Plugins::Director::Config do
|
|
34
34
|
subject.wait_time.should == 60
|
35
35
|
end
|
36
36
|
end
|
37
|
+
|
38
|
+
describe "log" do
|
39
|
+
it "logs message to a logger using given log level if specified" do
|
40
|
+
log = mock('Logger')
|
41
|
+
subject.setup(:logger => log, :log_level => :info)
|
42
|
+
log.should_receive(:info).with("DIRECTORS LOG: test message")
|
43
|
+
subject.log("test message")
|
44
|
+
end
|
45
|
+
|
46
|
+
it "defaults log level to warn" do
|
47
|
+
log = mock('Logger')
|
48
|
+
subject.setup(:logger => log)
|
49
|
+
log.should_receive(:warn).with("DIRECTORS LOG: test message")
|
50
|
+
subject.log("test message")
|
51
|
+
end
|
52
|
+
end
|
37
53
|
end
|
@@ -83,17 +83,15 @@ describe Resque::Plugins::Director::Scaler do
|
|
83
83
|
end
|
84
84
|
|
85
85
|
it "should scale down a single worker by default" do
|
86
|
-
|
87
|
-
Resque.should_receive(:workers).and_return [@worker, worker2]
|
86
|
+
Resque.should_receive(:workers).and_return [@worker, @worker]
|
88
87
|
|
89
88
|
Process.should_receive(:kill).once
|
90
89
|
subject.scale_down
|
91
90
|
end
|
92
91
|
|
93
92
|
it "should scale down multiple workers" do
|
94
|
-
|
95
|
-
|
96
|
-
[@worker, worker2].each { |w| Process.should_receive(:kill).with("QUIT", w.pid) }
|
93
|
+
Resque.should_receive(:workers).and_return [@worker, @worker, @worker]
|
94
|
+
Process.should_receive(:kill).with("QUIT", @worker.pid)
|
97
95
|
subject.scale_down(2)
|
98
96
|
end
|
99
97
|
|
@@ -105,11 +103,17 @@ describe Resque::Plugins::Director::Scaler do
|
|
105
103
|
subject.scale_down(2)
|
106
104
|
end
|
107
105
|
|
106
|
+
it "should not throw exceptions when process throws exception" do
|
107
|
+
Resque.should_receive(:workers).and_return [@worker, @worker]
|
108
|
+
Process.should_receive(:kill).and_throw(:Exception)
|
109
|
+
lambda { subject.scale_down }.should_not raise_error
|
110
|
+
end
|
111
|
+
|
108
112
|
it "should not scale down workers on different queues" do
|
109
113
|
worker2 = Resque::Worker.new(:not_test)
|
110
114
|
@worker.stub(:pid => 1)
|
111
115
|
worker2.stub(:pid => 2)
|
112
|
-
Resque.should_receive(:workers).and_return [@worker, @worker, worker2]
|
116
|
+
Resque.should_receive(:workers).and_return [worker2, @worker, @worker, worker2]
|
113
117
|
|
114
118
|
Process.should_not_receive(:kill).with("QUIT", worker2.pid)
|
115
119
|
Process.should_receive(:kill).with("QUIT", @worker.pid)
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: resque-director
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 19
|
5
5
|
prerelease:
|
6
6
|
segments:
|
7
7
|
- 1
|
8
|
-
- 0
|
9
8
|
- 1
|
10
|
-
|
9
|
+
- 0
|
10
|
+
version: 1.1.0
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Nolan Frausto
|
@@ -15,38 +15,26 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date: 2011-08-
|
18
|
+
date: 2011-08-23 00:00:00 Z
|
19
19
|
dependencies:
|
20
20
|
- !ruby/object:Gem::Dependency
|
21
21
|
requirement: &id001 !ruby/object:Gem::Requirement
|
22
22
|
none: false
|
23
23
|
requirements:
|
24
|
-
- -
|
24
|
+
- - ~>
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
hash:
|
26
|
+
hash: 47
|
27
27
|
segments:
|
28
|
+
- 1
|
29
|
+
- 14
|
28
30
|
- 0
|
29
|
-
version:
|
31
|
+
version: 1.14.0
|
30
32
|
version_requirements: *id001
|
31
|
-
name: json
|
32
|
-
prerelease: false
|
33
|
-
type: :runtime
|
34
|
-
- !ruby/object:Gem::Dependency
|
35
|
-
requirement: &id002 !ruby/object:Gem::Requirement
|
36
|
-
none: false
|
37
|
-
requirements:
|
38
|
-
- - ">="
|
39
|
-
- !ruby/object:Gem::Version
|
40
|
-
hash: 3
|
41
|
-
segments:
|
42
|
-
- 0
|
43
|
-
version: "0"
|
44
|
-
version_requirements: *id002
|
45
33
|
name: resque
|
46
34
|
prerelease: false
|
47
35
|
type: :runtime
|
48
36
|
- !ruby/object:Gem::Dependency
|
49
|
-
requirement: &
|
37
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
50
38
|
none: false
|
51
39
|
requirements:
|
52
40
|
- - ~>
|
@@ -57,12 +45,12 @@ dependencies:
|
|
57
45
|
- 3
|
58
46
|
- 0
|
59
47
|
version: 2.3.0
|
60
|
-
version_requirements: *
|
48
|
+
version_requirements: *id002
|
61
49
|
name: rspec
|
62
50
|
prerelease: false
|
63
51
|
type: :development
|
64
52
|
- !ruby/object:Gem::Dependency
|
65
|
-
requirement: &
|
53
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
66
54
|
none: false
|
67
55
|
requirements:
|
68
56
|
- - ~>
|
@@ -73,12 +61,12 @@ dependencies:
|
|
73
61
|
- 0
|
74
62
|
- 0
|
75
63
|
version: 1.0.0
|
76
|
-
version_requirements: *
|
64
|
+
version_requirements: *id003
|
77
65
|
name: bundler
|
78
66
|
prerelease: false
|
79
67
|
type: :development
|
80
68
|
- !ruby/object:Gem::Dependency
|
81
|
-
requirement: &
|
69
|
+
requirement: &id004 !ruby/object:Gem::Requirement
|
82
70
|
none: false
|
83
71
|
requirements:
|
84
72
|
- - ~>
|
@@ -89,12 +77,12 @@ dependencies:
|
|
89
77
|
- 6
|
90
78
|
- 4
|
91
79
|
version: 1.6.4
|
92
|
-
version_requirements: *
|
80
|
+
version_requirements: *id004
|
93
81
|
name: jeweler
|
94
82
|
prerelease: false
|
95
83
|
type: :development
|
96
84
|
- !ruby/object:Gem::Dependency
|
97
|
-
requirement: &
|
85
|
+
requirement: &id005 !ruby/object:Gem::Requirement
|
98
86
|
none: false
|
99
87
|
requirements:
|
100
88
|
- - ">="
|
@@ -103,7 +91,7 @@ dependencies:
|
|
103
91
|
segments:
|
104
92
|
- 0
|
105
93
|
version: "0"
|
106
|
-
version_requirements: *
|
94
|
+
version_requirements: *id005
|
107
95
|
name: rcov
|
108
96
|
prerelease: false
|
109
97
|
type: :development
|