autoscaler 0.14.0 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: ec6a1b37ad89976bcf80edccfd67480c72aee6f0
4
- data.tar.gz: c94e28fe7bcd92502be6dbee2cd9fd45b2ecbcb9
3
+ metadata.gz: 2178b48972a2f8602b145cd95959d98df9df800a
4
+ data.tar.gz: 6fa236be6c17027b4283866739df03f45f62d601
5
5
  SHA512:
6
- metadata.gz: 6885e017918b53921cffb12aff92b59579ea1023d41f4f5bc5828d6f50dda383a92b5ca8c6320905b72b30bd37bced45c5c39b31d20bfc6647b4f6531fca5733
7
- data.tar.gz: 7c3c78d2c162e50a4432e95ea907c39977a5b07fb44433d5d84bfc35dcb9ae75dcddadaff765d67b3b2d3e379046d950aaefd679516e1e04d6bd594655cab8c6
6
+ metadata.gz: 690808fd62d423dcc2f928f44b0d50f705af9208aac0e38919d380326c40d8e2ee845ef92f4b19b4ffb5e0568f3c934bd1ce5158e329316a78b23c4ae11c6e0f
7
+ data.tar.gz: a81d4fd5ab0ae14640cd7814c7d2188a9516515e75c6bf3f8496bf66134f97a6cc7a23d45b684c602321b8300daab1c2735752e21e8feeda08c8d08900811444
@@ -1,5 +1,19 @@
1
1
  # Changelog
2
2
 
3
+ ## 1.0.0
4
+
5
+ Potentially breaking changes
6
+
7
+ - Implemented named arguments for several classes. (Breaking if you instantiated these classes with arguments)
8
+ - `HerokuPlatformScaler`
9
+ - `BinaryScalingStrategy`
10
+ - `LinearScalingStrategy`
11
+ - `DelayedShutdown`
12
+ - As consequence of above, specifies min ruby version 2.1
13
+ - Remove depreciated ENV variables `HEROKU_ACCESS_TOKEN` and `HERKOU_APP`
14
+ - `HerokuScaler` has been removed, as the old API has been disabled by Heroku. (See <https://devcenter.heroku.com/changelog-items/1181>)
15
+ Consider to use `HerokuPlatformScaler`.
16
+
3
17
  ## 0.14.0
4
18
 
5
19
  - Heroku has claimed the `HEROKU_` ENV prefix for Herkou-16 stack. The preferred variables are now `AUTOSCALER_HEROKU_APP` and `AUTOSCALER_HEROKU_ACCESS_TOKEN`. (The complex example has been similarly updated.) The old ENV variables will still be accepted, with a warning, but may be removed in a major release. (HerokuScaler was not updated.)
data/README.md CHANGED
@@ -38,7 +38,7 @@ Install the middleware in your `Sidekiq.configure_` blocks
38
38
 
39
39
  - HerokuPlatformScaler includes an attempt at current-worker cache that may be overcomplication, and doesn't work very well on the server
40
40
  - Multiple scale-down loops may be started, particularly if there are multiple jobs queued when the servers comes up. Heroku seems to handle multiple scale-down commands well.
41
- - The scale-down monitor is triggered on job completion (and server middleware is only run around jobs), so if the server nevers processes any jobs, it won't turn off.
41
+ - The scale-down monitor is triggered on job completion (and server middleware is only run around jobs), so if the server never processes any jobs, it won't turn off.
42
42
  - The retry and schedule lists are considered - if you schedule a long-running task, the process will not scale-down.
43
43
  - If background jobs trigger jobs in other scaled processes, please note you'll need `config.client_middleware` in your `Sidekiq.configure_server` block in order to scale-up.
44
44
  - Exceptions while calling the Heroku API are caught and printed by default. See `HerokuPlatformScaler#exception_handler` to override
@@ -12,9 +12,9 @@ if ENV['AUTOSCALER_HEROKU_APP']
12
12
  # We are using the convention that worker process type is the
13
13
  # same as the queue name
14
14
  heroku[queue] = Autoscaler::HerokuPlatformScaler.new(
15
- queue,
16
- ENV['AUTOSCALER_HEROKU_ACCESS_TOKEN'],
17
- ENV['AUTOSCALER_HEROKU_APP'])
15
+ type: queue,
16
+ token: ENV['AUTOSCALER_HEROKU_ACCESS_TOKEN'],
17
+ app: ENV['AUTOSCALER_HEROKU_APP'])
18
18
  end
