middle_management 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +3 -0
- data/.rvmrc +3 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +110 -0
- data/README +62 -0
- data/Rakefile +2 -0
- data/lib/middle_management/config.rb +10 -0
- data/lib/middle_management/job_modifications.rb +12 -0
- data/lib/middle_management/manager.rb +37 -0
- data/lib/middle_management/slacker.rb +7 -0
- data/lib/middle_management/version.rb +3 -0
- data/lib/middle_management.rb +4 -0
- data/middle_management.gemspec +29 -0
- data/spec/lib/middle_management/job_modifications_spec.rb +18 -0
- data/spec/lib/middle_management/manager_spec.rb +171 -0
- data/spec/lib/middle_management_spec.rb +4 -0
- data/spec/spec_helper.rb +26 -0
- metadata +180 -0
data/.gitignore
ADDED
data/.rvmrc
ADDED
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,110 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
middle_management (0.0.1)
|
5
|
+
activesupport (~> 3.0)
|
6
|
+
delayed_job (>= 2.1.2)
|
7
|
+
heroku (>= 1.17.5)
|
8
|
+
|
9
|
+
GEM
|
10
|
+
remote: http://rubygems.org/
|
11
|
+
specs:
|
12
|
+
abstract (1.0.0)
|
13
|
+
actionmailer (3.0.3)
|
14
|
+
actionpack (= 3.0.3)
|
15
|
+
mail (~> 2.2.9)
|
16
|
+
actionpack (3.0.3)
|
17
|
+
activemodel (= 3.0.3)
|
18
|
+
activesupport (= 3.0.3)
|
19
|
+
builder (~> 2.1.2)
|
20
|
+
erubis (~> 2.6.6)
|
21
|
+
i18n (~> 0.4)
|
22
|
+
rack (~> 1.2.1)
|
23
|
+
rack-mount (~> 0.6.13)
|
24
|
+
rack-test (~> 0.5.6)
|
25
|
+
tzinfo (~> 0.3.23)
|
26
|
+
activemodel (3.0.3)
|
27
|
+
activesupport (= 3.0.3)
|
28
|
+
builder (~> 2.1.2)
|
29
|
+
i18n (~> 0.4)
|
30
|
+
activerecord (3.0.3)
|
31
|
+
activemodel (= 3.0.3)
|
32
|
+
activesupport (= 3.0.3)
|
33
|
+
arel (~> 2.0.2)
|
34
|
+
tzinfo (~> 0.3.23)
|
35
|
+
activeresource (3.0.3)
|
36
|
+
activemodel (= 3.0.3)
|
37
|
+
activesupport (= 3.0.3)
|
38
|
+
activesupport (3.0.3)
|
39
|
+
arel (2.0.7)
|
40
|
+
builder (2.1.2)
|
41
|
+
configuration (1.2.0)
|
42
|
+
daemons (1.1.0)
|
43
|
+
delayed_job (2.1.2)
|
44
|
+
activesupport (~> 3.0)
|
45
|
+
daemons
|
46
|
+
diff-lcs (1.1.2)
|
47
|
+
erubis (2.6.6)
|
48
|
+
abstract (>= 1.0.0)
|
49
|
+
fakeweb (1.3.0)
|
50
|
+
heroku (1.17.5)
|
51
|
+
json (~> 1.4.6)
|
52
|
+
launchy (~> 0.3.2)
|
53
|
+
rest-client (>= 1.4.0, < 1.7.0)
|
54
|
+
i18n (0.5.0)
|
55
|
+
json (1.4.6)
|
56
|
+
launchy (0.3.7)
|
57
|
+
configuration (>= 0.0.5)
|
58
|
+
rake (>= 0.8.1)
|
59
|
+
mail (2.2.14)
|
60
|
+
activesupport (>= 2.3.6)
|
61
|
+
i18n (>= 0.4.0)
|
62
|
+
mime-types (~> 1.16)
|
63
|
+
treetop (~> 1.4.8)
|
64
|
+
mime-types (1.16)
|
65
|
+
polyglot (0.3.1)
|
66
|
+
rack (1.2.1)
|
67
|
+
rack-mount (0.6.13)
|
68
|
+
rack (>= 1.0.0)
|
69
|
+
rack-test (0.5.7)
|
70
|
+
rack (>= 1.0)
|
71
|
+
rails (3.0.3)
|
72
|
+
actionmailer (= 3.0.3)
|
73
|
+
actionpack (= 3.0.3)
|
74
|
+
activerecord (= 3.0.3)
|
75
|
+
activeresource (= 3.0.3)
|
76
|
+
activesupport (= 3.0.3)
|
77
|
+
bundler (~> 1.0)
|
78
|
+
railties (= 3.0.3)
|
79
|
+
railties (3.0.3)
|
80
|
+
actionpack (= 3.0.3)
|
81
|
+
activesupport (= 3.0.3)
|
82
|
+
rake (>= 0.8.7)
|
83
|
+
thor (~> 0.14.4)
|
84
|
+
rake (0.8.7)
|
85
|
+
rest-client (1.6.1)
|
86
|
+
mime-types (>= 1.16)
|
87
|
+
rspec (2.4.0)
|
88
|
+
rspec-core (~> 2.4.0)
|
89
|
+
rspec-expectations (~> 2.4.0)
|
90
|
+
rspec-mocks (~> 2.4.0)
|
91
|
+
rspec-core (2.4.0)
|
92
|
+
rspec-expectations (2.4.0)
|
93
|
+
diff-lcs (~> 1.1.2)
|
94
|
+
rspec-mocks (2.4.0)
|
95
|
+
thor (0.14.6)
|
96
|
+
treetop (1.4.9)
|
97
|
+
polyglot (>= 0.3.1)
|
98
|
+
tzinfo (0.3.24)
|
99
|
+
|
100
|
+
PLATFORMS
|
101
|
+
ruby
|
102
|
+
|
103
|
+
DEPENDENCIES
|
104
|
+
activesupport (~> 3.0)
|
105
|
+
delayed_job (>= 2.1.2)
|
106
|
+
fakeweb (>= 1.3.0)
|
107
|
+
heroku (>= 1.17.5)
|
108
|
+
middle_management!
|
109
|
+
rails (~> 3.0)
|
110
|
+
rspec (>= 2.4.0)
|
data/README
ADDED
@@ -0,0 +1,62 @@
|
|
1
|
+
Middle Management manages your Delayed Job workers on Heroku. It "hires" and "fires" workers automatically so that you get all of your work done quickly for as little money as possible.
|
2
|
+
|
3
|
+
It requires delayed_job 2.1.2 or greater.
|
4
|
+
|
5
|
+
To get started:
|
6
|
+
|
7
|
+
1) Add "middle_management" to your Gemfile and run "bundle install"
|
8
|
+
|
9
|
+
2) Set the following REQUIRED environment variables using heroku config:add KEY=value:
|
10
|
+
MIDDLE_MANAGEMENT_HEROKU_USERNAME (your heroku username)
|
11
|
+
MIDDLE_MANAGEMENT_HEROKU_PASSWORD (your heroku password)
|
12
|
+
MIDDLE_MANAGEMENT_HEROKU_APP (the name of your heroku app)
|
13
|
+
MIDDLE_MANAGEMENT_MIN_WORKERS (the minimum number of workers to keep running; typically 0)
|
14
|
+
MIDDLE_MANAGEMENT_MAX_WORKERS (the maximum number of workers to allow to run)
|
15
|
+
|
16
|
+
3) Deploy to heroku and voila - you've got auto-scaling workers!
|
17
|
+
|
18
|
+
You can also limit the rate at which workers will scale up by setting:
|
19
|
+
MIDDLE_MANAGEMENT_JOBS_PER_WORKER (default = 1)
|
20
|
+
This should be the number of jobs a single worker can do more quickly than it takes to bring up another heroku worker. For short tasks with high throughput (e.g. sending emails), you should bump this up (I set it at 10). For long tasks with low throughput (e.g. PDF generation or traversing a social graph), keep this low.
|
21
|
+
|
22
|
+
How it works:
|
23
|
+
|
24
|
+
Every time a job is created or destroyed, Middle Management checks to see how many jobs are outstanding and how many workers are reserved to work on them. If the number of workers needed differs from the number of current workers, Middle Management hires or fires the needed or excess workers.
|
25
|
+
|
26
|
+
workers_needed is calculated as: outstanding jobs / MIDDLE_MANAGEMENT_JOBS_PER_WORKER
|
27
|
+
|
28
|
+
Middle Management will ALWAYS honor the maximum number of workers, as set by the MIDDLE_MANAGEMENT_MAX_WORKERS variable, and the minimum number of workers, as set by the MIDDLE_MANAGEMENT_MIN_WORKERS variable. If you use Delayed Job to schedule future jobs, you should always keep at least one worker running so that it can be worked off at the appropriate time. If all of your jobs are runnable at the time they are created, it is safe to set MIDDLE_MANAGEMENT_MIN_WORKERS to zero.
|
29
|
+
|
30
|
+
Step-by-step chart, assumes:
|
31
|
+
MIDDLE_MANAGEMENT_MIN_WORKERS=0
|
32
|
+
MIDDLE_MANAGEMENT_MAX_WORKERS=10
|
33
|
+
MIDDLE_MANAGEMENT_JOBS_PER_WORKER=1
|
34
|
+
|
35
|
+
App Action Middle Management Action Total Workers Running
|
36
|
+
Boot Up 0
|
37
|
+
Rotate 3 images Start 3 workers 3
|
38
|
+
First image rotated Stop 1 worker 2
|
39
|
+
Second image rotated Stop 1 worker 1
|
40
|
+
Send 105 emails with send_later Start 8 workers 10
|
41
|
+
First 10 emails sent 10
|
42
|
+
Next 10 emails sent 10
|
43
|
+
Next 10 emails sent 10
|
44
|
+
Next 10 emails sent 10
|
45
|
+
Next 10 emails sent 10
|
46
|
+
Next 10 emails sent 10
|
47
|
+
Next 10 emails sent 10
|
48
|
+
Next 10 emails sent 10
|
49
|
+
Next 10 emails sent 10
|
50
|
+
Next 10 emails sent Stop 5 workers 5
|
51
|
+
Last 5 emails sent Stop 5 workers 0
|
52
|
+
|
53
|
+
Testing in your heroku environment:
|
54
|
+
|
55
|
+
Middle Management comes with a Slacker class, whose sole purpose is to create jobs that take some time to run. You can use this class to create jobs in your production environment and monitor how your workers scale accordingly.
|
56
|
+
|
57
|
+
To get started, go to a heroku console and run:
|
58
|
+
> 30.times {MiddleManagement::Slacker.delay.slack_for(10)}
|
59
|
+
|
60
|
+
This will create 30 jobs, each of which sleeps for 10 seconds. Middle Management will scale your workers as these jobs are created and run off. You can monitor the changes to your worker count by using "heroku workers" from your command line.
|
61
|
+
|
62
|
+
http://github.com/freerobby/heroku_mbo is an empty rails app that includes this gem. If you want to fiddle with this gem in isolation, I recommend cloning that repo, creating a heroku app from it, and playing with it on a console.
|
data/Rakefile
ADDED
@@ -0,0 +1,10 @@
|
|
1
|
+
module MiddleManagement
|
2
|
+
class Config
|
3
|
+
HEROKU_USERNAME = ENV['MIDDLE_MANAGEMENT_HEROKU_USERNAME']
|
4
|
+
HEROKU_PASSWORD = ENV['MIDDLE_MANAGEMENT_HEROKU_PASSWORD']
|
5
|
+
HEROKU_APP = ENV['MIDDLE_MANAGEMENT_HEROKU_APP']
|
6
|
+
MIN_WORKERS = ENV['MIDDLE_MANAGEMENT_MIN_WORKERS'].to_i
|
7
|
+
MAX_WORKERS = ENV['MIDDLE_MANAGEMENT_MAX_WORKERS'].to_i
|
8
|
+
JOBS_PER_WORKER = ENV['MIDDLE_MANAGEMENT_JOBS_PER_WORKER'].to_i == 0 ? 1 : ENV['MIDDLE_MANAGEMENT_JOBS_PER_WORKER'].to_i
|
9
|
+
end
|
10
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
require 'delayed_job'
|
2
|
+
require 'delayed/backend/active_record'
|
3
|
+
|
4
|
+
class Delayed::Job < ::ActiveRecord::Base
|
5
|
+
after_create :enforce
|
6
|
+
after_destroy :enforce
|
7
|
+
|
8
|
+
private
|
9
|
+
def self.enforce
|
10
|
+
MiddleManagement::Manager.enforce_number_of_current_jobs(Delayed::Job.where("run_at <= ? AND failed_at IS NULL AND locked_by IS NULL", Delayed::Backend::ActiveRecord::Job.db_time_now).count)
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'heroku'
|
2
|
+
|
3
|
+
module MiddleManagement
|
4
|
+
class Manager
|
5
|
+
def self.enforce_number_of_current_jobs(num)
|
6
|
+
self.set_num_workers(self.calculate_needed_worker_count(num)) if self.num_jobs_changes_worker_count?(num)
|
7
|
+
end
|
8
|
+
|
9
|
+
private
|
10
|
+
cattr_accessor :current_worker_count
|
11
|
+
|
12
|
+
def self.calculate_needed_worker_count(num_jobs)
|
13
|
+
ideal_worker_count = num_jobs / MiddleManagement::Config::JOBS_PER_WORKER + 1
|
14
|
+
ideal_worker_count -= 1 if num_jobs % MiddleManagement::Config::JOBS_PER_WORKER == 0
|
15
|
+
[MiddleManagement::Config::MIN_WORKERS, [ideal_worker_count, MiddleManagement::Config::MAX_WORKERS].min].max
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.num_jobs_changes_worker_count?(num_jobs)
|
19
|
+
return false if num_jobs.nil?
|
20
|
+
return false if self.calculate_needed_worker_count(num_jobs) == current_worker_count
|
21
|
+
return true if current_worker_count.nil?
|
22
|
+
# Next two lines are verified in calculate_needed_worker_count(), but let's be safe since we're dealing with real money...
|
23
|
+
return false if self.calculate_needed_worker_count(num_jobs) < MiddleManagement::Config::MIN_WORKERS
|
24
|
+
return false if self.calculate_needed_worker_count(num_jobs) > MiddleManagement::Config::MAX_WORKERS
|
25
|
+
true
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.get_heroku_client
|
29
|
+
Heroku::Client.new(MiddleManagement::Config::HEROKU_USERNAME, MiddleManagement::Config::HEROKU_PASSWORD)
|
30
|
+
end
|
31
|
+
|
32
|
+
def self.set_num_workers(num_workers)
|
33
|
+
self.get_heroku_client.set_workers(MiddleManagement::Config::HEROKU_APP, num_workers)
|
34
|
+
current_worker_count = num_workers
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "middle_management/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "middle_management"
|
7
|
+
s.version = MiddleManagement::VERSION
|
8
|
+
s.platform = Gem::Platform::RUBY
|
9
|
+
s.authors = ["Robby Grossman"]
|
10
|
+
s.email = ["robby@freerobby.com"]
|
11
|
+
s.homepage = "http://github.com/freerobby/middle_management"
|
12
|
+
s.summary = %q{Delayed Job worker management for Heroku.}
|
13
|
+
s.description = %q{Middle Management hires and fires your delayed_job workers automatically so that you get all of your work done quickly for as little money as possible.}
|
14
|
+
|
15
|
+
s.add_runtime_dependency "activesupport", "~> 3.0"
|
16
|
+
s.add_runtime_dependency "delayed_job", ">= 2.1.2"
|
17
|
+
s.add_runtime_dependency "heroku", ">= 1.17.5"
|
18
|
+
|
19
|
+
s.add_development_dependency "fakeweb", ">= 1.3.0"
|
20
|
+
s.add_development_dependency "rails", "~> 3.0"
|
21
|
+
s.add_development_dependency "rspec", ">= 2.4.0"
|
22
|
+
|
23
|
+
s.rubyforge_project = "middle_management"
|
24
|
+
|
25
|
+
s.files = `git ls-files`.split("\n")
|
26
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
27
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
28
|
+
s.require_paths = ["lib"]
|
29
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require File.expand_path("#{File.dirname(__FILE__)}/../../spec_helper")
|
2
|
+
|
3
|
+
describe Delayed::Job do
|
4
|
+
before do
|
5
|
+
@client_mock = mock("Heroku Client")
|
6
|
+
MiddleManagement::Manager.should_receive(:get_heroku_client).any_number_of_times.and_return(@client_mock)
|
7
|
+
end
|
8
|
+
|
9
|
+
describe "#enforce" do
|
10
|
+
it "micromanages remaining jobs" do
|
11
|
+
MiddleManagement::Manager.should_receive(:enforce_number_of_current_jobs).with(3).exactly(:once)
|
12
|
+
Delayed::Job.should_receive(:count).exactly(:once).and_return(3)
|
13
|
+
Delayed::Backend::ActiveRecord::Job.should_receive(:db_time_now).and_return(Time.now)
|
14
|
+
Delayed::Job.should_receive(:where).any_number_of_times.and_return(Delayed::Job)
|
15
|
+
Delayed::Job.send(:enforce)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,171 @@
|
|
1
|
+
require File.expand_path("#{File.dirname(__FILE__)}/../../spec_helper")
|
2
|
+
|
3
|
+
describe MiddleManagement::Manager do
|
4
|
+
describe "#enforce_number_of_current_jobs" do
|
5
|
+
before do
|
6
|
+
stub_config(:HEROKU_APP, "test_app")
|
7
|
+
stub_config(:MIN_WORKERS, 1)
|
8
|
+
stub_config(:MAX_WORKERS, 10)
|
9
|
+
@client_mock = mock("Heroku Client")
|
10
|
+
MiddleManagement::Manager.should_receive(:get_heroku_client).any_number_of_times.and_return(@client_mock)
|
11
|
+
end
|
12
|
+
describe "changes number of workers" do
|
13
|
+
it "makes api call" do
|
14
|
+
MiddleManagement::Manager.send(:current_worker_count=, 5)
|
15
|
+
@client_mock.should_receive(:set_workers).exactly(:once)
|
16
|
+
MiddleManagement::Manager.enforce_number_of_current_jobs(6)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
describe "no change to number of workers" do
|
20
|
+
it "does not make api call" do
|
21
|
+
MiddleManagement::Manager.send(:current_worker_count=, 5)
|
22
|
+
@client_mock.should_not_receive(:set_workers)
|
23
|
+
MiddleManagement::Manager.enforce_number_of_current_jobs(5)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
describe "private methods" do
|
29
|
+
describe "#calculate_needed_worker_count" do
|
30
|
+
describe "1 job per worker" do
|
31
|
+
before do
|
32
|
+
stub_config(:MIN_WORKERS, 1)
|
33
|
+
stub_config(:MAX_WORKERS, 10)
|
34
|
+
stub_config(:JOBS_PER_WORKER, 1)
|
35
|
+
end
|
36
|
+
it "returns min workers when fewer jobs than min workers" do
|
37
|
+
MiddleManagement::Manager.send(:calculate_needed_worker_count, 0).should == 1
|
38
|
+
end
|
39
|
+
it "returns max workers when more jobs than max workers" do
|
40
|
+
MiddleManagement::Manager.send(:calculate_needed_worker_count, 11).should == 10
|
41
|
+
end
|
42
|
+
it "returns number of jobs when job count in worker range" do
|
43
|
+
MiddleManagement::Manager.send(:calculate_needed_worker_count, 5).should == 5
|
44
|
+
end
|
45
|
+
end
|
46
|
+
describe "3 jobs per worker, MIN=0, MAX=10" do
|
47
|
+
before do
|
48
|
+
stub_config(:MIN_WORKERS, 0)
|
49
|
+
stub_config(:MAX_WORKERS, 10)
|
50
|
+
stub_config(:JOBS_PER_WORKER, 3)
|
51
|
+
end
|
52
|
+
it "returns 0 workers for 0 jobs" do
|
53
|
+
MiddleManagement::Manager.send(:calculate_needed_worker_count, 0).should == 0
|
54
|
+
end
|
55
|
+
end
|
56
|
+
describe "3 jobs per worker, MIN=1 MAX=10" do
|
57
|
+
before do
|
58
|
+
stub_config(:MIN_WORKERS, 1)
|
59
|
+
stub_config(:MAX_WORKERS, 10)
|
60
|
+
stub_config(:JOBS_PER_WORKER, 3)
|
61
|
+
end
|
62
|
+
it "returns 1 worker for 0 jobs" do
|
63
|
+
MiddleManagement::Manager.send(:calculate_needed_worker_count, 0).should == 1
|
64
|
+
end
|
65
|
+
it "returns 1 worker for 1 job" do
|
66
|
+
MiddleManagement::Manager.send(:calculate_needed_worker_count, 1).should == 1
|
67
|
+
end
|
68
|
+
it "returns 1 worker for 2 jobs" do
|
69
|
+
MiddleManagement::Manager.send(:calculate_needed_worker_count, 2).should == 1
|
70
|
+
end
|
71
|
+
it "returns 1 worker for 3 jobs" do
|
72
|
+
MiddleManagement::Manager.send(:calculate_needed_worker_count, 3).should == 1
|
73
|
+
end
|
74
|
+
it "returns 2 workers for 4 jobs" do
|
75
|
+
MiddleManagement::Manager.send(:calculate_needed_worker_count, 4).should == 2
|
76
|
+
end
|
77
|
+
it "returns 2 workers for 5 jobs" do
|
78
|
+
MiddleManagement::Manager.send(:calculate_needed_worker_count, 5).should == 2
|
79
|
+
end
|
80
|
+
it "returns 2 workers for 6 jobs" do
|
81
|
+
MiddleManagement::Manager.send(:calculate_needed_worker_count, 6).should == 2
|
82
|
+
end
|
83
|
+
it "returns 3 workers for 7 jobs" do
|
84
|
+
MiddleManagement::Manager.send(:calculate_needed_worker_count, 7).should == 3
|
85
|
+
end
|
86
|
+
it "returns 10 workers for 500 jobs" do
|
87
|
+
MiddleManagement::Manager.send(:calculate_needed_worker_count, 500).should == 10
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
describe "#num_jobs_changes_worker_count?" do
|
92
|
+
before do
|
93
|
+
stub_config(:MIN_WORKERS, 1)
|
94
|
+
stub_config(:MAX_WORKERS, 10)
|
95
|
+
stub_config(:JOBS_PER_WORKER, 1)
|
96
|
+
end
|
97
|
+
describe "running min workers" do
|
98
|
+
before do
|
99
|
+
MiddleManagement::Manager.send(:current_worker_count=, 1)
|
100
|
+
end
|
101
|
+
it "false to bring worker down" do
|
102
|
+
MiddleManagement::Manager.send(:num_jobs_changes_worker_count?, 0).should == false
|
103
|
+
end
|
104
|
+
it "true to bring worker up" do
|
105
|
+
MiddleManagement::Manager.send(:num_jobs_changes_worker_count?, 2).should == true
|
106
|
+
end
|
107
|
+
it "false to keep worker count" do
|
108
|
+
MiddleManagement::Manager.send(:num_jobs_changes_worker_count?, 1).should == false
|
109
|
+
end
|
110
|
+
end
|
111
|
+
describe "running max workers" do
|
112
|
+
before do
|
113
|
+
MiddleManagement::Manager.send(:current_worker_count=, 10)
|
114
|
+
end
|
115
|
+
it "true to bring worker down" do
|
116
|
+
MiddleManagement::Manager.send(:num_jobs_changes_worker_count?, 9).should == true
|
117
|
+
end
|
118
|
+
it "false to bring worker up" do
|
119
|
+
MiddleManagement::Manager.send(:num_jobs_changes_worker_count?, 11).should == false
|
120
|
+
end
|
121
|
+
it "false to keep worker count" do
|
122
|
+
MiddleManagement::Manager.send(:num_jobs_changes_worker_count?, 10).should == false
|
123
|
+
end
|
124
|
+
end
|
125
|
+
describe "num workers in middle of range" do
|
126
|
+
before do
|
127
|
+
MiddleManagement::Manager.send(:current_worker_count=, 5)
|
128
|
+
end
|
129
|
+
it "true to bring worker down" do
|
130
|
+
MiddleManagement::Manager.send(:num_jobs_changes_worker_count?, 4).should == true
|
131
|
+
end
|
132
|
+
it "true to bring worker up" do
|
133
|
+
MiddleManagement::Manager.send(:num_jobs_changes_worker_count?, 6).should == true
|
134
|
+
end
|
135
|
+
it "false to keep worker count" do
|
136
|
+
MiddleManagement::Manager.send(:num_jobs_changes_worker_count?, 5).should == false
|
137
|
+
end
|
138
|
+
end
|
139
|
+
describe "current_worker_count not set" do
|
140
|
+
before do
|
141
|
+
MiddleManagement::Manager.send(:current_worker_count=, nil)
|
142
|
+
end
|
143
|
+
it "true when setting to number" do
|
144
|
+
MiddleManagement::Manager.send(:num_jobs_changes_worker_count?, 1).should == true
|
145
|
+
end
|
146
|
+
it "false when setting to nil" do
|
147
|
+
MiddleManagement::Manager.send(:num_jobs_changes_worker_count?, nil).should == false
|
148
|
+
end
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
describe "#get_heroku_client" do
|
153
|
+
it "instantiates a heroku client using environment variables" do
|
154
|
+
stub_config(:HEROKU_USERNAME, "test_user")
|
155
|
+
stub_config(:HEROKU_PASSWORD, "test_pass")
|
156
|
+
Heroku::Client.should_receive(:new).with("test_user", "test_pass").exactly(:once)
|
157
|
+
MiddleManagement::Manager.send(:get_heroku_client)
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
describe "#set_num_workers" do
|
162
|
+
it "sets the specified number of workers" do
|
163
|
+
client_mock = mock("Heroku Client")
|
164
|
+
MiddleManagement::Manager.should_receive(:get_heroku_client).and_return(client_mock)
|
165
|
+
stub_config(:HEROKU_APP, "test_app")
|
166
|
+
client_mock.should_receive(:set_workers).with("test_app", 3).exactly(:once)
|
167
|
+
MiddleManagement::Manager.send(:set_num_workers, 3)
|
168
|
+
end
|
169
|
+
end
|
170
|
+
end
|
171
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'fakeweb'
|
2
|
+
|
3
|
+
require 'middle_management'
|
4
|
+
|
5
|
+
# Disable HTTP connections
|
6
|
+
RSpec.configure do |config|
|
7
|
+
config.before(:all) do
|
8
|
+
FakeWeb.allow_net_connect = false
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
# Backup and restore environment for each test
|
13
|
+
RSpec.configure do |config|
|
14
|
+
config.before(:each) do
|
15
|
+
@env_backup = Hash.new
|
16
|
+
ENV.keys.each {|key| @env_backup[key] = ENV[key]}
|
17
|
+
end
|
18
|
+
config.after(:each) do
|
19
|
+
@env_backup.keys.each {|key| ENV[key] = @env_backup[key]}
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def stub_config(constant, value)
|
24
|
+
MiddleManagement::Config.send(:remove_const, constant)
|
25
|
+
MiddleManagement::Config.const_set(constant, value)
|
26
|
+
end
|
metadata
ADDED
@@ -0,0 +1,180 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: middle_management
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 29
|
5
|
+
prerelease: false
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 0
|
9
|
+
- 1
|
10
|
+
version: 0.0.1
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- Robby Grossman
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2011-01-16 00:00:00 -05:00
|
19
|
+
default_executable:
|
20
|
+
dependencies:
|
21
|
+
- !ruby/object:Gem::Dependency
|
22
|
+
name: activesupport
|
23
|
+
prerelease: false
|
24
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ~>
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
hash: 7
|
30
|
+
segments:
|
31
|
+
- 3
|
32
|
+
- 0
|
33
|
+
version: "3.0"
|
34
|
+
type: :runtime
|
35
|
+
version_requirements: *id001
|
36
|
+
- !ruby/object:Gem::Dependency
|
37
|
+
name: delayed_job
|
38
|
+
prerelease: false
|
39
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
40
|
+
none: false
|
41
|
+
requirements:
|
42
|
+
- - ">="
|
43
|
+
- !ruby/object:Gem::Version
|
44
|
+
hash: 15
|
45
|
+
segments:
|
46
|
+
- 2
|
47
|
+
- 1
|
48
|
+
- 2
|
49
|
+
version: 2.1.2
|
50
|
+
type: :runtime
|
51
|
+
version_requirements: *id002
|
52
|
+
- !ruby/object:Gem::Dependency
|
53
|
+
name: heroku
|
54
|
+
prerelease: false
|
55
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
56
|
+
none: false
|
57
|
+
requirements:
|
58
|
+
- - ">="
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
hash: 89
|
61
|
+
segments:
|
62
|
+
- 1
|
63
|
+
- 17
|
64
|
+
- 5
|
65
|
+
version: 1.17.5
|
66
|
+
type: :runtime
|
67
|
+
version_requirements: *id003
|
68
|
+
- !ruby/object:Gem::Dependency
|
69
|
+
name: fakeweb
|
70
|
+
prerelease: false
|
71
|
+
requirement: &id004 !ruby/object:Gem::Requirement
|
72
|
+
none: false
|
73
|
+
requirements:
|
74
|
+
- - ">="
|
75
|
+
- !ruby/object:Gem::Version
|
76
|
+
hash: 27
|
77
|
+
segments:
|
78
|
+
- 1
|
79
|
+
- 3
|
80
|
+
- 0
|
81
|
+
version: 1.3.0
|
82
|
+
type: :development
|
83
|
+
version_requirements: *id004
|
84
|
+
- !ruby/object:Gem::Dependency
|
85
|
+
name: rails
|
86
|
+
prerelease: false
|
87
|
+
requirement: &id005 !ruby/object:Gem::Requirement
|
88
|
+
none: false
|
89
|
+
requirements:
|
90
|
+
- - ~>
|
91
|
+
- !ruby/object:Gem::Version
|
92
|
+
hash: 7
|
93
|
+
segments:
|
94
|
+
- 3
|
95
|
+
- 0
|
96
|
+
version: "3.0"
|
97
|
+
type: :development
|
98
|
+
version_requirements: *id005
|
99
|
+
- !ruby/object:Gem::Dependency
|
100
|
+
name: rspec
|
101
|
+
prerelease: false
|
102
|
+
requirement: &id006 !ruby/object:Gem::Requirement
|
103
|
+
none: false
|
104
|
+
requirements:
|
105
|
+
- - ">="
|
106
|
+
- !ruby/object:Gem::Version
|
107
|
+
hash: 31
|
108
|
+
segments:
|
109
|
+
- 2
|
110
|
+
- 4
|
111
|
+
- 0
|
112
|
+
version: 2.4.0
|
113
|
+
type: :development
|
114
|
+
version_requirements: *id006
|
115
|
+
description: Middle Management hires and fires your delayed_job workers automatically so that you get all of your work done quickly for as little money as possible.
|
116
|
+
email:
|
117
|
+
- robby@freerobby.com
|
118
|
+
executables: []
|
119
|
+
|
120
|
+
extensions: []
|
121
|
+
|
122
|
+
extra_rdoc_files: []
|
123
|
+
|
124
|
+
files:
|
125
|
+
- .gitignore
|
126
|
+
- .rvmrc
|
127
|
+
- Gemfile
|
128
|
+
- Gemfile.lock
|
129
|
+
- README
|
130
|
+
- Rakefile
|
131
|
+
- lib/middle_management.rb
|
132
|
+
- lib/middle_management/config.rb
|
133
|
+
- lib/middle_management/job_modifications.rb
|
134
|
+
- lib/middle_management/manager.rb
|
135
|
+
- lib/middle_management/slacker.rb
|
136
|
+
- lib/middle_management/version.rb
|
137
|
+
- middle_management.gemspec
|
138
|
+
- spec/lib/middle_management/job_modifications_spec.rb
|
139
|
+
- spec/lib/middle_management/manager_spec.rb
|
140
|
+
- spec/lib/middle_management_spec.rb
|
141
|
+
- spec/spec_helper.rb
|
142
|
+
has_rdoc: true
|
143
|
+
homepage: http://github.com/freerobby/middle_management
|
144
|
+
licenses: []
|
145
|
+
|
146
|
+
post_install_message:
|
147
|
+
rdoc_options: []
|
148
|
+
|
149
|
+
require_paths:
|
150
|
+
- lib
|
151
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
152
|
+
none: false
|
153
|
+
requirements:
|
154
|
+
- - ">="
|
155
|
+
- !ruby/object:Gem::Version
|
156
|
+
hash: 3
|
157
|
+
segments:
|
158
|
+
- 0
|
159
|
+
version: "0"
|
160
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
161
|
+
none: false
|
162
|
+
requirements:
|
163
|
+
- - ">="
|
164
|
+
- !ruby/object:Gem::Version
|
165
|
+
hash: 3
|
166
|
+
segments:
|
167
|
+
- 0
|
168
|
+
version: "0"
|
169
|
+
requirements: []
|
170
|
+
|
171
|
+
rubyforge_project: middle_management
|
172
|
+
rubygems_version: 1.3.7
|
173
|
+
signing_key:
|
174
|
+
specification_version: 3
|
175
|
+
summary: Delayed Job worker management for Heroku.
|
176
|
+
test_files:
|
177
|
+
- spec/lib/middle_management/job_modifications_spec.rb
|
178
|
+
- spec/lib/middle_management/manager_spec.rb
|
179
|
+
- spec/lib/middle_management_spec.rb
|
180
|
+
- spec/spec_helper.rb
|