sqskiq 0.0.3 → 0.0.4

Sign up to get free protection for your applications and to get access to all the features.
data/lib/sqskiq.rb CHANGED
@@ -7,58 +7,59 @@ require 'sqskiq/batch_process'
7
7
 
8
8
  module Sqskiq
9
9
 
10
- def self.bootstrap(options, worker_class)
11
- params = [ @aws_access_key_id, @aws_secret_access_key, options[:queue_name] ]
10
+ ##
11
+ # Configures and starts actor system
12
+ def self.bootstrap(worker_config, worker_class)
13
+ config = valid_config_from(worker_config)
14
+ credentials = [ @aws_access_key_id, @aws_secret_access_key, config[:queue_name] ]
12
15
 
13
- configured_pool_sizes = pool_sizes(options)
16
+ Celluloid::Actor[:manager] = @manager = Manager.new(config[:empty_queue_throttle])
17
+ Celluloid::Actor[:fetcher] = @fetcher = Fetcher.pool(:size => config[:num_fetchers], :args => credentials)
18
+ Celluloid::Actor[:deleter] = @deleter = Deleter.pool(:size => config[:num_deleters], :args => credentials)
19
+ Celluloid::Actor[:processor] = @processor = Processor.pool(:size => config[:num_workers], :args => worker_class)
20
+ Celluloid::Actor[:batcher] = @batcher = BatchProcessor.pool(:size => config[:num_batches])
14
21
 
15
- Celluloid::Actor[:manager] = @manager = Manager.new
16
- Celluloid::Actor[:fetcher] = @fetcher = Fetcher.pool(:size => configured_pool_sizes[:num_fetchers], :args => params)
17
- Celluloid::Actor[:processor] = @processor = Processor.pool(:size => configured_pool_sizes[:num_workers], :args => worker_class)
18
- Celluloid::Actor[:batch_processor] = @batch_processor = BatchProcessor.pool(:size => configured_pool_sizes[:num_batches])
19
- Celluloid::Actor[:deleter] = @deleter = Deleter.pool(:size => configured_pool_sizes[:num_deleters], :args => params)
20
-
21
22
  configure_signal_listeners
22
-
23
+
23
24
  @manager.bootstrap
24
25
  while @manager.running? do
25
26
  sleep 2
26
27
  end
27
-
28
- @fetcher.__shutdown__
29
- @batch_processor.__shutdown__
30
- @processor.__shutdown__
31
- @deleter.__shutdown__
32
-
33
28
  @manager.terminate
34
29
  end
35
30
 
31
+ # Subscribes actors to receive system signals
32
+ # Each actor when receives a signal should execute
33
+ # appropriate code to exit cleanly
36
34
  def self.configure_signal_listeners
37
35
  ['SIGTERM', 'TERM', 'SIGINT'].each do |signal|
38
36
  trap(signal) do
39
37
  @manager.publish('SIGTERM')
40
- @batch_processor.publish('SIGTERM')
38
+ @batcher.publish('SIGTERM')
41
39
  @processor.publish('SIGTERM')
42
40
  end
43
41
  end
44
42
  end
45
43
 
46
- def self.pool_sizes(options)
47
- # for now, min processors should be 2
48
- num_workers = (options[:processors].nil? || options[:processors].to_i < 2)? 20 : options[:processors]
49
-
50
- # each fetch brings up to 10 messages to process.
51
- # the number of fetchers is a number able to keep all
52
- # workers handling messages
53
- # TODO: acctualy the min number must be greater than 2 because we are using
54
- # celluloid pool, but that will be changed!
44
+ ##
45
+ # checks the provided configuration
46
+ # and add the defaults when not specified
47
+ def self.valid_config_from(worker_config)
48
+ num_workers = (worker_config[:processors].nil? || worker_config[:processors].to_i < 2)? 20 : worker_config[:processors]
49
+ # messy code due to celluloid pool constraint of 2 as min pool size: see spec for better understanding
55
50
  num_fetchers = num_workers / 10
56
51
  num_fetchers = num_fetchers + 1 if num_workers % 10 > 0
57
52
  num_fetchers = 2 if num_fetchers < 2
