autoscaler 0.10.0 → 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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 3d1fdf02eef4bd9e9ed8ca36ec7a88b0e298ead8
4
- data.tar.gz: fd639d4797b01dc4aded9d0d11dc0d386d24f39e
3
+ metadata.gz: c7c64a64f5b36a7615b3fc8587d78ef94f608649
4
+ data.tar.gz: bd6f84ada4beb4cd9a6d933809da089aeeee6ae7
5
5
  SHA512:
6
- metadata.gz: 43c6d465b6bbbb1978d9ace6e5adc60db0a4c4aac72b206706f10397121dafc9ad2c59edd1a02395a10df8b01927abf445977be997d60bf0d19e172ed24aa85b
7
- data.tar.gz: 27fdfa89b504de1b90a735929e96d9c96e7fd0f84e2b8d80ea05a2ec58d786af71f726109649dfa220b7d9a5cd038f34dc591d28a9218587c073a2a6b8fb9961
6
+ metadata.gz: e5ae3abf03d4ce9dc2c1bf7d7510f4d4c1b132e5f180dac7882630bd9a0bcf36ba43baf43338896b980909215ee0cadd66687ea438561aa880f9ec84f702cedb
7
+ data.tar.gz: 6f26a32336472672ee927d55578c4ce1da108163d58ed4c7494b92ea5c3e2c027d6013d42dab4e05d151b1a7772f1d82e717e3f7ce80ad6794204842219c13bf
@@ -1,5 +1,10 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.11.0
4
+
5
+ - Replace celluloid monitor with thread based middleware for Sidekiq 4.0
6
+ - Tests no longer use use redis namespace
7
+
3
8
  ## 0.10.0
4
9
 
5
10
  - Require Sidekiq 3.5
data/README.md CHANGED
@@ -12,7 +12,7 @@ Tested on Ruby 2.1.7 and Heroku Cedar stack.
12
12
 
13
13
  ## Getting Started
14
14
 
