sqskiq 0.0.1 → 0.0.2

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.
@@ -1,21 +1,19 @@
1
1
  require 'celluloid'
2
2
  require 'celluloid/autostart'
3
+ require 'sqskiq/signal_handler'
3
4
 
4
5
  module Sqskiq
5
6
  class BatchProcessor
6
7
  include Celluloid
7
- include Celluloid::Notifications
8
-
8
+ include Sqskiq::SignalHandler
9
+
9
10
  def initialize
10
11
  @manager = Celluloid::Actor[:manager]
11
12
  @processor = Celluloid::Actor[:processor]
12
-
13
- subscribe_interrupt
13
+ subscribe_for_shutdown
14
14
  end
15
15
 
16
16
  def batch_process(messages)
17
- p "processing #{messages.size} messages"
18
-
19
17
  process_result = []
20
18
  messages.each do |message|
21
19
  process_result << @processor.future.process(message)
@@ -23,24 +21,17 @@ module Sqskiq
23
21
 
24
22
  success_messages = []
25
23
  process_result.each do |result|
26
- value = result.value
27
- if value[:success]
28
- success_messages << value[:message]
24
+
25
+ unless @shutting_down
26
+ value = result.value
27
+ if value[:success]
28
+ success_messages << value[:message]
29
+ end
29
30
  end
30
31
  end
31
32
 
32
33
  @manager.async.batch_process_done(success_messages)
33
34
  end
34
-
35
- def subscribe_interrupt
36
- subscribe('SIGINT', :interrupt)
37
- subscribe('TERM', :interrupt)
38
- subscribe('SIGTERM', :interrupt)
39
- end
40
-
41
- def interrupt(signal)
42
- self.terminate
43
- end
44
-
45
35
  end
36
+
46
37
  end
data/lib/sqskiq/delete.rb CHANGED
@@ -5,7 +5,6 @@ require 'sqskiq/aws'
5
5
  module Sqskiq
6
6
  class Deleter
7
7
  include Celluloid
8
- include Celluloid::Notifications
9
8
  include Sqskiq::AWS
10
9
 
11
10
  def initialize(aws_access_key_id, aws_secret_access_key, queue_name)
data/lib/sqskiq/fetch.rb CHANGED
@@ -5,13 +5,10 @@ require 'sqskiq/aws'
5
5
  module Sqskiq
6
6
  class Fetcher
7
7
  include Celluloid
8
- include Celluloid::Notifications
9
8
  include Sqskiq::AWS
10
9
 
11
10
  def initialize(aws_access_key_id, aws_secret_access_key, queue_name)
12
11
  init_queue(aws_access_key_id, aws_secret_access_key, queue_name)
13
- subscribe_interrupt
14
-
15
12
  @manager = Celluloid::Actor[:manager]
16
13
  end
17
14
 
@@ -20,15 +17,5 @@ module Sqskiq
20
17
  @manager.async.fetch_done(messages)
21
18
  end
22
19
 
23
- def subscribe_interrupt
24
- subscribe('SIGINT', :interrupt)
25
- subscribe('SIGTERM', :interrupt)
26
- subscribe('TERM', :interrupt)
27
- end
28
-
29
- def interrupt(signal)
30
- self.terminate
31
- end
32
-
33
20
  end
34
21
  end
@@ -1,23 +1,22 @@
1
1
  require 'celluloid'
2
2
  require 'celluloid/autostart'
3
-
3
+ require 'sqskiq/signal_handler'
4
4
 
5
5
  module Sqskiq
6
6
  class Manager
7
7
  include Celluloid
8
- include Celluloid::Notifications
9
-
8
+ include Sqskiq::SignalHandler
9
+
10
10
  def initialize
11
- @shutting_down = false
12
- subscribe_shutting_down
11
+ subscribe_for_shutdown
13
12
  end
14
13
 
15
14
  def bootstrap
16
15
  @fetcher = Celluloid::Actor[:fetcher]
17
16
  @batch_processor = Celluloid::Actor[:batch_processor]
18
17
  @deleter = Celluloid::Actor[:deleter]
19
- #TODO default value. Should be configurable
20
- new_fetch(2)
18
+
19
+ new_fetch(@fetcher.size)
21
20
  end
22
21
 
23
22
  def fetch_done(messages)
@@ -25,7 +24,7 @@ module Sqskiq
25
24
  end
26
25
 
27
26
  def batch_process_done(messages)
28
- @deleter.async.delete(messages) if @deleter.alive?
27
+ @deleter.async.delete(messages)
29
28
  new_fetch(1)
30
29
  end
31
30
 
@@ -33,18 +32,9 @@ module Sqskiq
33
32
  num.times { @fetcher.async.fetch unless @shutting_down }
34
33
  end
35
34
 