19
19
  end
20
20
 
@@ -3,7 +3,7 @@ module Autoscaler
3
3
  # The default strategy has a single worker when there is anything, or shuts it down.
4
4
  class BinaryScalingStrategy
5
5
  #@param [integer] active_workers number of workers when in the active state.
6
- def initialize(active_workers = 1)
6
+ def initialize(active_workers: 1)
7
7
  @active_workers = active_workers
8
8
  end
9
9
 
@@ -3,7 +3,7 @@ module Autoscaler
3
3
  class DelayedShutdown
4
4
  # @param [ScalingStrategy] strategy object that makes most decisions
5
5
  # @param [Numeric] timeout number of seconds to stay up after base strategy says zero
6
- def initialize(strategy, timeout)
6
+ def initialize(strategy:, timeout:)
7
7
  @strategy = strategy
8
8
  @timeout = timeout
9
9
  active_now!
@@ -8,18 +8,9 @@ module Autoscaler
8
8
  # @param [String] token Heroku OAuth access token
9
9
  # @param [String] app Heroku app name
10
10
  def initialize(
11
- type = 'worker',
12
- token = ENV['AUTOSCALER_HEROKU_ACCESS_TOKEN'],
13
- app = ENV['AUTOSCALER_HEROKU_APP'])
14
-
15
- if (token.nil? && ENV['HEROKU_ACCESS_TOKEN'])
16
- warn "Autoscaler: ENV AUTOSCALER_HEROKU_ACCESS_TOKEN is now preferred, HEROKU_ACCESS_TOKEN may be removed in a future release"
17
- token = ENV['HEROKU_ACCESS_TOKEN']
18
- end
19
- if (app.nil? && ENV['HEROKU_APP'])
20
- warn "Autoscaler: ENV AUTOSCALER_HEROKU_APP is now preferred, HEROKU_APP may be removed in a future release"
21
- app = ENV['HEROKU_APP']
22
- end
11
+ type: 'worker',
12
+ token: ENV['AUTOSCALER_HEROKU_ACCESS_TOKEN'],
13
+ app: ENV['AUTOSCALER_HEROKU_APP'])
23
14
 
24
15
  @client = PlatformAPI.connect_oauth(token)
25
16
  @type = type
@@ -5,7 +5,7 @@ module Autoscaler
5
5
  #@param [integer] max_workers maximum number of workers to spin up.
6
6
  #@param [integer] worker_capacity the amount of jobs one worker can handle
7
7
  #@param [float] min_factor minimum work required to scale, as percentage of worker_capacity
8
- def initialize(max_workers = 1, worker_capacity = 25, min_factor = 0)
8
+ def initialize(max_workers: 1, worker_capacity: 25, min_factor: 0)
9
9
  @max_workers = max_workers # max # of workers we can scale to
10
10
  @total_capacity = (@max_workers * worker_capacity).to_f # total capacity of max workers
11
11
  min_capacity = [0, min_factor].max.to_f * worker_capacity # min capacity required to scale first worker
@@ -32,7 +32,7 @@ module Autoscaler
32
32
  if timeout.respond_to?(:call)
33
33
  timeout
34
34
  else
35
- DelayedShutdown.new(BinaryScalingStrategy.new, timeout)
35
+ DelayedShutdown.new(strategy: BinaryScalingStrategy.new, timeout: timeout)
36
36
  end
37
37
  end
38
38
 
@@ -1,5 +1,3 @@
1
- require 'heroku-api'
2
-
3
1
  module Autoscaler
4
2
  # A minimal scaler to use as stub for local testing
5
3
  class StubScaler
@@ -1,4 +1,4 @@
1
1
  module Autoscaler
2
2
  # version number
3
- VERSION = "0.14.0"
3
+ VERSION = "1.0.0"
4
4
  end
@@ -13,7 +13,7 @@ describe Autoscaler::BinaryScalingStrategy do
13
13
 
14
14
  it "does not scale with pending work" do
15
15
  system = TestSystem.new(1)
16
- strategy = cut.new(2)
16
+ strategy = cut.new(active_workers: 2)
17
17
  expect(strategy.call(system, 1)).to eq 2
18
18
  end
19
19
  end
@@ -6,17 +6,17 @@ describe Autoscaler::DelayedShutdown do
6
6
  let(:cut) {Autoscaler::DelayedShutdown}
