autoscaler 0.14.0 → 1.0.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.
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