36
- def shutting_down(signal)
37
- @shutting_down = true
38
- end
39
-
40
35
  def running?
41
- not (@deleter.busy_size == 0 and @shutting_down)
36
+ not (@shutting_down and @deleter.busy_size == 0 and @batch_processor.busy_size == 0)
42
37
  end
43
38
 
44
- def subscribe_shutting_down
45
- subscribe('SIGINT', :shutting_down)
46
- subscribe('TERM', :shutting_down)
47
- subscribe('SIGTERM', :shutting_down)
48
- end
49
39
  end
50
40
  end
@@ -1,36 +1,28 @@
1
1
  require 'celluloid'
2
2
  require 'celluloid/autostart'
3
+ require 'sqskiq/signal_handler'
3
4
 
4
5
  module Sqskiq
5
6
  class Processor
6
7
  include Celluloid
7
- include Celluloid::Notifications
8
+ include Sqskiq::SignalHandler
8
9
 
9
10
  def initialize(worker_class)
10
11
  @worker_instance = worker_class.new
11
-
12
- subscribe_interrupt
12
+ subscribe_for_shutdown
13
13
  end
14
14
 
15
15
  def process(message)
16
+ return { :success => false, :message => message } if @shutting_down
17
+
16
18
  result = true
17
19
  begin
18
20
  @worker_instance.perform(message)
19
21
  rescue Exception => e
20
22
  result = false
21
23
  end
22
- { :success => result, :message => message }
23
- end
24
-
25
- def subscribe_interrupt
26
- subscribe('SIGINT', :interrupt)
27
- subscribe('TERM', :interrupt)
28
- subscribe('SIGTERM', :interrupt)
24
+ return { :success => result, :message => message }
29
25
  end
30
-
31
- def interrupt(signal)
32
- self.terminate
33
- end
34
-
26
+
35
27
  end
36
28
  end
data/lib/sqskiq/worker.rb CHANGED
@@ -1,3 +1,5 @@
1
+ require 'active_support/core_ext/class/attribute'
2
+
1
3
  module Sqskiq
2
4
  module Worker
3
5
 
data/lib/sqskiq.rb CHANGED
@@ -8,28 +8,17 @@ require 'sqskiq/batch_process'
8
8
  module Sqskiq
9
9
 
10
10
  def self.bootstrap(options, worker_class)
11
-
12
11
  params = [ @aws_access_key_id, @aws_secret_access_key, options[:queue_name] ]
13
-
12
+
13
+ configured_pool_sizes = pool_sizes(options)
14
+
14
15
  Celluloid::Actor[:manager] = @manager = Manager.new
15
- Celluloid::Actor[:fetcher] = @fetcher = Fetcher.pool(:size => 2, :args => params)
16
- Celluloid::Actor[:processor] = @processor = Processor.pool(:size => 20, :args => worker_class)
17
- Celluloid::Actor[:batch_processor] = @batch_processor = BatchProcessor.pool(:size => 2)
18
- Celluloid::Actor[:deleter] = @deleter = Deleter.pool(:size => 2, :args => params)
19
-
20
- p "pid = #{Process.pid}"
21
-
22
- trap('SIGTERM') do
23
- @manager.publish('SIGTERM')
24
- end
25
-
26
- trap('TERM') do
27
- @manager.publish('TERM')
28
- end
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)
29
20
 
30
- trap('SIGINT') do
31
- @manager.publish('SIGINT')
32
- end
21
+ configure_signal_listeners
33
22
 
34
23
  @manager.bootstrap
35
24
  while @manager.running? do
@@ -43,7 +32,35 @@ module Sqskiq
43
32
 
44
33
  @manager.terminate
45
34
  end
46
-
35
+
36
+ def self.configure_signal_listeners
37
+ ['SIGTERM', 'TERM', 'SIGINT'].each do |signal|
38
+ trap(signal) do
39
+ @manager.publish('SIGTERM')
40
+ @batch_processor.publish('SIGTERM')
41
+ @processor.publish('SIGTERM')
42
+ end
43
+ end
44
+ end
45
+
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!
55
+ num_fetchers = num_workers / 10
56
+ num_fetchers = num_fetchers + 1 if num_workers % 10 > 0
57
+ num_fetchers = 2 if num_fetchers < 2
58
+
59
+ num_deleters = num_batches = num_fetchers
60
+
61
+ { num_workers: num_workers, num_fetchers: num_fetchers, num_batches: num_batches, num_deleters: num_deleters }
62
+ end
63
+
47
64
  def self.configure
48
65
  yield self
49
66
  end
@@ -0,0 +1,70 @@
1
+ require 'spec_helper'
2
+
3
+ describe Sqskiq::Manager do
4
+ let(:deleter) { Object.new }
5
+ let(:fetcher) { Object.new }
6
+ let(:batch_processor) { Object.new }
7
+ let(:shutting_down) { false }
8
+ subject = described_class.new
9
+
10
+ before do
11
+ subject.instance_variable_set(:@fetcher, fetcher)
12
+ subject.instance_variable_set(:@deleter, deleter)
13
+ subject.instance_variable_set(:@batch_processor, batch_processor)
14
+ subject.instance_variable_set(:@shutting_down, shutting_down)
15
+ end
16
+
17
+ describe '#running?' do
18
+
19
+ context 'when the actor system is shutting down' do
20
+ let(:shutting_down) { true }
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
29
+
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
35
+
36
+ it 'returns true' do
37
+ subject.running?.should be_true
38
+ end
39
+ end
40
+
41
+ describe 'if batch_processor and deleter are empties' do
42
+ before do
43
+ deleter.should_receive(:busy_size).and_return(0)
44
+ batch_processor.should_receive(:busy_size).and_return(0)
45
+ end
46
+
47
+ it 'returns false' do
48
+ subject.running?.should be_false
49
+ end
50
+ end
51
+
52
+ end
53
+
54
+ context 'when the actor system is not shutting down' do
55
+
56
+ describe 'even if batch_processor and deleter are empties' do
57
+ before do
58
+ deleter.stub(:busy_size).and_return(0)
59
+ batch_processor.stub(:busy_size).and_return(0)
60
+ end
61
+
62
+ it 'returns true' do
63
+ subject.running?.should be_true
64
+ end
65
+ end
66
+
67
+ end
68
+ end
69
+
70
+ end
@@ -0,0 +1,14 @@
1
+ require 'spec_helper'
2
+
3
+ describe Sqskiq::Processor do
4
+ let(:worker) { double }
5
+ subject { described_class.new double(new: worker) }
6
+
7
+ let(:message) { double(body: 'hello!') }
8
+
9
+ context "Error handling" do
10
+ before { worker.stub(:perform) { raise Exception.new "OMG!" } }
11
+
12
+ it { expect(subject.process(message)[:success]).to be_false }
13
+ end
14
+ end
@@ -0,0 +1 @@
1
+ require 'sqskiq'
@@ -0,0 +1,60 @@
1
+ require 'spec_helper'
2
+
3
+ describe Sqskiq do
4
+
5
+ describe 'number of processors is lesser than 2' do
6
+ let(:options) { [ { processors: 1 }, ].sample }
7
+
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)
14
+ end
15
+ end
16
+
17
+ describe 'number of processor is greater than 2' do
18
+
19
+ describe 'with nothing remaining after performing division by 10' do
20
+ let(:options) { { processors: [ 20, 30, 40 ].sample } }
21
+
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)
28
+ end
29
+
30
+ end
31
+
32
+ describe 'with remaining value after performing division by 10' do
33
+ let(:options) { { processors: [ 21, 31, 41 ].sample } }
34
+
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)
41
+ end
42
+
43
+ end
44
+
45
+ describe 'and lesser or equals to 10' do
46
+ let(:options) { { processors: Random.rand(2..10) } }
47
+
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)
54
+ end
55
+
56
+ end
57
+ end
58
+
59
+
60
+ 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.1
4
+ version: 0.0.2
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -43,6 +43,38 @@ dependencies:
43
43
  - - ~>
44
44
  - !ruby/object:Gem::Version
45
45
  version: 1.9.1
46
+ - !ruby/object:Gem::Dependency
47
+ name: activesupport
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ! '>='
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ type: :runtime
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ - !ruby/object:Gem::Dependency
63
+ name: rspec
64
+ requirement: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ! '>='
68
+ - !ruby/object:Gem::Version
69
+ version: '0'
70
+ type: :development
71
+ prerelease: false
72
+ version_requirements: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ! '>='
76
+ - !ruby/object:Gem::Version
77
+ version: '0'
46
78
  description: sqskiq
47
79
  email: ri.vanlazar@gmail.com
48
80
  executables: []
@@ -57,6 +89,10 @@ files:
57
89
  - lib/sqskiq/worker.rb
58
90
  - lib/sqskiq/batch_process.rb
59
91
  - lib/sqskiq/aws.rb
92
+ - spec/manager_spec.rb
93
+ - spec/processor_spec.rb
94
+ - spec/spec_helper.rb
95
+ - spec/sqskiq_spec.rb
60
96
  homepage: http://rubygems.org/gems/sqskiq
61
97
  licenses: []
62
98
  post_install_message:
@@ -81,4 +117,8 @@ rubygems_version: 1.8.25
81
117
  signing_key:
82
118
  specification_version: 3
83
119
  summary: sqskiq
84
- test_files: []
120
+ test_files:
121
+ - spec/manager_spec.rb
122
+ - spec/processor_spec.rb
123
+ - spec/spec_helper.rb
124
+ - spec/sqskiq_spec.rb