autoscale 0.9.3 → 0.11.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.
Files changed (37) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +11 -2
  3. data/README.md +19 -13
  4. data/examples/complex.rb +3 -3
  5. data/examples/simple.rb +2 -2
  6. data/lib/autoscaler/binary_scaling_strategy.rb +1 -1
  7. data/lib/autoscaler/heroku_platform_scaler.rb +84 -0
  8. data/lib/autoscaler/ignore_scheduled_and_retrying.rb +5 -0
  9. data/lib/autoscaler/linear_scaling_strategy.rb +1 -1
  10. data/lib/autoscaler/sidekiq.rb +2 -2
  11. data/lib/autoscaler/sidekiq/client.rb +1 -1
  12. data/lib/autoscaler/sidekiq/entire_queue_system.rb +10 -0
  13. data/lib/autoscaler/sidekiq/sleep_wait_server.rb +2 -2
  14. data/lib/autoscaler/sidekiq/specified_queue_system.rb +10 -0
  15. data/lib/autoscaler/sidekiq/thread_server.rb +90 -0
  16. data/lib/autoscaler/version.rb +1 -1
  17. data/spec/autoscaler/binary_scaling_strategy_spec.rb +2 -2
  18. data/spec/autoscaler/counter_cache_memory_spec.rb +3 -3
  19. data/spec/autoscaler/counter_cache_redis_spec.rb +6 -6
  20. data/spec/autoscaler/delayed_shutdown_spec.rb +4 -4
  21. data/spec/autoscaler/heroku_platform_scaler_spec.rb +47 -0
  22. data/spec/autoscaler/heroku_scaler_spec.rb +8 -8
  23. data/spec/autoscaler/ignore_scheduled_and_retrying_spec.rb +4 -4
  24. data/spec/autoscaler/linear_scaling_strategy_spec.rb +13 -13
  25. data/spec/autoscaler/sidekiq/activity_spec.rb +4 -4
  26. data/spec/autoscaler/sidekiq/client_spec.rb +5 -5
  27. data/spec/autoscaler/sidekiq/entire_queue_system_spec.rb +11 -11
  28. data/spec/autoscaler/sidekiq/sleep_wait_server_spec.rb +21 -21
  29. data/spec/autoscaler/sidekiq/specified_queue_system_spec.rb +10 -10
  30. data/spec/autoscaler/sidekiq/thread_server_spec.rb +44 -0
  31. data/spec/spec_helper.rb +4 -2
  32. data/spec/test_system.rb +6 -0
  33. metadata +71 -15
  34. data/lib/autoscaler/sidekiq/celluloid_monitor.rb +0 -68
  35. data/lib/autoscaler/sidekiq/monitor_middleware_adapter.rb +0 -46
  36. data/spec/autoscaler/sidekiq/celluloid_monitor_spec.rb +0 -39
  37. data/spec/autoscaler/sidekiq/monitor_middleware_adapter_spec.rb +0 -16
@@ -8,12 +8,12 @@ describe Autoscaler::BinaryScalingStrategy do
8
8
  it "scales with no work" do
9
9
  system = TestSystem.new(0)
10
10
  strategy = cut.new
11
- strategy.call(system, 1).should == 0
11
+ expect(strategy.call(system, 1)).to eq 0
12
12
  end
13
13
 
14
14
  it "does not scale with pending work" do
15
15
  system = TestSystem.new(1)
16
16
  strategy = cut.new(2)
17
- strategy.call(system, 1).should == 2
17
+ expect(strategy.call(system, 1)).to eq 2
18
18
  end
19
19
  end
@@ -5,17 +5,17 @@ describe Autoscaler::CounterCacheMemory do
5
5
  let(:cut) {Autoscaler::CounterCacheMemory}
6
6
 
7
7
  it {expect{cut.new.counter}.to raise_error(cut::Expired)}
8
- it {cut.new.counter{1}.should == 1}
8
+ it {expect(cut.new.counter{1}).to eq 1}
9
9
 
10
10
  it 'set and store' do
11
11
  cache = cut.new
12
12
  cache.counter = 1
13
- cache.counter.should == 1
13
+ expect(cache.counter).to eq 1
14
14
  end
15
15
 
16
16
  it 'times out' do
17
17
  cache = cut.new(0)
18
18
  cache.counter = 1
19
- expect{cache.counter.should}.to raise_error(cut::Expired)
19
+ expect{cache.counter}.to raise_error(cut::Expired)
20
20
  end
21
21
  end
@@ -11,11 +11,11 @@ describe Autoscaler::CounterCacheRedis do
11
11
  subject {cut.new(Sidekiq.method(:redis))}
