autoscaler 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.2.0
4
+
5
+ - Raise minimum Sidekiq version to 2.6.1 to take advantage of Stats API
6
+ - Inspect scheduled and retry sets to see if they match `specified_queues`
7
+ - Testing: Refactor server middleware tests
8
+
3
9
  ## 0.1.0
4
10
 
5
11
  - The `retry` and `scheduled` queues are now considered for shutdown
data/README.md CHANGED
@@ -58,7 +58,7 @@ Justin Love, [@wondible](http://twitter.com/wondible), [https://github.com/Justi
58
58
 
59
59
  Ported to Heroku-Api by Fix Peña, [https://github.com/fixr](https://github.com/fixr)
60
60
 
61
- Retry/schedule queues by Matt Anderson [https://github.com/tonkapark](https://github.com/tonkapark)
61
+ Retry/schedule sets by Matt Anderson [https://github.com/tonkapark](https://github.com/tonkapark) and Thibaud Guillaume-Gentil [https://github.com/jilion](https://github.com/jilion)
62
62
 
63
63
  ## Licence
64
64
 
data/examples/complex.rb CHANGED
@@ -1,4 +1,3 @@
1
- require 'securerandom' # bug in Sidekiq as of 2.2.1
2
1
  require 'sidekiq'
3
2
  require 'autoscaler/sidekiq'
4
3
  require 'autoscaler/heroku_scaler'
data/examples/simple.rb CHANGED
@@ -1,4 +1,3 @@
1
- require 'securerandom' # bug in Sidekiq as of 2.2.1
2
1
  require 'sidekiq'
3
2
  require 'autoscaler/sidekiq'
4
3
  require 'autoscaler/heroku_scaler'
@@ -1,4 +1,3 @@
1
- require 'securerandom' # bug in Sidekiq as of 2.2.1
2
1
  require 'sidekiq'
3
2
 
4
3
  module Autoscaler
@@ -42,35 +41,41 @@ module Autoscaler
42
41
  end
43
42
 
44
43
  private
45
- def queues
46
- @specified_queues || registered_queues
44
+ def refresh_sidekiq_queues!
45
+ @sidekiq_queues = ::Sidekiq::Stats.new.queues
47
46
  end
48
47
 
49
- def registered_queues
50
- ::Sidekiq.redis { |x| x.smembers('queues') }
48
+ attr_reader :sidekiq_queues
49
+
50
+ def queue_names
51
+ (@specified_queues || sidekiq_queues.keys)
51
52
  end
52
53
 
53
- def empty?(name)
54
- ::Sidekiq.redis { |conn| conn.llen("queue:#{name}") == 0 }
54
+ def queued_work?
55
+ queue_names.any? {|name| sidekiq_queues[name].to_i > 0 }
55
56
  end
56
-
57
+
57
58
  def scheduled_work?
58
- ::Sidekiq.redis { |c| c.zcard("schedule") > 0 }
59
+ empty_sorted_set?("schedule")
59
60
  end
60
-
61
+
61
62
  def retry_work?
62
- ::Sidekiq.redis { |c| c.zcard("retry") > 0 }
63
- end
63
+ empty_sorted_set?("retry")
64
+ end
65
+
66
+ def empty_sorted_set?(sorted_set)
67
+ ss = ::Sidekiq::SortedSet.new(sorted_set)
68
+ ss.any? { |job| queue_names.include?(job.queue) }
69
+ end
64
70
 
65
71
  def pending_work?
66
- queues.any? {|q| !empty?(q)}
72
+ refresh_sidekiq_queues!
73
+ queued_work? || scheduled_work? || retry_work?
67
74
  end
68
75
 
69
76
  def wait_for_task_or_scale
70
77
  loop do
71
78
  return if pending_work?
72
- return if scheduled_work?
73
- return if retry_work?
74
79
  return @scaler.workers = 0 if idle?
75
80
  sleep(0.5)
76
81
  end
@@ -1,4 +1,4 @@
1
1
  module Autoscaler
2
2
  # version number
3
- VERSION = "0.1.0"
3
+ VERSION = "0.2.0"
4
4
  end
@@ -14,60 +14,110 @@ describe Autoscaler::Sidekiq do
14
14
  @redis = Sidekiq.redis = REDIS
15
15
  Sidekiq.redis {|c| c.flushdb }
16
16
  end
17
-
17
+
18
18
  let(:scaler) do
19
19
  Scaler.new(workers)
20
20
  end
21
21
 
22
22
  describe Autoscaler::Sidekiq::Client do
23
23
  let(:cut) {Autoscaler::Sidekiq::Client}
24
- let(:sa) {cut.new('queue' => scaler)}
24
+ let(:client) {cut.new('queue' => scaler)}
25
25
  let(:workers) {0}
26
26
 
27
27
  describe 'scales' do
28
- before {sa.call(Class, {}, 'queue') {}}
29
- subject {scaler.workers}
30
- it {should == 1}
28
+ before {client.call(Class, {}, 'queue') {}}
29
+ it {scaler.workers.should == 1}
31
30
  end
32
31
 
33
32
  describe 'yields' do
34
- it {sa.call(Class, {}, 'queue') {:foo}.should == :foo}
33
+ it {client.call(Class, {}, 'queue') {:foo}.should == :foo}
35
34
  end
36
35
  end
37
36
 
38
37
  describe Autoscaler::Sidekiq::Server do
39
38
  let(:cut) {Autoscaler::Sidekiq::Server}
40
- let(:sa) {cut.new(scaler, 0)}
39
+ let(:server) {cut.new(scaler, 0, ['queue'])}
41
40
  let(:workers) {1}
42
41
 
42
+ def with_work_in_set(queue, set)
43
+ payload = Sidekiq.dump_json('queue' => queue)
44
+ Sidekiq.redis { |c| c.zadd(set, (Time.now.to_f + 30.to_f).to_s, payload)}
45
+ end
46
+
47
+ def with_scheduled_work_in(queue)
48
+ with_work_in_set(queue, 'schedule')
49
+ end
50
+
51
+ def with_retry_work_in(queue)
52
+ with_work_in_set(queue, 'retry')
53
+ end
54
+
55
+ def when_run
56
+ server.call(Object.new, {}, 'queue') {}
57
+ end
58
+
59
+ def self.when_run_should_scale
60
+ it('should downscale') do
61
+ when_run
62
+ scaler.workers.should == 0
63
+ end
64
+ end
65
+
66
+ def self.when_run_should_not_scale
67
+ it('should not downscale') do
68
+ when_run
69
+ scaler.workers.should == 1
70
+ end
71
+ end
72
+
43
73
  describe 'scales' do
44
- context "with only enqueued work" do
45
- before{sa.call(Object.new, {}, 'queue') {}}
46
- subject {scaler.workers}
47
- it {should == 0}
74
+ context "with no work" do
75
+ before do
76
+ server.stub(:sidekiq_queues).and_return({'queue' => 0, 'another_queue' => 1})
77
+ end
78
+ when_run_should_scale
79
+ end
80
+
81
+ context "with scheduled work in another queue" do
82
+ before do
83
+ with_scheduled_work_in('another_queue')
84
+ end
85
+ when_run_should_scale
86
+ end
87
+
88
+ context "with retry work in another queue" do
89
+ before do
90
+ with_retry_work_in('another_queue')
91
+ end
92
+ when_run_should_scale
93
+ end
94
+ end
95
+
96
+ describe 'does not scale' do
97
+ context "with enqueued work" do
98
+ before do
99
+ server.stub(:sidekiq_queues).and_return({'queue' => 1})
100
+ end
101
+ when_run_should_not_scale
48
102
  end
49
-
103
+
50
104
  context "with schedule work" do
51
- before do
52
- Sidekiq.redis { |c| c.zadd('schedule', (Time.now.to_f + 30.to_f).to_s, '{}' )}
53
- sa.call(Object.new, {}, 'queue') {}
105
+ before do
106
+ with_scheduled_work_in('queue')
54
107
  end
55
- subject {scaler.workers}
56
- it {should == 1}
108
+ when_run_should_not_scale
57
109
  end
58
-
110
+
59
111
  context "with retry work" do
60
112
  before do
61
- Sidekiq.redis { |c| c.zadd('retry', (Time.now.to_f + 30.to_f).to_s, '{}' )}
62
- sa.call(Object.new, {}, 'queue') {}
63
- end
64
- subject {scaler.workers}
65
- it {should == 1}
113
+ with_retry_work_in('queue')
114
+ end
115
+ when_run_should_not_scale
66
116
  end
67
117
  end
68
-
118
+
69
119
  describe 'yields' do
70
- it {sa.call(Object.new, {}, 'queue') {:foo}.should == :foo}
71
- end
120
+ it {server.call(Object.new, {}, 'queue') {:foo}.should == :foo}
121
+ end
72
122
  end
73
123
  end
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.1.0
4
+ version: 0.2.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -10,24 +10,30 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2013-01-20 00:00:00.000000000 Z
13
+ date: 2013-01-29 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: sidekiq
17
17
  requirement: !ruby/object:Gem::Requirement
18
18
  none: false
19
19
  requirements:
20
- - - ~>
20
+ - - ! '>='
21
+ - !ruby/object:Gem::Version
22
+ version: 2.6.1
23
+ - - <
21
24
  - !ruby/object:Gem::Version
22
- version: '2.2'
25
+ version: '3.0'
23
26
  type: :runtime
24
27
  prerelease: false
25
28
  version_requirements: !ruby/object:Gem::Requirement
26
29
  none: false
27
30
  requirements:
28
- - - ~>
31
+ - - ! '>='
32
+ - !ruby/object:Gem::Version
33
+ version: 2.6.1
34
+ - - <
29
35
  - !ruby/object:Gem::Version
30
- version: '2.2'
36
+ version: '3.0'
31
37
  - !ruby/object:Gem::Dependency
32
38
  name: heroku-api
33
39
  requirement: !ruby/object:Gem::Requirement