58
-
59
53
  num_deleters = num_batches = num_fetchers
60
-
61
- { num_workers: num_workers, num_fetchers: num_fetchers, num_batches: num_batches, num_deleters: num_deleters }
54
+
55
+ {
56
+ num_workers: num_workers,
57
+ num_fetchers: num_fetchers,
58
+ num_batches: num_batches,
59
+ num_deleters: num_deleters,
60
+ queue_name: worker_config[:queue_name],
61
+ empty_queue_throttle: worker_config[:empty_queue_throttle] || 0
62
+ }
62
63
  end
63
64
 
64
65
  def self.configure
@@ -13,7 +13,7 @@ module Sqskiq
13
13
  subscribe_for_shutdown
14
14
  end
15
15
 
16
- def batch_process(messages)
16
+ def process(messages)
17
17
  process_result = []
18
18
  messages.each do |message|
19
19
  process_result << @processor.future.process(message)
@@ -30,7 +30,7 @@ module Sqskiq
30
30
  end
31
31
  end
32
32
 
33
- @manager.async.batch_process_done(success_messages)
33
+ @manager.async.batch_done(success_messages)
34
34
  end
35
35
  end
36
36
 
@@ -7,33 +7,43 @@ module Sqskiq
7
7
  include Celluloid
8
8
  include Sqskiq::SignalHandler
9
9
 
10
- def initialize
11
- subscribe_for_shutdown
10
+ @empty_queue = false
11
+
12
+ def initialize(empty_queue_throttle)
13
+ @empty_queue_throttle = empty_queue_throttle
14
+ subscribe_for_shutdown
12
15
  end
13
16
 
14
17
  def bootstrap
15
18
  @fetcher = Celluloid::Actor[:fetcher]
16
- @batch_processor = Celluloid::Actor[:batch_processor]
19
+ @batcher = Celluloid::Actor[:batcher]
17
20
  @deleter = Celluloid::Actor[:deleter]
18
21
 
19
22
  new_fetch(@fetcher.size)
20
23
  end
21
24
 
22
25
  def fetch_done(messages)
23
- @batch_processor.async.batch_process(messages) unless @shutting_down
26
+ @empty_queue = messages.empty?
27
+ @batcher.async.process(messages) unless @shutting_down
24
28
  end
25
29
 
26
- def batch_process_done(messages)
30
+ def batch_done(messages)
27
31
  @deleter.async.delete(messages)
28
32
  new_fetch(1)
29
33
  end
30
34
 
31
35
  def new_fetch(num)
32
- num.times { @fetcher.async.fetch unless @shutting_down }
36
+ after(throttle) do
37
+ num.times { @fetcher.async.fetch unless @shutting_down }
38
+ end
33
39
  end
34
40
 
35
41
  def running?
36
- not (@shutting_down and @deleter.busy_size == 0 and @batch_processor.busy_size == 0)
42
+ not (@shutting_down and @deleter.busy_size == 0 and @batcher.busy_size == 0)
43
+ end
44
+
45
+ def throttle
46
+ @empty_queue ? @empty_queue_throttle : 0
37
47
  end
38
48
 
39
49
  end
data/spec/manager_spec.rb CHANGED
@@ -1,70 +1,115 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  describe Sqskiq::Manager do
4
+ subject { Sqskiq::Manager.new(Random.rand(1..100)) }
5
+
4
6
  let(:deleter) { Object.new }
5
7
  let(:fetcher) { Object.new }
6
- let(:batch_processor) { Object.new }
8
+ let(:batcher) { Object.new }
7
9
  let(:shutting_down) { false }
8
- subject = described_class.new
9
10
 
10
11
  before do
11
12
  subject.instance_variable_set(:@fetcher, fetcher)
12
13
  subject.instance_variable_set(:@deleter, deleter)
13
- subject.instance_variable_set(:@batch_processor, batch_processor)
14
+ subject.instance_variable_set(:@batcher, batcher)
14
15
  subject.instance_variable_set(:@shutting_down, shutting_down)
15
16
  end
16
17
 
17
18
  describe '#running?' do
