autoscale 0.9.3 → 0.11.0

Sign up to get free protection for your applications and to get access to all the features.
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
@@ -24,40 +24,40 @@ describe Autoscaler::Sidekiq::SpecifiedQueueSystem do
24
24
 
25
25
  subject {cut.new(['queue'])}
26
26
 
27
- it {subject.queue_names.should == ['queue']}
28
- it {subject.workers.should == 0}
27
+ it {expect(subject.queue_names).to eq ['queue']}
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' => 1})
33
- subject.queued.should == 0
32
+ allow(subject).to receive(:sidekiq_queues).and_return({'queue' => 0, 'another_queue' => 1})
33
+ expect(subject.queued).to eq 0
34
34
  end
35
35
 
36
36
  it "with scheduled work in another queue" do
37
37
  with_scheduled_work_in('another_queue')
38
- subject.scheduled.should == 0
38
+ expect(subject.scheduled).to eq 0
39
39
  end
40
40
 
41
41
  it "with retry work in another queue" do
42
42
  with_retry_work_in('another_queue')
43
- subject.retrying.should == 0
43
+ expect(subject.retrying).to eq 0
44
44
  end
45
45
  end
46
46
 
47
47
  describe 'with queued work' do
48
48
  it "with enqueued work" do
49
- subject.stub(:sidekiq_queues).and_return({'queue' => 1})
50
- subject.queued.should == 1
49
+ allow(subject).to receive(:sidekiq_queues).and_return({'queue' => 1})
50
+ expect(subject.queued).to eq 1
51
51
  end
52
52
 
53
53
  it "with schedule work" do
54
54
  with_scheduled_work_in('queue')
55
- subject.scheduled.should == 1
55
+ expect(subject.scheduled).to eq 1
56
56
  end
57
57
 
58
58
  it "with retry work" do
59
59
  with_retry_work_in('queue')
60
- subject.retrying.should == 1
60
+ expect(subject.retrying).to eq 1
61
61
  end
62
62
  end
63
63
  end
@@ -0,0 +1,44 @@
1
+ require 'spec_helper'
2
+ require 'autoscaler/sidekiq/thread_server'
3
+ require 'timeout'
4
+
5
+ describe Autoscaler::Sidekiq::ThreadServer do
6
+ before do
7
+ @redis = Sidekiq.redis = REDIS
8
+ Sidekiq.redis {|c| c.flushdb }
9
+ end
10
+
11
+ let(:cut) {Autoscaler::Sidekiq::ThreadServer}
12
+ let(:scaler) {TestScaler.new(1)}
13
+
14
+ describe "thread core" do
15
+ it "scales with no work" do
16
+ server = cut.new(scaler, lambda{|s,t| 0})
17
+ Timeout.timeout(1) { server.run(0.5) }
18
+ expect(scaler.workers).to eq 0
19
+ server.terminate
20
+ end
21
+
22
+ it "does not scale with pending work" do
23
+ server = cut.new(scaler, lambda{|s,t| 1})
24
+ expect {Timeout.timeout(1) { server.run(0.5) }}.to raise_error Timeout::Error
25
+ expect(scaler.workers).to eq 1
26
+ server.terminate
27
+ end
28
+
29
+ it "will downscale with initial workers zero" do
30
+ scaler = TestScaler.new(0)
31
+ server = cut.new(scaler, lambda{|s,t| 0})
32
+ Timeout.timeout(1) { server.run(0.5) }
33
+ expect(scaler.workers).to eq 0
34
+ server.terminate
35
+ end
36
+ end
37
+
38
+ describe "Middleware interface" do
39
+ let(:server) {cut.new(scaler, 0, ['queue'])}
40
+
41
+ it('yields') {expect(server.call(Object.new, {}, 'queue') {:foo}).to eq :foo}
42
+ it('yields with a redis pool') {expect(server.call(Object.new, {}, 'queue', Sidekiq.method(:redis)) {:foo}).to eq :foo}
43
+ end
44
+ end
@@ -1,10 +1,12 @@
1
+ require 'rspec/its'
1
2
  require 'sidekiq'
2
- REDIS = Sidekiq::RedisConnection.create(:url => 'http://localhost:9736', :namespace => 'autoscaler')
3
+ REDIS = Sidekiq::RedisConnection.create(:url => 'redis://localhost:9736', :namespace => 'autoscaler')
3
4
 
4
5
  RSpec.configure do |config|
5
6
  config.mock_with :rspec
6
7
 
7
- config.filter_run_excluding :online => true unless ENV['HEROKU_APP']
8
+ config.filter_run_excluding :api1 => true unless ENV['HEROKU_API_KEY']
9
+ config.filter_run_excluding :platform_api => true unless ENV['HEROKU_ACCESS_TOKEN']
8
10
  end