12
12
 
13
13
  it {expect{subject.counter}.to raise_error(cut::Expired)}
14
- it {subject.counter{1}.should == 1}
14
+ it {expect(subject.counter{1}).to eq 1}
15
15
 
16
16
  it 'set and store' do
17
17
  subject.counter = 2
18
- subject.counter.should == 2
18
+ expect(subject.counter).to eq 2
19
19
  end
20
20
 
21
21
  it 'does not conflict with multiple worker types' do
@@ -23,7 +23,7 @@ describe Autoscaler::CounterCacheRedis do
23
23
  subject.counter = 1
24
24
  other_worker_cache.counter = 2
25
25
 
26
- subject.counter.should == 1
26
+ expect(subject.counter).to eq 1
27
27
  other_worker_cache.counter = 2
28
28
  end
29
29
 
@@ -37,13 +37,13 @@ describe Autoscaler::CounterCacheRedis do
37
37
  it 'passed a connection pool' do
38
38
  cache = cut.new(@redis)
39
39
  cache.counter = 4
40
- cache.counter.should == 4
40
+ expect(cache.counter).to eq 4
41
41
  end
42
42
 
43
43
  it 'passed a plain connection' do
44
- connection = Redis.connect(:url => 'http://localhost:9736', :namespace => 'autoscaler')
44
+ connection = Redis.connect(:url => 'redis://localhost:9736', :namespace => 'autoscaler')
45
45
  cache = cut.new connection
46
46
  cache.counter = 5
47
- cache.counter.should == 5
47
+ expect(cache.counter).to eq 5
48
48
  end
49
49
  end
@@ -7,17 +7,17 @@ describe Autoscaler::DelayedShutdown do
7
7
 
8
8
  it "returns normal values" do
9
9
  strategy = cut.new(lambda{|s,t| 2}, 0)
10
- strategy.call(nil, 1).should == 2
10
+ expect(strategy.call(nil, 1)).to eq 2
11
11
  end
12
12
 
13
13
  it "delays zeros" do
14
14
  strategy = cut.new(lambda{|s,t| 0}, 60)
15
- strategy.call(nil, 1).should == 1
15
+ expect(strategy.call(nil, 1)).to eq 1
16
16
  end
17
17
 
18
18
  it "eventually returns zero" do
19
19
  strategy = cut.new(lambda{|s,t| 0}, 60)
20
- strategy.stub(:level_idle_time).and_return(61)
21
- strategy.call(nil, 61).should == 0
20
+ allow(strategy).to receive(:level_idle_time).and_return(61)
21
+ expect(strategy.call(nil, 61)).to eq 0
22
22
  end
23
23
  end
@@ -0,0 +1,47 @@
1
+ require 'spec_helper'
2
+ require 'autoscaler/heroku_platform_scaler'
3
+
4
+ describe Autoscaler::HerokuPlatformScaler, :platform_api => true do
5
+ let(:cut) {Autoscaler::HerokuPlatformScaler}
6
+ let(:client) {cut.new}
7
+ subject {client}
8
+
9
+ its(:workers) {should eq(0)}
10
+
11
+ describe 'scaled' do
12
+ around do |example|
13
+ client.workers = 1
14
+ example.call
15
+ client.workers = 0
16
+ end
17
+
18
+ its(:workers) {should eq(1)}
19
+ end
20
+
21
+ shared_examples 'exception handler' do |exception_class|
22
+ before do
23
+ expect(client).to receive(:client){
24
+ raise exception_class.new(Exception.new('oops'))
25
+ }
26
+ end
27
+
28
+ describe "default handler" do
29
+ it {expect{client.workers}.to_not raise_error}
30
+ it {expect(client.workers).to eq(0)}
31
+ it {expect{client.workers = 2}.to_not raise_error}
32
+ end
33
+
34
+ describe "custom handler" do
35
+ before do
36
+ @caught = false
37
+ client.exception_handler = lambda {|exception| @caught = true}
38
+ end
39
+
40
+ it {client.workers; expect(@caught).to be(true)}
41
+ end
42
+ end
43
+
44
+ describe 'exception handling', :focus => true do
45
+ it_behaves_like 'exception handler', Excon::Errors::Error
46
+ end
47
+ end
@@ -2,33 +2,33 @@ require 'spec_helper'
2
2
  require 'autoscaler/heroku_scaler'
3
3
  require 'heroku/api/errors'
4
4
 
5
- describe Autoscaler::HerokuScaler, :online => true do
5
+ describe Autoscaler::HerokuScaler, :api1 => true do
6
6
  let(:cut) {Autoscaler::HerokuScaler}