18
-
19
19
  context 'when the actor system is shutting down' do
20
- let(:shutting_down) { true }
20
+ let(:shutting_down) { true }
21
21
 
22
- describe 'if deleter is not empty' do
23
- before { deleter.should_receive(:busy_size).and_return(1) }
24
-
25
- it 'returns true' do
26
- subject.running?.should be_true
27
- end
28
- end
22
+ describe 'if deleter is not empty' do
23
+ before { deleter.should_receive(:busy_size).and_return(1) }
24
+
25
+ it { should be_running }
26
+ end
29
27
 
30
- describe 'if batch_processor is not empty' do
31
- before do
32
- deleter.should_receive(:busy_size).and_return(0)
33
- batch_processor.should_receive(:busy_size).and_return(1)
34
- end
28
+ describe 'if batcher is not empty' do
29
+ before do
30
+ deleter.should_receive(:busy_size).and_return(0)
31
+ batcher.should_receive(:busy_size).and_return(1)
32
+ end
35
33
 
36
- it 'returns true' do
37
- subject.running?.should be_true
38
- end
39
- end
34
+ it { should be_running }
35
+ end
40
36
 
41
- describe 'if batch_processor and deleter are empties' do
37
+ describe 'if batcher and deleter are empties' do
42
38
  before do
43
39
  deleter.should_receive(:busy_size).and_return(0)
44
- batch_processor.should_receive(:busy_size).and_return(0)
40
+ batcher.should_receive(:busy_size).and_return(0)
45
41
  end
46
42
 
47
- it 'returns false' do
48
- subject.running?.should be_false
49
- end
50
- end
51
-
43
+ it { should_not be_running }
44
+ end
52
45
  end
53
46
 
54
47
  context 'when the actor system is not shutting down' do
55
48
 
56
- describe 'even if batch_processor and deleter are empties' do
49
+ describe 'even if batcher and deleter are empties' do
57
50
  before do
58
51
  deleter.stub(:busy_size).and_return(0)
59
- batch_processor.stub(:busy_size).and_return(0)
52
+ batcher.stub(:busy_size).and_return(0)
60
53
  end
61
54
 
62
- it 'returns true' do
63
- subject.running?.should be_true
64
- end
55
+ it { should be_running }
65
56
  end
66
57
 
67
58
  end
68
59
  end
60
+
61
+ describe '#new_fetch' do
62
+ before do
63
+ subject.instance_variable_set(:@empty_queue, empty)
64
+ subject.instance_variable_set(:@empty_queue_throttle, 10)
65
+ end
66
+
67
+ context 'if queue is not empty' do
68
+ let(:empty) { false }
69
+
70
+ it 'applies a throttle of 0 seconds' do
71
+ Sqskiq::Manager.any_instance.should_receive(:after).with(0)
72
+ subject.new_fetch(1)
73
+ end
74
+ end
75
+
76
+ context 'if queue is empty' do
77
+ let(:empty) { true }
78
+
79
+ it 'applies a throttle of empty_queue_throttle seconds' do
80
+ Sqskiq::Manager.any_instance.should_receive(:after).with(10)
81
+ subject.new_fetch(1)
82
+ end
83
+ end
84
+ end
85
+
86
+ describe '#fetch_done' do
87
+
88
+ before { subject.instance_variable_set(:@empty_queue, empty) }
89
+
90
+ context 'when at least one message has been received' do
91
+ let(:messages) { ['someMessage'] }
92
+ let(:empty) { true }
93
+
94
+ it 'sets @empty_queue to false and process the messages' do
95
+ batcher.should_receive(:async).and_return(batcher)
96
+ batcher.should_receive(:process).with(messages)
97
+ subject.fetch_done(messages)
98
+ subject.instance_variable_get(:@empty_queue).should be_false
99
+ end
100
+ end
101
+
102
+ context 'when no messages are received' do
103
+ let(:messages) { [] }
104
+ let(:empty) { false }
69
105
 
106
+ it 'sets @empty_queue to true and does not process the messages' do
107
+ batcher.should_receive(:async).and_return(batcher)
108
+ batcher.should_receive(:process).with(messages)
109
+ subject.fetch_done(messages)
110
+ subject.instance_variable_get(:@empty_queue).should be_true
111
+ end
112
+ end
113
+ end
114
+
70
115
  end