7
7
 
8
8
  it "returns normal values" do
9
- strategy = cut.new(lambda{|s,t| 2}, 0)
9
+ strategy = cut.new(strategy: lambda{|s,t| 2}, timeout: 0)
10
10
  expect(strategy.call(nil, 1)).to eq 2
11
11
  end
12
12
 
13
13
  it "delays zeros" do
14
- strategy = cut.new(lambda{|s,t| 0}, 60)
14
+ strategy = cut.new(strategy: lambda{|s,t| 0}, timeout: 60)
15
15
  expect(strategy.call(nil, 1)).to eq 1
16
16
  end
17
17
 
18
18
  it "eventually returns zero" do
19
- strategy = cut.new(lambda{|s,t| 0}, 60)
19
+ strategy = cut.new(strategy: lambda{|s,t| 0}, timeout: 60)
20
20
  allow(strategy).to receive(:level_idle_time).and_return(61)
21
21
  expect(strategy.call(nil, 61)).to eq 0
22
22
  end
@@ -7,79 +7,79 @@ describe Autoscaler::LinearScalingStrategy do
7
7
 
8
8
  it "deactivates with no work" do
9
9
  system = TestSystem.new(0)
10
- strategy = cut.new(1)
10
+ strategy = cut.new(max_workers: 1)
11
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
- strategy = cut.new(1)
16
+ strategy = cut.new(max_workers: 1)
17
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
- strategy = cut.new(2, 2)
22
+ strategy = cut.new(max_workers: 2, worker_capacity: 2)
23
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
- strategy = cut.new(2, 2)
28
+ strategy = cut.new(max_workers: 2, worker_capacity: 2)
29
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
- strategy = cut.new(5, 2)
34
+ strategy = cut.new(max_workers: 5, worker_capacity: 2)
35
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
- strategy = cut.new(10, 4, 0.5)
40
+ strategy = cut.new(max_workers: 10, worker_capacity: 4, min_factor: 0.5)
41
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
- strategy = cut.new(10, 4, 0.5)
46
+ strategy = cut.new(max_workers: 10, worker_capacity: 4, min_factor: 0.5)
47
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
- strategy = cut.new(5, 4, 0.5)
52
+ strategy = cut.new(max_workers: 5, worker_capacity: 4, min_factor: 0.5)
53
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
- strategy = cut.new(5, 4, 2)
58
+ strategy = cut.new(max_workers: 5, worker_capacity: 4, min_factor: 2)
59
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
- strategy = cut.new(5, 4, 2)
64
+ strategy = cut.new(max_workers: 5, worker_capacity: 4, min_factor: 2)
65
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
- strategy = cut.new(5, 4)
70
+ strategy = cut.new(max_workers: 5, worker_capacity: 4)
71
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
- strategy = cut.new(5, 4)
76
+ strategy = cut.new(max_workers: 5, worker_capacity: 4)
77
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
- strategy = cut.new(0, 0)
82
+ strategy = cut.new(max_workers: 0, worker_capacity: 0)
83
83
  expect(strategy.call(system, 5)).to eq 0
84
84
  end
85
85
  end
@@ -5,8 +5,7 @@ REDIS = Sidekiq::RedisConnection.create(:url => 'redis://localhost:9736')
5
5
  RSpec.configure do |config|
6
6
  config.mock_with :rspec
7
7
 
8
- config.filter_run_excluding :api1 => true unless ENV['HEROKU_API_KEY']
9
- config.filter_run_excluding :platform_api => true unless ENV['AUTOSCALER_HEROKU_ACCESS_TOKEN'] || ENV['HEROKU_ACCESS_TOKEN']
8
+ config.filter_run_excluding :platform_api => true unless ENV['AUTOSCALER_HEROKU_ACCESS_TOKEN']
10
9
  end
11
10
 
12
11
  class TestScaler
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: autoscaler
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.14.0
4
+ version: 1.0.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: 2017-07-07 00:00:00.000000000 Z
12
+ date: 2018-05-21 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: sidekiq
@@ -25,20 +25,6 @@ dependencies:
25
25
  - - "~>"
26
26
  - !ruby/object:Gem::Version
27
27
  version: '5.0'