9
11
 
10
12
  class TestScaler
@@ -8,4 +8,10 @@ class TestSystem
8
8
  def queued; @pending; end
9
9
  def scheduled; 0; end
10
10
  def retrying; 0; end
11
+ def total_work
12
+ queued + scheduled + retrying + workers
13
+ end
14
+ def any_work?
15
+ queued > 0 || scheduled > 0 || retrying > 0 || workers > 0
16
+ end
11
17
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: autoscale
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.9.3
4
+ version: 0.11.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Justin Love
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2015-10-13 00:00:00.000000000 Z
12
+ date: 2015-11-17 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: sidekiq
@@ -17,14 +17,14 @@ dependencies:
17
17
  requirements:
18
18
  - - "~>"
19
19
  - !ruby/object:Gem::Version
20
- version: '3.5'
20
+ version: '4.0'
21
21
  type: :runtime
22
22
  prerelease: false
23
23
  version_requirements: !ruby/object:Gem::Requirement
24
24
  requirements:
25
25
  - - "~>"
26
26
  - !ruby/object:Gem::Version
27
- version: '3.5'
27
+ version: '4.0'
28
28
  - !ruby/object:Gem::Dependency
29
29
  name: heroku-api
30
30
  requirement: !ruby/object:Gem::Requirement
@@ -39,6 +39,20 @@ dependencies:
39
39
  - - ">="
40
40
  - !ruby/object:Gem::Version
41
41
  version: '0'
42
+ - !ruby/object:Gem::Dependency
43
+ name: platform-api
44
+ requirement: !ruby/object:Gem::Requirement
45
+ requirements:
46
+ - - ">="
47
+ - !ruby/object:Gem::Version
48
+ version: '0'
49
+ type: :runtime
50
+ prerelease: false
51
+ version_requirements: !ruby/object:Gem::Requirement
52
+ requirements:
53
+ - - ">="
54
+ - !ruby/object:Gem::Version
55
+ version: '0'
42
56
  - !ruby/object:Gem::Dependency
43
57
  name: bundler
44
58
  requirement: !ruby/object:Gem::Requirement
@@ -57,16 +71,58 @@ dependencies:
57
71
  name: rspec
58
72
  requirement: !ruby/object:Gem::Requirement
59
73
  requirements:
60
- - - "<"
74
+ - - ">="
75
+ - !ruby/object:Gem::Version
76
+ version: '0'
77
+ type: :development
78
+ prerelease: false
79
+ version_requirements: !ruby/object:Gem::Requirement
80
+ requirements:
81
+ - - ">="
82
+ - !ruby/object:Gem::Version
83
+ version: '0'
84
+ - !ruby/object:Gem::Dependency
85
+ name: rspec-its
86
+ requirement: !ruby/object:Gem::Requirement
87
+ requirements:
88
+ - - ">="
89
+ - !ruby/object:Gem::Version
90
+ version: '0'
91
+ type: :development
92
+ prerelease: false
93
+ version_requirements: !ruby/object:Gem::Requirement
94
+ requirements:
95
+ - - ">="
96
+ - !ruby/object:Gem::Version
97
+ version: '0'
98
+ - !ruby/object:Gem::Dependency
99
+ name: guard-rspec
100
+ requirement: !ruby/object:Gem::Requirement
101
+ requirements:
102
+ - - ">="
103
+ - !ruby/object:Gem::Version
104
+ version: '0'
105
+ type: :development
106
+ prerelease: false
107
+ version_requirements: !ruby/object:Gem::Requirement
108
+ requirements:
109
+ - - ">="
110
+ - !ruby/object:Gem::Version
111
+ version: '0'
112
+ - !ruby/object:Gem::Dependency
113
+ name: guard-process
114
+ requirement: !ruby/object:Gem::Requirement
115
+ requirements:
116
+ - - ">="
61
117
  - !ruby/object:Gem::Version
62
- version: '3'
118
+ version: '0'
63
119
  type: :development
64
120
  prerelease: false
65
121
  version_requirements: !ruby/object:Gem::Requirement
66
122
  requirements:
67
- - - "<"
123
+ - - ">="
68
124
  - !ruby/object:Gem::Version
69
- version: '3'
125
+ version: '0'
70
126
  description: Currently provides a Sidekiq middleware that does 0/1 scaling of Heroku
71
127
  processes
72
128
  email:
@@ -85,37 +141,37 @@ files:
85
141
  - lib/autoscaler/counter_cache_memory.rb
86
142
  - lib/autoscaler/counter_cache_redis.rb
87
143
  - lib/autoscaler/delayed_shutdown.rb
144
+ - lib/autoscaler/heroku_platform_scaler.rb
88
145
  - lib/autoscaler/heroku_scaler.rb