data/spec/sqskiq_spec.rb CHANGED
@@ -6,11 +6,11 @@ describe Sqskiq do
6
6
  let(:options) { [ { processors: 1 }, ].sample }
7
7
 
8
8
  it 'uses the defaut value of 20' do
9
- pool_sizes = Sqskiq.pool_sizes(options)
10
- pool_sizes[:num_workers].should eq(20)
11
- pool_sizes[:num_fetchers].should eq(2)
12
- pool_sizes[:num_batches].should eq(2)
13
- pool_sizes[:num_deleters].should eq(2)
9
+ config = Sqskiq.valid_config_from(options)
10
+ config[:num_workers].should eq(20)
11
+ config[:num_fetchers].should eq(2)
12
+ config[:num_batches].should eq(2)
13
+ config[:num_deleters].should eq(2)
14
14
  end
15
15
  end
16
16
 
@@ -20,11 +20,11 @@ describe Sqskiq do
20
20
  let(:options) { { processors: [ 20, 30, 40 ].sample } }
21
21
 
22
22
  it 'uses the the given value' do
23
- pool_sizes = Sqskiq.pool_sizes(options)
24
- pool_sizes[:num_workers].should eq(options[:processors])
25
- pool_sizes[:num_fetchers].should eq(options[:processors] / 10)
26
- pool_sizes[:num_batches].should eq(options[:processors] / 10)
27
- pool_sizes[:num_deleters].should eq(options[:processors] / 10)
23
+ config = Sqskiq.valid_config_from(options)
24
+ config[:num_workers].should eq(options[:processors])
25
+ config[:num_fetchers].should eq(options[:processors] / 10)
26
+ config[:num_batches].should eq(options[:processors] / 10)
27
+ config[:num_deleters].should eq(options[:processors] / 10)
28
28
  end
29
29
 
30
30
  end
@@ -33,11 +33,11 @@ describe Sqskiq do
33
33
  let(:options) { { processors: [ 21, 31, 41 ].sample } }
34
34
 
35
35
  it 'uses the the given value for the processors and apply (processors / 10) + 1 for other pool sizes' do
36
- pool_sizes = Sqskiq.pool_sizes(options)
37
- pool_sizes[:num_workers].should eq(options[:processors])
38
- pool_sizes[:num_fetchers].should eq((options[:processors] / 10) + 1)
39
- pool_sizes[:num_batches].should eq((options[:processors] / 10) + 1)
40
- pool_sizes[:num_deleters].should eq((options[:processors] / 10) + 1)
36
+ config = Sqskiq.valid_config_from(options)
37
+ config[:num_workers].should eq(options[:processors])
38
+ config[:num_fetchers].should eq((options[:processors] / 10) + 1)
39
+ config[:num_batches].should eq((options[:processors] / 10) + 1)
40
+ config[:num_deleters].should eq((options[:processors] / 10) + 1)
41
41
  end
42
42
 
43
43
  end
@@ -46,11 +46,11 @@ describe Sqskiq do
46
46
  let(:options) { { processors: Random.rand(2..10) } }
47
47
 
48
48
  it 'uses the the given value for the processors and apply (processors / 10) for other pool sizes' do
49
- pool_sizes = Sqskiq.pool_sizes(options)
50
- pool_sizes[:num_workers].should eq(options[:processors])
51
- pool_sizes[:num_fetchers].should eq((options[:processors] / 10) + 2)
52
- pool_sizes[:num_batches].should eq((options[:processors] / 10) + 2)
53
- pool_sizes[:num_deleters].should eq((options[:processors] / 10) + 2)
49
+ config = Sqskiq.valid_config_from(options)
50
+ config[:num_workers].should eq(options[:processors])
51
+ config[:num_fetchers].should eq(2)
52
+ config[:num_batches].should eq(2)
53
+ config[:num_deleters].should eq(2)
54
54
  end
55
55
 
56
56
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sqskiq
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.3
4
+ version: 0.0.4
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors: