autoscaler 0.10.0 → 0.11.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: 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