autoscaler 0.1.0 → 0.2.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.
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