7
7
  let(:client) {cut.new}
8
8
  subject {client}
9
9
 
10
- its(:workers) {should == 0}
10
+ its(:workers) {should eq(0)}
11
11
 
12
12
  describe 'scaled' do
13
13
  around do |example|
14
14
  client.workers = 1
15
- example.yield
15
+ example.call
16
16
  client.workers = 0
17
17
  end
18
18
 
19
- its(:workers) {should == 1}
19
+ its(:workers) {should eq(1)}
20
20
  end
21
21
 
22
22
  shared_examples 'exception handler' do |exception_class|
23
23
  before do
24
- client.should_receive(:client){
24
+ expect(client).to receive(:client){
25
25
  raise exception_class.new(Exception.new('oops'))
26
26
  }
27
27
  end
28
28
 
29
29
  describe "default handler" do
30
30
  it {expect{client.workers}.to_not raise_error}
31
- it {client.workers.should == 0}
31
+ it {expect(client.workers).to eq(0)}
32
32
  it {expect{client.workers = 2}.to_not raise_error}
33
33
  end
34
34
 
@@ -38,7 +38,7 @@ describe Autoscaler::HerokuScaler, :online => true do
38
38
  client.exception_handler = lambda {|exception| @caught = true}
39
39
  end
40
40
 
41
- it {client.workers; @caught.should be_true}
41
+ it {client.workers; expect(@caught).to be(true)}
42
42
  end
43
43
  end
44
44
 
@@ -46,4 +46,4 @@ describe Autoscaler::HerokuScaler, :online => true do
46
46
  it_behaves_like 'exception handler', Excon::Errors::SocketError
47
47
  it_behaves_like 'exception handler', Heroku::API::Errors::Error
48
48
  end
49
- end
49
+ end
@@ -8,25 +8,25 @@ describe Autoscaler::IgnoreScheduledAndRetrying do
8
8
  it "passes through enqueued" do
9
9
  system = Struct.new(:enqueued).new(3)
10
10
  strategy = proc {|system, time| system.enqueued}
11
- cut.new(strategy).call(system, 0).should == 3
11
+ expect(cut.new(strategy).call(system, 0)).to eq 3
12
12
  end
13
13
 
14
14
  it "passes through workers" do
15
15
  system = Struct.new(:workers).new(3)
16
16
  strategy = proc {|system, time| system.workers}
17
- cut.new(strategy).call(system, 0).should == 3
17
+ expect(cut.new(strategy).call(system, 0)).to eq 3
18
18
  end
19
19
 
20
20
  it "ignores scheduled" do
21
21
  system = Struct.new(:scheduled).new(3)
22
22
  strategy = proc {|system, time| system.scheduled}
23
- cut.new(strategy).call(system, 0).should == 0
23
+ expect(cut.new(strategy).call(system, 0)).to eq 0
24
24
  end
25
25
 
26
26
  it "ignores retrying" do
27
27
  system = Struct.new(:retrying).new(3)
28
28
  strategy = proc {|system, time| system.retrying}
29
- cut.new(strategy).call(system, 0).should == 0
29
+ expect(cut.new(strategy).call(system, 0)).to eq 0
30
30
  end
31
31
  end
32
32
 
@@ -8,78 +8,78 @@ describe Autoscaler::LinearScalingStrategy do
8
8
  it "deactivates with no work" do
9
9
  system = TestSystem.new(0)
10
10
  strategy = cut.new(1)
11
- strategy.call(system, 1).should == 0
11
+ expect(strategy.call(system, 1)).to eq 0
12
12
  end
13
13
 
14
14
  it "activates with some work" do
15
15
  system = TestSystem.new(1)
16
16
  strategy = cut.new(1)
17
- strategy.call(system, 1).should be > 0
17
+ expect(strategy.call(system, 1)).to be > 0
18
18
  end
19
19
 
20
20
  it "minimally scales with minimal work" do
21
21
  system = TestSystem.new(1)
22
22
  strategy = cut.new(2, 2)
23
- strategy.call(system, 1).should == 1
23
+ expect(strategy.call(system, 1)).to eq 1
24
24
  end
25
25
 
26
26
  it "maximally scales with too much work" do
27
27
  system = TestSystem.new(5)
28
28
  strategy = cut.new(2, 2)
29
- strategy.call(system, 1).should == 2
29
+ expect(strategy.call(system, 1)).to eq 2
30
30
  end
31
31
 
32
32
  it "proportionally scales with some work" do
33
33
  system = TestSystem.new(5)
34
34
  strategy = cut.new(5, 2)
35
- strategy.call(system, 1).should == 3
35
+ expect(strategy.call(system, 1)).to eq 3
36
36
  end
37
37
 
38
38
  it "doesn't scale unless minimum is met" do
39
39
  system = TestSystem.new(2)
40
40
  strategy = cut.new(10, 4, 0.5)
41
- strategy.call(system, 1).should == 0
41
+ expect(strategy.call(system, 1)).to eq 0
42
42
  end
43
43
 
44
44
  it "scales proprotionally with a minimum" do
45
45
  system = TestSystem.new(3)
46
46
  strategy = cut.new(10, 4, 0.5)
47
- strategy.call(system, 1).should == 1
47
+ expect(strategy.call(system, 1)).to eq 1
48
48
  end
49
49
 
50
50
  it "scales maximally with a minimum" do
51
51
  system = TestSystem.new(25)
52
52
  strategy = cut.new(5, 4, 0.5)
53
- strategy.call(system, 1).should == 5
53
+ expect(strategy.call(system, 1)).to eq 5
54
54
  end
55
55
 
56
56
  it "scales proportionally with a minimum > 1" do
57
57
  system = TestSystem.new(12)
58
58
  strategy = cut.new(5, 4, 2)
59
- strategy.call(system, 1).should == 2
59
+ expect(strategy.call(system, 1)).to eq 2
60
60
  end
61
61
 
62
62
  it "scales maximally with a minimum factor > 1" do
63
63
  system = TestSystem.new(30)
64
64
  strategy = cut.new(5, 4, 2)
65
- strategy.call(system, 1).should == 5
65
+ expect(strategy.call(system, 1)).to eq 5
66
66
  end
67
67
 
68
68
  it "doesn't scale down engaged workers" do
69
69
  system = TestSystem.new(0, 2)
70
70
  strategy = cut.new(5, 4)
71
- strategy.call(system, 1).should == 2
71
+ expect(strategy.call(system, 1)).to eq 2
72
72
  end
73
73
 
74
74
  it "doesn't scale above max workers even if engaged workers is greater" do
75
75
  system = TestSystem.new(40, 6)
76
76
  strategy = cut.new(5, 4)
77
- strategy.call(system, 1).should == 5
77
+ expect(strategy.call(system, 1)).to eq 5
78
78
  end
79
79
 
80
80
  it "returns zero if requested capacity is zero" do
81
81
  system = TestSystem.new(0, 0)
82
82
  strategy = cut.new(0, 0)
83
- strategy.call(system, 5).should == 0
83
+ expect(strategy.call(system, 5)).to eq 0
84
84
  end
85
85
  end
@@ -16,19 +16,19 @@ describe Autoscaler::Sidekiq::Activity do
16
16
  activity.idle!('queue')
17
17
  other_process.working!('other_queue')
18
18
  end
19
- it {activity.should be_idle(['queue'])}
19
+ it {expect(activity).to be_idle(['queue'])}
20
20
  end
21
21
 
22
22
  it 'passed a connection pool' do
23
23
  activity = cut.new(5, @redis)
24
24
  activity.working!('queue')
25
- activity.should_not be_idle(['queue'])
25
+ expect(activity).to_not be_idle(['queue'])
26
26
  end
27
27
 
28
28
  it 'passed a plain connection' do
29
- connection = Redis.connect(:url => 'http://localhost:9736', :namespace => 'autoscaler')
29
+ connection = Redis.connect(:url => 'redis://localhost:9736', :namespace => 'autoscaler')
30
30
  activity = cut.new(5, connection)
31
31
  activity.working!('queue')
32
- activity.should_not be_idle(['queue'])
32
+ expect(activity).to_not be_idle(['queue'])
33
33
  end
34
34
  end
@@ -10,26 +10,26 @@ describe Autoscaler::Sidekiq::Client do
10
10
  describe 'call' do
11
11
  it 'scales' do
12
12
  client.call(Class, {}, 'queue') {}
13
- scaler.workers.should == 1
13
+ expect(scaler.workers).to eq 1
14
14
  end
15
15
 
16
16
  it 'scales with a redis pool' do
17
17
  client.call(Class, {}, 'queue', ::Sidekiq.method(:redis)) {}
18
- scaler.workers.should == 1
18
+ expect(scaler.workers).to eq 1
19
19
  end
20
20
 
21
- it('yields') {client.call(Class, {}, 'queue') {:foo}.should == :foo}
21
+ it('yields') {expect(client.call(Class, {}, 'queue') {:foo}).to eq :foo}
22
22
  end
23
23
 
24
24
  describe 'initial workers' do
25
25
  it 'works with default arguments' do
26
26
  client.set_initial_workers
27
- scaler.workers.should == 0
27
+ expect(scaler.workers).to eq 0
28
28
  end
29
29
 
30
30
  it 'scales when necessary' do
31
31
  client.set_initial_workers {|q| TestSystem.new(1)}
32
- scaler.workers.should == 1
32
+ expect(scaler.workers).to eq 1
33
33
  end
34
34
  end
35
35
  end
@@ -24,42 +24,42 @@ describe Autoscaler::Sidekiq::EntireQueueSystem do
24
24
 
25
25
  subject {cut.new}
26
26
 
27
- it {subject.queue_names.should == []}
28
- it {subject.workers.should == 0}
27
+ it {expect(subject.queue_names).to eq []}
28
+ it {expect(subject.workers).to eq 0}
29
29
 
30
30
  describe 'no queued work' do
31
31
  it "with no work" do
32
- subject.stub(:sidekiq_queues).and_return({'queue' => 0, 'another_queue' => 0})
33
- subject.queued.should == 0
32
+ allow(subject).to receive(:sidekiq_queues).and_return({'queue' => 0, 'another_queue' => 0})
33
+ expect(subject.queued).to eq 0
34
34
  end
35
35
 
36
36
  it "with no work and no queues" do
37
- subject.queued.should == 0
37
+ expect(subject.queued).to eq 0
38
38
  end
39
39
 
40
40
  it "with no scheduled work" do
41
- subject.scheduled.should == 0
41
+ expect(subject.scheduled).to eq 0
42
42
  end
43
43
 
44
44
  it "with no retry work" do
45
- subject.retrying.should == 0
45
+ expect(subject.retrying).to eq 0
46
46
  end
47
47
  end
48
48
 
49
49
  describe 'with queued work' do
50
50
  it "with enqueued work" do
51
- subject.stub(:sidekiq_queues).and_return({'queue' => 1})
52
- subject.queued.should == 1
51
+ allow(subject).to receive(:sidekiq_queues).and_return({'queue' => 1})
52
+ expect(subject.queued).to eq 1
53
53
  end
54
54
 
55
55
  it "with schedule work" do
56
56
  with_scheduled_work_in('queue')
57
- subject.scheduled.should == 1
57
+ expect(subject.scheduled).to eq 1
58
58
  end
59
59
 
60
60
  it "with retry work" do
61
61
  with_retry_work_in('queue')
62
- subject.retrying.should == 1
62
+ expect(subject.retrying).to eq 1
63
63
  end
64
64
  end
65
65
  end
@@ -12,34 +12,34 @@ describe Autoscaler::Sidekiq::SleepWaitServer do
12
12
  let(:server) {cut.new(scaler, 0, ['queue'])}
13
13
 
14
14
  shared_examples "a sleepwait server" do
15
- it "scales with no work" do
16
- server.stub(:pending_work?).and_return(false)
17
- when_run
18
- scaler.workers.should == 0
19
- end
15
+ it "scales with no work" do
16
+ allow(server).to receive(:pending_work?).and_return(false)
17
+ when_run
18
+ expect(scaler.workers).to eq 0
19
+ end
20
20
 
21
- it "does not scale with pending work" do
22
- server.stub(:pending_work?).and_return(true)
23
- when_run
24
- scaler.workers.should == 1
25
- end
21
+ it "does not scale with pending work" do
22
+ allow(server).to receive(:pending_work?).and_return(true)
23
+ when_run
24
+ expect(scaler.workers).to eq 1
25
+ end
26
26
  end
27
27
 
28
28
  describe "a middleware with no redis specified" do
29
- it_behaves_like "a sleepwait server" do
30
- def when_run
31
- server.call(Object.new, {}, 'queue') {}
32
- end
33
- end
29
+ it_behaves_like "a sleepwait server" do
30
+ def when_run
31
+ server.call(Object.new, {}, 'queue') {}
32
+ end
33
+ end
34
34
  end
35
35
 
36
36
  describe "a middleware with redis specified" do
37
- it_behaves_like "a sleepwait server" do
38
- def when_run
39
- server.call(Object.new, {}, 'queue', Sidekiq.method(:redis)) {}
40
- end
41
- end
37
+ it_behaves_like "a sleepwait server" do
38
+ def when_run
39
+ server.call(Object.new, {}, 'queue', Sidekiq.method(:redis)) {}
40
+ end
41
+ end
42
42
  end
43
43
 
44
- it('yields') {server.call(Object.new, {}, 'queue') {:foo}.should == :foo}
44
+ it('yields') {expect(server.call(Object.new, {}, 'queue') {:foo}).to eq :foo}
45
45
  end