sqskiq 0.0.3 → 0.0.4

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/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: