autoscale 0.9.1
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 +7 -0
- data/CHANGELOG.md +81 -0
- data/Guardfile +12 -0
- data/README.md +81 -0
- data/examples/complex.rb +39 -0
- data/examples/simple.rb +28 -0
- data/lib/autoscaler.rb +5 -0
- data/lib/autoscaler/binary_scaling_strategy.rb +26 -0
- data/lib/autoscaler/counter_cache_memory.rb +35 -0
- data/lib/autoscaler/counter_cache_redis.rb +50 -0
- data/lib/autoscaler/delayed_shutdown.rb +44 -0
- data/lib/autoscaler/heroku_scaler.rb +81 -0
- data/lib/autoscaler/ignore_scheduled_and_retrying.rb +13 -0
- data/lib/autoscaler/linear_scaling_strategy.rb +39 -0
- data/lib/autoscaler/sidekiq.rb +11 -0
- data/lib/autoscaler/sidekiq/activity.rb +62 -0
- data/lib/autoscaler/sidekiq/celluloid_monitor.rb +67 -0
- data/lib/autoscaler/sidekiq/client.rb +50 -0
- data/lib/autoscaler/sidekiq/entire_queue_system.rb +41 -0
- data/lib/autoscaler/sidekiq/monitor_middleware_adapter.rb +46 -0
- data/lib/autoscaler/sidekiq/queue_system.rb +20 -0
- data/lib/autoscaler/sidekiq/sleep_wait_server.rb +51 -0
- data/lib/autoscaler/sidekiq/specified_queue_system.rb +48 -0
- data/lib/autoscaler/stub_scaler.rb +25 -0
- data/lib/autoscaler/version.rb +4 -0
- data/spec/autoscaler/binary_scaling_strategy_spec.rb +19 -0
- data/spec/autoscaler/counter_cache_memory_spec.rb +21 -0
- data/spec/autoscaler/counter_cache_redis_spec.rb +49 -0
- data/spec/autoscaler/delayed_shutdown_spec.rb +23 -0
- data/spec/autoscaler/heroku_scaler_spec.rb +49 -0
- data/spec/autoscaler/ignore_scheduled_and_retrying_spec.rb +33 -0
- data/spec/autoscaler/linear_scaling_strategy_spec.rb +85 -0
- data/spec/autoscaler/sidekiq/activity_spec.rb +34 -0
- data/spec/autoscaler/sidekiq/celluloid_monitor_spec.rb +39 -0
- data/spec/autoscaler/sidekiq/client_spec.rb +35 -0
- data/spec/autoscaler/sidekiq/entire_queue_system_spec.rb +65 -0
- data/spec/autoscaler/sidekiq/monitor_middleware_adapter_spec.rb +16 -0
- data/spec/autoscaler/sidekiq/sleep_wait_server_spec.rb +45 -0
- data/spec/autoscaler/sidekiq/specified_queue_system_spec.rb +63 -0
- data/spec/spec_helper.rb +16 -0
- data/spec/test_system.rb +11 -0
- metadata +187 -0
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'heroku-api'
|
2
|
+
|
3
|
+
module Autoscaler
|
4
|
+
# A minimal scaler to use as stub for local testing
|
5
|
+
class StubScaler
|
6
|
+
# @param [String] type used to distinguish messages from multiple stubs
|
7
|
+
def initialize(type = 'worker')
|
8
|
+
@type = type
|
9
|
+
@workers = 0
|
10
|
+
end
|
11
|
+
|
12
|
+
attr_reader :type
|
13
|
+
|
14
|
+
# Read the current worker count
|
15
|
+
# @return [Numeric] number of workers
|
16
|
+
attr_reader :workers
|
17
|
+
|
18
|
+
# Set the number of workers
|
19
|
+
# @param [Numeric] n number of workers
|
20
|
+
def workers=(n)
|
21
|
+
p "Scaling #{type} to #{n}"
|
22
|
+
@workers = n
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'test_system'
|
3
|
+
require 'autoscaler/binary_scaling_strategy'
|
4
|
+
|
5
|
+
describe Autoscaler::BinaryScalingStrategy do
|
6
|
+
let(:cut) {Autoscaler::BinaryScalingStrategy}
|
7
|
+
|
8
|
+
it "scales with no work" do
|
9
|
+
system = TestSystem.new(0)
|
10
|
+
strategy = cut.new
|
11
|
+
strategy.call(system, 1).should == 0
|
12
|
+
end
|
13
|
+
|
14
|
+
it "does not scale with pending work" do
|
15
|
+
system = TestSystem.new(1)
|
16
|
+
strategy = cut.new(2)
|
17
|
+
strategy.call(system, 1).should == 2
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'autoscaler/counter_cache_memory'
|
3
|
+
|
4
|
+
describe Autoscaler::CounterCacheMemory do
|
5
|
+
let(:cut) {Autoscaler::CounterCacheMemory}
|
6
|
+
|
7
|
+
it {expect{cut.new.counter}.to raise_error(cut::Expired)}
|
8
|
+
it {cut.new.counter{1}.should == 1}
|
9
|
+
|
10
|
+
it 'set and store' do
|
11
|
+
cache = cut.new
|
12
|
+
cache.counter = 1
|
13
|
+
cache.counter.should == 1
|
14
|
+
end
|
15
|
+
|
16
|
+
it 'times out' do
|
17
|
+
cache = cut.new(0)
|
18
|
+
cache.counter = 1
|
19
|
+
expect{cache.counter.should}.to raise_error(cut::Expired)
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'autoscaler/counter_cache_redis'
|
3
|
+
|
4
|
+
describe Autoscaler::CounterCacheRedis do
|
5
|
+
before do
|
6
|
+
@redis = Sidekiq.redis = REDIS
|
7
|
+
Sidekiq.redis {|c| c.flushdb }
|
8
|
+
end
|
9
|
+
|
10
|
+
let(:cut) {Autoscaler::CounterCacheRedis}
|
11
|
+
subject {cut.new(Sidekiq.method(:redis))}
|
12
|
+
|
13
|
+
it {expect{subject.counter}.to raise_error(cut::Expired)}
|
14
|
+
it {subject.counter{1}.should == 1}
|
15
|
+
|
16
|
+
it 'set and store' do
|
17
|
+
subject.counter = 2
|
18
|
+
subject.counter.should == 2
|
19
|
+
end
|
20
|
+
|
21
|
+
it 'does not conflict with multiple worker types' do
|
22
|
+
other_worker_cache = cut.new(@redis, 300, 'other_worker')
|
23
|
+
subject.counter = 1
|
24
|
+
other_worker_cache.counter = 2
|
25
|
+
|
26
|
+
subject.counter.should == 1
|
27
|
+
other_worker_cache.counter = 2
|
28
|
+
end
|
29
|
+
|
30
|
+
it 'times out' do
|
31
|
+
cache = cut.new(Sidekiq.method(:redis), 1) # timeout 0 invalid
|
32
|
+
cache.counter = 3
|
33
|
+
sleep(2)
|
34
|
+
expect{cache.counter}.to raise_error(cut::Expired)
|
35
|
+
end
|
36
|
+
|
37
|
+
it 'passed a connection pool' do
|
38
|
+
cache = cut.new(@redis)
|
39
|
+
cache.counter = 4
|
40
|
+
cache.counter.should == 4
|
41
|
+
end
|
42
|
+
|
43
|
+
it 'passed a plain connection' do
|
44
|
+
connection = Redis.connect(:url => 'http://localhost:9736', :namespace => 'autoscaler')
|
45
|
+
cache = cut.new connection
|
46
|
+
cache.counter = 5
|
47
|
+
cache.counter.should == 5
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'test_system'
|
3
|
+
require 'autoscaler/delayed_shutdown'
|
4
|
+
|
5
|
+
describe Autoscaler::DelayedShutdown do
|
6
|
+
let(:cut) {Autoscaler::DelayedShutdown}
|
7
|
+
|
8
|
+
it "returns normal values" do
|
9
|
+
strategy = cut.new(lambda{|s,t| 2}, 0)
|
10
|
+
strategy.call(nil, 1).should == 2
|
11
|
+
end
|
12
|
+
|
13
|
+
it "delays zeros" do
|
14
|
+
strategy = cut.new(lambda{|s,t| 0}, 60)
|
15
|
+
strategy.call(nil, 1).should == 1
|
16
|
+
end
|
17
|
+
|
18
|
+
it "eventually returns zero" do
|
19
|
+
strategy = cut.new(lambda{|s,t| 0}, 60)
|
20
|
+
strategy.stub(:level_idle_time).and_return(61)
|
21
|
+
strategy.call(nil, 61).should == 0
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'autoscaler/heroku_scaler'
|
3
|
+
require 'heroku/api/errors'
|
4
|
+
|
5
|
+
describe Autoscaler::HerokuScaler, :online => true do
|
6
|
+
let(:cut) {Autoscaler::HerokuScaler}
|
7
|
+
let(:client) {cut.new}
|
8
|
+
subject {client}
|
9
|
+
|
10
|
+
its(:workers) {should == 0}
|
11
|
+
|
12
|
+
describe 'scaled' do
|
13
|
+
around do |example|
|
14
|
+
client.workers = 1
|
15
|
+
example.yield
|
16
|
+
client.workers = 0
|
17
|
+
end
|
18
|
+
|
19
|
+
its(:workers) {should == 1}
|
20
|
+
end
|
21
|
+
|
22
|
+
shared_examples 'exception handler' do |exception_class|
|
23
|
+
before do
|
24
|
+
client.should_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 {client.workers.should == 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; @caught.should 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
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'test_system'
|
3
|
+
require 'autoscaler/ignore_scheduled_and_retrying'
|
4
|
+
|
5
|
+
describe Autoscaler::IgnoreScheduledAndRetrying do
|
6
|
+
let(:cut) {Autoscaler::IgnoreScheduledAndRetrying}
|
7
|
+
|
8
|
+
it "passes through enqueued" do
|
9
|
+
system = Struct.new(:enqueued).new(3)
|
10
|
+
strategy = proc {|system, time| system.enqueued}
|
11
|
+
cut.new(strategy).call(system, 0).should == 3
|
12
|
+
end
|
13
|
+
|
14
|
+
it "passes through workers" do
|
15
|
+
system = Struct.new(:workers).new(3)
|
16
|
+
strategy = proc {|system, time| system.workers}
|
17
|
+
cut.new(strategy).call(system, 0).should == 3
|
18
|
+
end
|
19
|
+
|
20
|
+
it "ignores scheduled" do
|
21
|
+
system = Struct.new(:scheduled).new(3)
|
22
|
+
strategy = proc {|system, time| system.scheduled}
|
23
|
+
cut.new(strategy).call(system, 0).should == 0
|
24
|
+
end
|
25
|
+
|
26
|
+
it "ignores retrying" do
|
27
|
+
system = Struct.new(:retrying).new(3)
|
28
|
+
strategy = proc {|system, time| system.retrying}
|
29
|
+
cut.new(strategy).call(system, 0).should == 0
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
|
@@ -0,0 +1,85 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'test_system'
|
3
|
+
require 'autoscaler/linear_scaling_strategy'
|
4
|
+
|
5
|
+
describe Autoscaler::LinearScalingStrategy do
|
6
|
+
let(:cut) {Autoscaler::LinearScalingStrategy}
|
7
|
+
|
8
|
+
it "deactivates with no work" do
|
9
|
+
system = TestSystem.new(0)
|
10
|
+
strategy = cut.new(1)
|
11
|
+
strategy.call(system, 1).should == 0
|
12
|
+
end
|
13
|
+
|
14
|
+
it "activates with some work" do
|
15
|
+
system = TestSystem.new(1)
|
16
|
+
strategy = cut.new(1)
|
17
|
+
strategy.call(system, 1).should be > 0
|
18
|
+
end
|
19
|
+
|
20
|
+
it "minimally scales with minimal work" do
|
21
|
+
system = TestSystem.new(1)
|
22
|
+
strategy = cut.new(2, 2)
|
23
|
+
strategy.call(system, 1).should == 1
|
24
|
+
end
|
25
|
+
|
26
|
+
it "maximally scales with too much work" do
|
27
|
+
system = TestSystem.new(5)
|
28
|
+
strategy = cut.new(2, 2)
|
29
|
+
strategy.call(system, 1).should == 2
|
30
|
+
end
|
31
|
+
|
32
|
+
it "proportionally scales with some work" do
|
33
|
+
system = TestSystem.new(5)
|
34
|
+
strategy = cut.new(5, 2)
|
35
|
+
strategy.call(system, 1).should == 3
|
36
|
+
end
|
37
|
+
|
38
|
+
it "doesn't scale unless minimum is met" do
|
39
|
+
system = TestSystem.new(2)
|
40
|
+
strategy = cut.new(10, 4, 0.5)
|
41
|
+
strategy.call(system, 1).should == 0
|
42
|
+
end
|
43
|
+
|
44
|
+
it "scales proprotionally with a minimum" do
|
45
|
+
system = TestSystem.new(3)
|
46
|
+
strategy = cut.new(10, 4, 0.5)
|
47
|
+
strategy.call(system, 1).should == 1
|
48
|
+
end
|
49
|
+
|
50
|
+
it "scales maximally with a minimum" do
|
51
|
+
system = TestSystem.new(25)
|
52
|
+
strategy = cut.new(5, 4, 0.5)
|
53
|
+
strategy.call(system, 1).should == 5
|
54
|
+
end
|
55
|
+
|
56
|
+
it "scales proportionally with a minimum > 1" do
|
57
|
+
system = TestSystem.new(12)
|
58
|
+
strategy = cut.new(5, 4, 2)
|
59
|
+
strategy.call(system, 1).should == 2
|
60
|
+
end
|
61
|
+
|
62
|
+
it "scales maximally with a minimum factor > 1" do
|
63
|
+
system = TestSystem.new(30)
|
64
|
+
strategy = cut.new(5, 4, 2)
|
65
|
+
strategy.call(system, 1).should == 5
|
66
|
+
end
|
67
|
+
|
68
|
+
it "doesn't scale down engaged workers" do
|
69
|
+
system = TestSystem.new(0, 2)
|
70
|
+
strategy = cut.new(5, 4)
|
71
|
+
strategy.call(system, 1).should == 2
|
72
|
+
end
|
73
|
+
|
74
|
+
it "doesn't scale above max workers even if engaged workers is greater" do
|
75
|
+
system = TestSystem.new(40, 6)
|
76
|
+
strategy = cut.new(5, 4)
|
77
|
+
strategy.call(system, 1).should == 5
|
78
|
+
end
|
79
|
+
|
80
|
+
it "returns zero if requested capacity is zero" do
|
81
|
+
system = TestSystem.new(0, 0)
|
82
|
+
strategy = cut.new(0, 0)
|
83
|
+
strategy.call(system, 5).should == 0
|
84
|
+
end
|
85
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'autoscaler/sidekiq/activity'
|
3
|
+
|
4
|
+
describe Autoscaler::Sidekiq::Activity do
|
5
|
+
before do
|
6
|
+
@redis = Sidekiq.redis = REDIS
|
7
|
+
Sidekiq.redis {|c| c.flushdb }
|
8
|
+
end
|
9
|
+
|
10
|
+
let(:cut) {Autoscaler::Sidekiq::Activity}
|
11
|
+
let(:activity) {cut.new(0)}
|
12
|
+
|
13
|
+
context "when another process is working" do
|
14
|
+
let(:other_process) {cut.new(10)}
|
15
|
+
before do
|
16
|
+
activity.idle!('queue')
|
17
|
+
other_process.working!('other_queue')
|
18
|
+
end
|
19
|
+
it {activity.should be_idle(['queue'])}
|
20
|
+
end
|
21
|
+
|
22
|
+
it 'passed a connection pool' do
|
23
|
+
activity = cut.new(5, @redis)
|
24
|
+
activity.working!('queue')
|
25
|
+
activity.should_not be_idle(['queue'])
|
26
|
+
end
|
27
|
+
|
28
|
+
it 'passed a plain connection' do
|
29
|
+
connection = Redis.connect(:url => 'http://localhost:9736', :namespace => 'autoscaler')
|
30
|
+
activity = cut.new(5, connection)
|
31
|
+
activity.working!('queue')
|
32
|
+
activity.should_not be_idle(['queue'])
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,39 @@
|
|
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
|
+
scaler.workers.should == 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
|
+
scaler.workers.should == 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
|
+
scaler.workers.should == 0
|
37
|
+
manager.terminate
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'test_system'
|
3
|
+
require 'autoscaler/sidekiq/client'
|
4
|
+
|
5
|
+
describe Autoscaler::Sidekiq::Client do
|
6
|
+
let(:cut) {Autoscaler::Sidekiq::Client}
|
7
|
+
let(:scaler) {TestScaler.new(0)}
|
8
|
+
let(:client) {cut.new('queue' => scaler)}
|
9
|
+
|
10
|
+
describe 'call' do
|
11
|
+
it 'scales' do
|
12
|
+
client.call(Class, {}, 'queue') {}
|
13
|
+
scaler.workers.should == 1
|
14
|
+
end
|
15
|
+
|
16
|
+
it 'scales with a redis pool' do
|
17
|
+
client.call(Class, {}, 'queue', ::Sidekiq.method(:redis)) {}
|
18
|
+
scaler.workers.should == 1
|
19
|
+
end
|
20
|
+
|
21
|
+
it('yields') {client.call(Class, {}, 'queue') {:foo}.should == :foo}
|
22
|
+
end
|
23
|
+
|
24
|
+
describe 'initial workers' do
|
25
|
+
it 'works with default arguments' do
|
26
|
+
client.set_initial_workers
|
27
|
+
scaler.workers.should == 0
|
28
|
+
end
|
29
|
+
|
30
|
+
it 'scales when necessary' do
|
31
|
+
client.set_initial_workers {|q| TestSystem.new(1)}
|
32
|
+
scaler.workers.should == 1
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'autoscaler/sidekiq/entire_queue_system'
|
3
|
+
|
4
|
+
describe Autoscaler::Sidekiq::EntireQueueSystem do
|
5
|
+
let(:cut) {Autoscaler::Sidekiq::EntireQueueSystem}
|
6
|
+
|
7
|
+
before do
|
8
|
+
@redis = Sidekiq.redis = REDIS
|
9
|
+
Sidekiq.redis {|c| c.flushdb }
|
10
|
+
end
|
11
|
+
|
12
|
+
def with_work_in_set(queue, set)
|
13
|
+
payload = Sidekiq.dump_json('queue' => queue)
|
14
|
+
Sidekiq.redis { |c| c.zadd(set, (Time.now.to_f + 30.to_f).to_s, payload)}
|
15
|
+
end
|
16
|
+
|
17
|
+
def with_scheduled_work_in(queue)
|
18
|
+
with_work_in_set(queue, 'schedule')
|
19
|
+
end
|
20
|
+
|
21
|
+
def with_retry_work_in(queue)
|
22
|
+
with_work_in_set(queue, 'retry')
|
23
|
+
end
|
24
|
+
|
25
|
+
subject {cut.new}
|
26
|
+
|
27
|
+
it {subject.queue_names.should == []}
|
28
|
+
it {subject.workers.should == 0}
|
29
|
+
|
30
|
+
describe 'no queued work' do
|
31
|
+
it "with no work" do
|
32
|
+
subject.stub(:sidekiq_queues).and_return({'queue' => 0, 'another_queue' => 0})
|
33
|
+
subject.queued.should == 0
|
34
|
+
end
|
35
|
+
|
36
|
+
it "with no work and no queues" do
|
37
|
+
subject.queued.should == 0
|
38
|
+
end
|
39
|
+
|
40
|
+
it "with no scheduled work" do
|
41
|
+
subject.scheduled.should == 0
|
42
|
+
end
|
43
|
+
|
44
|
+
it "with no retry work" do
|
45
|
+
subject.retrying.should == 0
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
describe 'with queued work' do
|
50
|
+
it "with enqueued work" do
|
51
|
+
subject.stub(:sidekiq_queues).and_return({'queue' => 1})
|
52
|
+
subject.queued.should == 1
|
53
|
+
end
|
54
|
+
|
55
|
+
it "with schedule work" do
|
56
|
+
with_scheduled_work_in('queue')
|
57
|
+
subject.scheduled.should == 1
|
58
|
+
end
|
59
|
+
|
60
|
+
it "with retry work" do
|
61
|
+
with_retry_work_in('queue')
|
62
|
+
subject.retrying.should == 1
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|