89
146
  - lib/autoscaler/ignore_scheduled_and_retrying.rb
90
147
  - lib/autoscaler/linear_scaling_strategy.rb
91
148
  - lib/autoscaler/sidekiq.rb
92
149
  - lib/autoscaler/sidekiq/activity.rb
93
- - lib/autoscaler/sidekiq/celluloid_monitor.rb
94
150
  - lib/autoscaler/sidekiq/client.rb
95
151
  - lib/autoscaler/sidekiq/entire_queue_system.rb
96
- - lib/autoscaler/sidekiq/monitor_middleware_adapter.rb
97
152
  - lib/autoscaler/sidekiq/queue_system.rb
98
153
  - lib/autoscaler/sidekiq/sleep_wait_server.rb
99
154
  - lib/autoscaler/sidekiq/specified_queue_system.rb
155
+ - lib/autoscaler/sidekiq/thread_server.rb
100
156
  - lib/autoscaler/stub_scaler.rb
101
157
  - lib/autoscaler/version.rb
102
158
  - spec/autoscaler/binary_scaling_strategy_spec.rb
103
159
  - spec/autoscaler/counter_cache_memory_spec.rb
104
160
  - spec/autoscaler/counter_cache_redis_spec.rb
105
161
  - spec/autoscaler/delayed_shutdown_spec.rb
162
+ - spec/autoscaler/heroku_platform_scaler_spec.rb
106
163
  - spec/autoscaler/heroku_scaler_spec.rb
107
164
  - spec/autoscaler/ignore_scheduled_and_retrying_spec.rb
108
165
  - spec/autoscaler/linear_scaling_strategy_spec.rb
109
166
  - spec/autoscaler/sidekiq/activity_spec.rb
110
- - spec/autoscaler/sidekiq/celluloid_monitor_spec.rb
111
167
  - spec/autoscaler/sidekiq/client_spec.rb
112
168
  - spec/autoscaler/sidekiq/entire_queue_system_spec.rb
113
- - spec/autoscaler/sidekiq/monitor_middleware_adapter_spec.rb
114
169
  - spec/autoscaler/sidekiq/sleep_wait_server_spec.rb
115
170
  - spec/autoscaler/sidekiq/specified_queue_system_spec.rb
171
+ - spec/autoscaler/sidekiq/thread_server_spec.rb
116
172
  - spec/spec_helper.rb
117
173
  - spec/test_system.rb
118
- homepage: ''
174
+ homepage: https://github.com/JustinLove/autoscaler
119
175
  licenses: []
120
176
  metadata: {}
121
177
  post_install_message:
@@ -144,16 +200,16 @@ test_files:
144
200
  - spec/autoscaler/counter_cache_memory_spec.rb
145
201
  - spec/autoscaler/counter_cache_redis_spec.rb
146
202
  - spec/autoscaler/delayed_shutdown_spec.rb
203
+ - spec/autoscaler/heroku_platform_scaler_spec.rb
147
204
  - spec/autoscaler/heroku_scaler_spec.rb
148
205
  - spec/autoscaler/ignore_scheduled_and_retrying_spec.rb
149
206
  - spec/autoscaler/linear_scaling_strategy_spec.rb
150
207
  - spec/autoscaler/sidekiq/activity_spec.rb
151
- - spec/autoscaler/sidekiq/celluloid_monitor_spec.rb
152
208
  - spec/autoscaler/sidekiq/client_spec.rb
153
209
  - spec/autoscaler/sidekiq/entire_queue_system_spec.rb
154
- - spec/autoscaler/sidekiq/monitor_middleware_adapter_spec.rb
155
210
  - spec/autoscaler/sidekiq/sleep_wait_server_spec.rb
156
211
  - spec/autoscaler/sidekiq/specified_queue_system_spec.rb
212
+ - spec/autoscaler/sidekiq/thread_server_spec.rb
157
213
  - spec/spec_helper.rb
158
214
  - spec/test_system.rb
159
215
  has_rdoc:
@@ -1,68 +0,0 @@
1
- require 'celluloid'
2
- require 'celluloid/supervision/deprecate'
3
-
4
- module Autoscaler
5
- module Sidekiq
6
- # Actor to monitor the sidekiq server for scale-down
7
- class CelluloidMonitor
8
- include Celluloid
9
-
10
- # @param [scaler] scaler object that actually performs scaling operations (e.g. {HerokuScaler})
11
- # @param [Strategy] strategy object that decides the target number of workers (e.g. {BinaryScalingStrategy})
12
- # @param [System] system interface to the queuing system for use by the strategy
13
- def initialize(scaler, strategy, system)
14
- @scaler = scaler
15
- @strategy = strategy
16
- @system = system
17
- @running = false
18
- end
19
-
20
- # Periodically update the desired number of workers
21
- # @param [Numeric] interval polling interval, mostly for testing
22
- def wait_for_downscale(interval = 15)
23
- once do
24
- active_now!
25
-
26
- workers = :unknown
27
-
28
- begin
29
- sleep(interval)
30
- target_workers = @strategy.call(@system, idle_time)
31
- workers = @scaler.workers = target_workers unless workers == target_workers
32
- end while workers > 0
33
- end
34
- end
35
-
36
- # Notify the monitor that a job is starting
37
- def starting_job
38
- end
39
-
40
- # Notify the monitor that a job has finished
41
- def finished_job
42
- active_now!
43
- async.wait_for_downscale
44
- end
45
-
46
- private
47
-
48
- def active_now!
49
- @activity = Time.now
50
- end
51
-
52
- def idle_time
53
- Time.now - @activity
54
- end
55
-
56
- def once
57
- return if @running
58
-
59
- begin
60
- @running = true
61
- yield
62
- ensure
63
- @running = false
64
- end
65
- end
66
- end
67
- end
68
- end
@@ -1,46 +0,0 @@
1
- require 'autoscaler/sidekiq/queue_system'
2
- require 'autoscaler/sidekiq/celluloid_monitor'
3
- require 'autoscaler/binary_scaling_strategy'
4
- require 'autoscaler/delayed_shutdown'
5
-
6
- module Autoscaler
7
- module Sidekiq
8
- # Shim to the existing autoscaler interface
9
- # Starts the monitor and notifies it of job events that may occur while it's sleeping
10
- class MonitorMiddlewareAdapter
11
- # @param [scaler] scaler object that actually performs scaling operations (e.g. {HerokuScaler})
12
- # @param [Strategy,Numeric] timeout strategy object that determines target workers, or a timeout in seconds to be passed to {DelayedShutdown}+{BinaryScalingStrategy}
13
- # @param [Array[String]] specified_queues list of queues to monitor to determine if there is work left. Defaults to all sidekiq queues.
14
- def initialize(scaler, timeout, specified_queues = nil)
15
- unless monitor
16
- CelluloidMonitor.supervise_as(:autoscaler_monitor,
17
- scaler,
18
- strategy(timeout),
19
- QueueSystem.new(specified_queues))
20
- end
21
- end
22
-
23
- # Sidekiq middleware api entry point
24
- def call(worker, msg, queue, _ = nil)
25
- monitor.async.starting_job
26
- yield
27
- ensure
28
- # monitor might have gone, e.g. if Sidekiq has received SIGTERM
29
- monitor.async.finished_job if monitor
30
- end
31
-
32
- private
33
- def monitor
34
- Celluloid::Actor[:autoscaler_monitor]
35
- end
36
-
37
- def strategy(timeout)
38
- if timeout.respond_to?(:call)
39
- timeout
40
- else
41
- DelayedShutdown.new(BinaryScalingStrategy.new, timeout)
42
- end
43
- end
44
- end
45
- end
46
- end
@@ -1,39 +0,0 @@
1
- require 'spec_helper'
2
- require 'test_system'
3
- require 'autoscaler/sidekiq/celluloid_monitor'
4
- require 'timeout'
5
-
6
- describe Autoscaler::Sidekiq::CelluloidMonitor do
7
- before do
8
- @redis = Sidekiq.redis = REDIS
9
- Sidekiq.redis {|c| c.flushdb }
10
- end
11
-
12
- let(:cut) {Autoscaler::Sidekiq::CelluloidMonitor}
13
- let(:scaler) {TestScaler.new(1)}
14
-
15
- it "scales with no work" do
16
- system = TestSystem.new(0)
17
- manager = cut.new(scaler, lambda{|s,t| 0}, system)
18
- Timeout.timeout(1) { manager.wait_for_downscale(0.5) }
19
- scaler.workers.should == 0
20
- manager.terminate
21
- end
22
-
23
- it "does not scale with pending work" do
24
- system = TestSystem.new(1)
25
- manager = cut.new(scaler, lambda{|s,t| 1}, system)
26
- expect {Timeout.timeout(1) { manager.wait_for_downscale(0.5) }}.to raise_error Timeout::Error
27
- scaler.workers.should == 1
28
- manager.terminate
29
- end
30
-
31
- it "will downscale with initial workers zero" do
32
- system = TestSystem.new(0)
33
- scaler = TestScaler.new(0)
34
- manager = cut.new(scaler, lambda{|s,t| 0}, system)
35
- Timeout.timeout(1) { manager.wait_for_downscale(0.5) }
36
- scaler.workers.should == 0
37
- manager.terminate
38
- end
39
- end