28
- - !ruby/object:Gem::Dependency
29
- name: heroku-api
30
- requirement: !ruby/object:Gem::Requirement
31
- requirements:
32
- - - "~>"
33
- - !ruby/object:Gem::Version
34
- version: '0.0'
35
- type: :runtime
36
- prerelease: false
37
- version_requirements: !ruby/object:Gem::Requirement
38
- requirements:
39
- - - "~>"
40
- - !ruby/object:Gem::Version
41
- version: '0.0'
42
28
  - !ruby/object:Gem::Dependency
43
29
  name: platform-api
44
30
  requirement: !ruby/object:Gem::Requirement
@@ -142,7 +128,6 @@ files:
142
128
  - lib/autoscaler/counter_cache_redis.rb
143
129
  - lib/autoscaler/delayed_shutdown.rb
144
130
  - lib/autoscaler/heroku_platform_scaler.rb
145
- - lib/autoscaler/heroku_scaler.rb
146
131
  - lib/autoscaler/ignore_scheduled_and_retrying.rb
147
132
  - lib/autoscaler/linear_scaling_strategy.rb
148
133
  - lib/autoscaler/sidekiq.rb
@@ -160,7 +145,6 @@ files:
160
145
  - spec/autoscaler/counter_cache_redis_spec.rb
161
146
  - spec/autoscaler/delayed_shutdown_spec.rb
162
147
  - spec/autoscaler/heroku_platform_scaler_spec.rb
163
- - spec/autoscaler/heroku_scaler_spec.rb
164
148
  - spec/autoscaler/ignore_scheduled_and_retrying_spec.rb
165
149
  - spec/autoscaler/linear_scaling_strategy_spec.rb
166
150
  - spec/autoscaler/sidekiq/activity_spec.rb
@@ -181,9 +165,9 @@ require_paths:
181
165
  - lib
182
166
  required_ruby_version: !ruby/object:Gem::Requirement
183
167
  requirements:
184
- - - ">="
168
+ - - "~>"
185
169
  - !ruby/object:Gem::Version
186
- version: '0'
170
+ version: '2.1'
187
171
  required_rubygems_version: !ruby/object:Gem::Requirement
188
172
  requirements:
189
173
  - - ">="
@@ -197,20 +181,19 @@ specification_version: 4
197
181
  summary: Start/stop Sidekiq workers on Heroku
198
182
  test_files:
199
183
  - Guardfile
200
- - spec/autoscaler/binary_scaling_strategy_spec.rb
201
- - spec/autoscaler/counter_cache_memory_spec.rb
184
+ - spec/spec_helper.rb
185
+ - spec/test_system.rb
202
186
  - spec/autoscaler/counter_cache_redis_spec.rb
203
- - spec/autoscaler/delayed_shutdown_spec.rb
204
- - spec/autoscaler/heroku_platform_scaler_spec.rb
205
- - spec/autoscaler/heroku_scaler_spec.rb
206
187
  - spec/autoscaler/ignore_scheduled_and_retrying_spec.rb
207
188
  - spec/autoscaler/linear_scaling_strategy_spec.rb
208
- - spec/autoscaler/sidekiq/activity_spec.rb
209
189
  - spec/autoscaler/sidekiq/client_spec.rb
210
- - spec/autoscaler/sidekiq/entire_queue_system_spec.rb
211
190
  - spec/autoscaler/sidekiq/sleep_wait_server_spec.rb
212
- - spec/autoscaler/sidekiq/specified_queue_system_spec.rb
213
191
  - spec/autoscaler/sidekiq/thread_server_spec.rb
214
- - spec/spec_helper.rb
215
- - spec/test_system.rb
192
+ - spec/autoscaler/sidekiq/specified_queue_system_spec.rb
193
+ - spec/autoscaler/sidekiq/activity_spec.rb
194
+ - spec/autoscaler/sidekiq/entire_queue_system_spec.rb
195
+ - spec/autoscaler/counter_cache_memory_spec.rb
196
+ - spec/autoscaler/binary_scaling_strategy_spec.rb
197
+ - spec/autoscaler/delayed_shutdown_spec.rb
198
+ - spec/autoscaler/heroku_platform_scaler_spec.rb
216
199
  has_rdoc:
@@ -1,85 +0,0 @@
1
- require 'heroku-api'
2
- require 'autoscaler/counter_cache_memory'
3
-
4
- module Autoscaler
5
- # Wraps the Heroku API to provide just the interface that we need for scaling.
6
- # OBSOLETE: The v1 Heroku API is no longer available, and HerokuScaler will likely be removed in a future major release. Please see current setup instructions and examples for HerokuPlatformScaler.
7
- class HerokuScaler
8
- # @param [String] type process type this scaler controls
9
- # @param [String] key Heroku API key
10
- # @param [String] app Heroku app name
11
- def initialize(
12
- type = 'worker',
13
- key = ENV['HEROKU_API_KEY'],
14
- app = ENV['HEROKU_APP'])
15
-
16
- warn "Autoscaler: The v1 Heroku API is no longer available, and HerokuScaler will likely be removed in a future major release. Please see current setup instructions and examples for HerokuPlatformScaler."
17
-
18
- @client = Heroku::API.new(:api_key => key)
19
- @type = type
20
- @app = app
21
- @workers = CounterCacheMemory.new
22
- end
23
-
24
- attr_reader :app
25
- attr_reader :type
26
-
27
- # Read the current worker count (value may be cached)
28
- # @return [Numeric] number of workers
29
- def workers
30
- @workers.counter {@workers.counter = heroku_get_workers}
31
- end
32
-
33
- # Set the number of workers (noop if workers the same)
34
- # @param [Numeric] n number of workers
35
- def workers=(n)
36
- unknown = false
37
- current = @workers.counter{unknown = true; 1}
38
- if n != current || unknown
39
- p "Scaling #{type} to #{n}"
40
- heroku_set_workers(n)
41
- @workers.counter = n
42
- end
43
- end
44
-
45
- # Callable object which responds to exceptions during api calls #
46
- # @example
47
- # heroku.exception_handler = lambda {|exception| MyApp.logger.error(exception)}
48
- # heroku.exception_handler = lambda {|exception| raise}
49
- # # default
50
- # lambda {|exception|
51
- # p exception
52
- # puts exception.backtrace
53
- # }
54
- attr_writer :exception_handler
55
-
56
- # Object which supports #counter and #counter=
57
- # Defaults to CounterCacheMemory
58
- def counter_cache=(cache)
59
- @workers = cache
60
- end
61
-
62
- private
63
- attr_reader :client
64
-
65
- def heroku_get_workers
66
- client.get_ps(app).body.count {|ps| ps['process'].match /#{type}\.\d?/ }
67
- rescue Excon::Errors::Error, Heroku::API::Errors::Error => e
68
- exception_handler.call(e)
69
- 0
70
- end
71
-
72
- def heroku_set_workers(n)
73
- client.post_ps_scale(app, type, n)
74
- rescue Excon::Errors::Error, Heroku::API::Errors::Error => e
75
- exception_handler.call(e)
76
- end
77
-
78
- def exception_handler
79
- @exception_handler ||= lambda {|exception|
80
- p exception
81
- puts exception.backtrace
82
- }
83
- end
84
- end
85
- end
@@ -1,49 +0,0 @@
1
- require 'spec_helper'
2
- require 'autoscaler/heroku_scaler'
3
- require 'heroku/api/errors'
4
-
5
- describe Autoscaler::HerokuScaler, :api1 => true do
6
- let(:cut) {Autoscaler::HerokuScaler}
7
- let(:client) {cut.new}
8
- subject {client}
9
-
10
- its(:workers) {should eq(0)}
11
-
12
- describe 'scaled' do
13
- around do |example|
14
- client.workers = 1
15
- example.call
16
- client.workers = 0
17
- end
18
-
19
- its(:workers) {should eq(1)}
20
- end
21
-
22
- shared_examples 'exception handler' do |exception_class|
23
- before do
24
- expect(client).to receive(:client){
25
- raise exception_class.new(Exception.new('oops'))
26
- }
27
- end
28
-
29
- describe "default handler" do
30
- it {expect{client.workers}.to_not raise_error}
31
- it {expect(client.workers).to eq(0)}
32
- it {expect{client.workers = 2}.to_not raise_error}
33
- end
34
-
35
- describe "custom handler" do
36
- before do
37
- @caught = false
38
- client.exception_handler = lambda {|exception| @caught = true}
39
- end
40
-
41
- it {client.workers; expect(@caught).to be(true)}
42
- end
43
- end
44
-
45
- describe 'exception handling', :focus => true do
46
- it_behaves_like 'exception handler', Excon::Errors::SocketError
47
- it_behaves_like 'exception handler', Heroku::API::Errors::Error
48
- end
49
- end