15
- This gem uses the [Heroku Platform-Api](https://github.com/heroku/platform-api.rb) gem, which requires an OAuth token from Heroku. It will also need the heroku app name. By default, these are specified through environment variables. You can also pass them to `HerokuPlatformScaler` explicitly.
15
+ This gem uses the [Heroku Platform-Api](https://github.com/heroku/platform-api) gem, which requires an OAuth token from Heroku. It will also need the heroku app name. By default, these are specified through environment variables. You can also pass them to `HerokuPlatformScaler` explicitly.
16
16
 
17
17
  HEROKU_ACCESS_TOKEN=.....
18
18
  HEROKU_APP=....
@@ -77,6 +77,7 @@ Justin Love, [@wondible](http://twitter.com/wondible), [https://github.com/Justi
77
77
  ### Contributors
78
78
 
79
79
  - Benjamin Kudria [https://github.com/bkudria](https://github.com/bkudria)
80
+ - claudiofullscreen [https://github.com/claudiofullscreen](https://github.com/claudiofullscreen)
80
81
  - Fix Peña [https://github.com/fixr](https://github.com/fixr)
81
82
  - Gabriel Givigier Guimarães [https://github.com/givigier](https://github.com/givigier)
82
83
  - Matt Anderson [https://github.com/tonkapark](https://github.com/tonkapark)
@@ -1,11 +1,11 @@
1
1
  require 'autoscaler/sidekiq/client'
2
- require 'autoscaler/sidekiq/monitor_middleware_adapter'
2
+ require 'autoscaler/sidekiq/thread_server'
3
3
 
4
4
  module Autoscaler
5
5
  # namespace module for Sidekiq middlewares
6
6
  module Sidekiq
7
7
  # Sidekiq server middleware
8
8
  # Performs scale-down when the queue is empty
9
- Server = MonitorMiddlewareAdapter
9
+ Server = ThreadServer
10
10
  end
11
11
  end
@@ -0,0 +1,90 @@
1
+ require 'autoscaler/sidekiq/queue_system'
2
+ require 'autoscaler/binary_scaling_strategy'
3
+ require 'autoscaler/delayed_shutdown'
4
+ require 'thread'
5
+
6
+ module Autoscaler
7
+ module Sidekiq
8
+ # Sidekiq server middleware
9
+ # spawns a thread to monitor the sidekiq server for scale-down
10
+ class ThreadServer
11
+ # @param [scaler] scaler object that actually performs scaling operations (e.g. {HerokuPlatformScaler})
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
+ @scaler = scaler
16
+ @strategy = strategy(timeout)
17
+ @system = QueueSystem.new(specified_queues)
18
+ @mutex = Mutex.new
19
+ @done = false
20
+ end
21
+
22
+ # Sidekiq middleware api entry point
23
+ def call(worker, msg, queue, _ = nil)
24
+ yield
25
+ ensure
26
+ active_now!
27
+ wait_for_downscale
28
+ end
29
+
30
+ # Start the monitoring thread if it's not running
31
+ def wait_for_downscale
32
+ @thread ||= Thread.new do
33
+ begin
34
+ run
35
+ rescue
36
+ @thread = nil
37
+ end
38
+ end
39
+ end
40
+
41
+ # Thread core loop
42
+ # Periodically update the desired number of workers
43
+ # @param [Numeric] interval polling interval, mostly for testing
44
+ def run(interval = 15)
45
+ active_now!
46
+
47
+ workers = :unknown
48
+
49
+ begin
50
+ sleep(interval)
51
+ target_workers = @strategy.call(@system, idle_time)
52
+ workers = @scaler.workers = target_workers unless workers == target_workers
53
+ end while !@done && workers > 0
54
+ ::Sidekiq::ProcessSet.new.each(&:quiet!)
55
+ end
56
+
57
+ # Shut down the thread, pause until complete
58
+ def terminate
59
+ @done = true
60
+ if @thread
61
+ t = @thread
62
+ @thread = nil
63
+ t.value
64
+ end
65
+ end
66
+
67
+ private
68
+
69
+ def active_now!
70
+ @mutex.synchronize do
71
+ @activity = Time.now
72
+ end
73
+ end
74
+
75
+ def idle_time
76
+ @mutex.synchronize do
77
+ Time.now - @activity
78
+ end
79
+ end
80
+
81
+ def strategy(timeout)
82
+ if timeout.respond_to?(:call)
83
+ timeout
84
+ else
85
+ DelayedShutdown.new(BinaryScalingStrategy.new, timeout)
86
+ end
87
+ end
88
+ end
89
+ end
90
+ end
@@ -1,4 +1,4 @@
1
1
  module Autoscaler
2
2
  # version number
3
- VERSION = "0.10.0"
3
+ VERSION = "0.11.0"
4
4
  end
@@ -41,7 +41,7 @@ describe Autoscaler::CounterCacheRedis do
41
41
  end
42
42
 
43
43
  it 'passed a plain connection' do
44
- connection = Redis.connect(:url => 'redis://localhost:9736', :namespace => 'autoscaler')
44
+ connection = Redis.connect(:url => 'redis://localhost:9736')
45
45
  cache = cut.new connection
46
46
  cache.counter = 5
47
47
  expect(cache.counter).to eq 5
@@ -26,7 +26,7 @@ describe Autoscaler::Sidekiq::Activity do
26
26
  end
27
27
 
28
28
  it 'passed a plain connection' do
29
- connection = Redis.connect(:url => 'redis://localhost:9736', :namespace => 'autoscaler')
29
+ connection = Redis.connect(:url => 'redis://localhost:9736')
30
30
  activity = cut.new(5, connection)
31
31
  activity.working!('queue')
32
32
  expect(activity).to_not be_idle(['queue'])
@@ -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,6 +1,6 @@
1
1
  require 'rspec/its'
2
2
  require 'sidekiq'
3
- REDIS = Sidekiq::RedisConnection.create(:url => 'redis://localhost:9736', :namespace => 'autoscaler')
3
+ REDIS = Sidekiq::RedisConnection.create(:url => 'redis://localhost:9736')
4
4
 
5
5
  RSpec.configure do |config|
6
6
  config.mock_with :rspec
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.10.0
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-27 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,28 +17,14 @@ dependencies:
17
17
  requirements:
18
18
  - - "~>"
19
19
  - !ruby/object:Gem::Version
20
- version: 3.5.1
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.1
28
- - !ruby/object:Gem::Dependency
29
- name: celluloid
30
- requirement: !ruby/object:Gem::Requirement
31
- requirements:
32
- - - "~>"
33
- - !ruby/object:Gem::Version
34
- version: 0.17.2
35
- type: :runtime
36
- prerelease: false
37
- version_requirements: !ruby/object:Gem::Requirement
38
- requirements:
39
- - - "~>"
40
- - !ruby/object:Gem::Version
41
- version: 0.17.2
27
+ version: '4.0'
42
28
  - !ruby/object:Gem::Dependency
43
29
  name: heroku-api
44
30
  requirement: !ruby/object:Gem::Requirement
@@ -161,13 +147,12 @@ files:
161
147
  - lib/autoscaler/linear_scaling_strategy.rb
162
148
  - lib/autoscaler/sidekiq.rb
163
149
  - lib/autoscaler/sidekiq/activity.rb
164
- - lib/autoscaler/sidekiq/celluloid_monitor.rb
165
150
  - lib/autoscaler/sidekiq/client.rb
166
151
  - lib/autoscaler/sidekiq/entire_queue_system.rb
167
- - lib/autoscaler/sidekiq/monitor_middleware_adapter.rb
168
152
  - lib/autoscaler/sidekiq/queue_system.rb
169
153
  - lib/autoscaler/sidekiq/sleep_wait_server.rb
170
154
  - lib/autoscaler/sidekiq/specified_queue_system.rb
155
+ - lib/autoscaler/sidekiq/thread_server.rb
171
156
  - lib/autoscaler/stub_scaler.rb
172
157
  - lib/autoscaler/version.rb
173
158
  - spec/autoscaler/binary_scaling_strategy_spec.rb
@@ -179,12 +164,11 @@ files:
179
164
  - spec/autoscaler/ignore_scheduled_and_retrying_spec.rb
180
165
  - spec/autoscaler/linear_scaling_strategy_spec.rb
181
166
  - spec/autoscaler/sidekiq/activity_spec.rb
182
- - spec/autoscaler/sidekiq/celluloid_monitor_spec.rb
183
167
  - spec/autoscaler/sidekiq/client_spec.rb
184
168
  - spec/autoscaler/sidekiq/entire_queue_system_spec.rb
185
- - spec/autoscaler/sidekiq/monitor_middleware_adapter_spec.rb
186
169
  - spec/autoscaler/sidekiq/sleep_wait_server_spec.rb
187
170
  - spec/autoscaler/sidekiq/specified_queue_system_spec.rb
171
+ - spec/autoscaler/sidekiq/thread_server_spec.rb
188
172
  - spec/spec_helper.rb
189
173
  - spec/test_system.rb
190
174
  homepage: https://github.com/JustinLove/autoscaler
@@ -221,12 +205,11 @@ test_files:
221
205
  - spec/autoscaler/ignore_scheduled_and_retrying_spec.rb
222
206
  - spec/autoscaler/linear_scaling_strategy_spec.rb
223
207
  - spec/autoscaler/sidekiq/activity_spec.rb
224
- - spec/autoscaler/sidekiq/celluloid_monitor_spec.rb
225
208
  - spec/autoscaler/sidekiq/client_spec.rb
226
209
  - spec/autoscaler/sidekiq/entire_queue_system_spec.rb
227
- - spec/autoscaler/sidekiq/monitor_middleware_adapter_spec.rb
228
210
  - spec/autoscaler/sidekiq/sleep_wait_server_spec.rb
229
211
  - spec/autoscaler/sidekiq/specified_queue_system_spec.rb
212
+ - spec/autoscaler/sidekiq/thread_server_spec.rb
230
213
  - spec/spec_helper.rb
231
214
  - spec/test_system.rb
232
215
  has_rdoc:
@@ -1,68 +0,0 @@
1
- require 'celluloid/current'
2
-
3
- module Autoscaler
4
- module Sidekiq
5
- # Actor to monitor the sidekiq server for scale-down
6
- class CelluloidMonitor
7
- include Celluloid
8
-
9
- # @param [scaler] scaler object that actually performs scaling operations (e.g. {HerokuPlatformScaler})
10
- # @param [Strategy] strategy object that decides the target number of workers (e.g. {BinaryScalingStrategy})
11
- # @param [System] system interface to the queuing system for use by the strategy
12
- def initialize(scaler, strategy, system)
13
- @scaler = scaler
14
- @strategy = strategy
15
- @system = system
16
- @running = false
17
- end
18
-
19
- # Periodically update the desired number of workers
20
- # @param [Numeric] interval polling interval, mostly for testing
21
- def wait_for_downscale(interval = 15)
22
- once do
23
- active_now!
24
-
25
- workers = :unknown
26
-
27
- begin
28
- sleep(interval)
29
- target_workers = @strategy.call(@system, idle_time)
30
- workers = @scaler.workers = target_workers unless workers == target_workers
31
- end while workers > 0
32
- ::Sidekiq::ProcessSet.new.each(&:quiet!)
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,48 +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. {HerokuPlatformScaler})
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
- :args => [
18
- scaler,
19
- strategy(timeout),
20
- QueueSystem.new(specified_queues),
21
- ]
22
- end
23
- end
24
-
25
- # Sidekiq middleware api entry point
26
- def call(worker, msg, queue, _ = nil)
27
- monitor.async.starting_job
28
- yield
29
- ensure
30
- # monitor might have gone, e.g. if Sidekiq has received SIGTERM
31
- monitor.async.finished_job if monitor
32
- end
33
-
34
- private
35
- def monitor
36
- Celluloid::Actor[:autoscaler_monitor]
37
- end
38
-
39
- def strategy(timeout)
40
- if timeout.respond_to?(:call)
41
- timeout
42
- else
43
- DelayedShutdown.new(BinaryScalingStrategy.new, timeout)
44
- end
45
- end
46
- end
47
- end
48
- 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
- expect(scaler.workers).to eq 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
- expect(scaler.workers).to eq 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
- expect(scaler.workers).to eq 0
37
- manager.terminate
38
- end
39
- end
@@ -1,16 +0,0 @@
1
- require 'spec_helper'
2
- require 'autoscaler/sidekiq/monitor_middleware_adapter'
3
-
4
- describe Autoscaler::Sidekiq::MonitorMiddlewareAdapter do
5
- before do
6
- @redis = Sidekiq.redis = REDIS
7
- Sidekiq.redis {|c| c.flushdb }
8
- end
9
-
10
- let(:cut) {Autoscaler::Sidekiq::MonitorMiddlewareAdapter}
11
- let(:scaler) {TestScaler.new(1)}
12
- let(:server) {cut.new(scaler, 0, ['queue'])}
13
-
14
- it('yields') {expect(server.call(Object.new, {}, 'queue') {:foo}).to eq :foo}
15
- it('yields with a redis pool') {expect(server.call(Object.new, {}, 'queue', Sidekiq.method(:redis)) {:foo}).to eq :foo}
16
- end