toiler 0.5.1 → 0.6.0.pre1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 1defeb7538e17ae38a0028878f07de278ad6fc34
4
- data.tar.gz: 8a233a42c1776663ce3dd04d5f22e318587de752
3
+ metadata.gz: f3b78eabd0468862d06d5a6755a6cef9abed5200
4
+ data.tar.gz: 75bcd6cf425bab5ab8bf021bb6dd9c59bc2a9a2c
5
5
  SHA512:
6
- metadata.gz: bc096c97a2efca04256aaf8d7895d73bde781b2a6c961de35a0a188b7db14a92e0eaabb970040f37a8e1063b8de8801f3bae6fd6d38e4fb42937a9f9e9a06acb
7
- data.tar.gz: 41dc4911884dc11ac9df2b1e55d438effda0c9a752b916699e568e206e3ac6fca5cae0d0551ac306f1aa35dc93f1fc7353e17a873f53516f502da8f38a131a8f
6
+ metadata.gz: 3b710c1dbaf98523837a27eca2cd2ff4a35745846753828e5286cf3830ae066bf08344680bc741cd5dbe30f53a3a7170a55a13ebc8e861dd47c6afc8e2715299
7
+ data.tar.gz: f11cb35378dac421c0221f7e1ca37a562b31ff89ab9a1ca5bc5de6ed043c0ed234b4eed4447e1c938d07fbf241bd70f59f25d6376e24d9d16607d30110efa86c
@@ -1,8 +1,8 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- toiler (0.4.3)
5
- aws-sdk-sqs (~> 1.0, >= 1.0.0)
4
+ toiler (0.5.1.pre7)
5
+ aws-sdk-sqs (>= 1.0.0, < 2.0.0)
6
6
  concurrent-ruby (~> 1.0, >= 1.0.0)
7
7
  concurrent-ruby-edge (~> 0.3, >= 0.3)
8
8
 
@@ -11,14 +11,14 @@ GEM
11
11
  specs:
12
12
  ast (2.4.0)
13
13
  aws-eventstream (1.0.1)
14
- aws-partitions (1.100.0)
15
- aws-sdk-core (3.24.1)
14
+ aws-partitions (1.105.0)
15
+ aws-sdk-core (3.31.0)
16
16
  aws-eventstream (~> 1.0)
17
17
  aws-partitions (~> 1.0)
18
18
  aws-sigv4 (~> 1.0)
19
19
  jmespath (~> 1.0)
20
- aws-sdk-sqs (1.4.0)
21
- aws-sdk-core (~> 3)
20
+ aws-sdk-sqs (1.7.0)
21
+ aws-sdk-core (~> 3, >= 3.26.0)
22
22
  aws-sigv4 (~> 1.0)
23
23
  aws-sigv4 (1.0.3)
24
24
  concurrent-ruby (1.0.5)
@@ -10,49 +10,38 @@ module Toiler
10
10
  FETCH_LIMIT = 10
11
11
 
12
12
  attr_accessor :queue, :wait, :visibility_timeout, :free_processors,
13
- :scheduled, :executing, :polling
13
+ :executing, :waiting_messages, :concurrency
14
14
 
15
- def initialize(queue, client)
15
+ def initialize(queue, client, count)
16
16
  debug "Initializing Fetcher for queue #{queue}..."
17
17
  @queue = Toiler::Aws::Queue.new queue, client
18
- @wait = Toiler.options[:wait] || 20
19
- @free_processors = Concurrent::AtomicFixnum.new(0)
18
+ @wait = Toiler.options[:wait] || 60
19
+ @free_processors = count
20
20
  @batch = Toiler.worker_class_registry[queue].batch?
21
21
  @visibility_timeout = @queue.visibility_timeout
22
- @scheduled = Concurrent::AtomicBoolean.new
23
- @executing = Concurrent::AtomicBoolean.new
24
- @polling = Concurrent::AtomicBoolean.new
22
+ @executing = false
23
+ @waiting_messages = 0
24
+ @concurrency = count
25
25
  debug "Finished initializing Fetcher for queue #{queue}"
26
+ tell :poll_messages
26
27
  end
27
28
 
28
29
  def default_executor
29
- Concurrent.global_io_executor
30
+ Concurrent.global_fast_executor
30
31
  end
31
32
 
32
33
  def on_message(msg)
33
- executing.make_true
34
+ @executing = true
34
35
  method, *args = msg
35
36
  send(method, *args)
36
37
  rescue StandardError => e
37
38
  error "Fetcher #{queue.name} raised exception #{e.class}: #{e.message}\n#{e.backtrace.join("\n")}"
38
39
  ensure
39
- executing.make_false
40
+ @executing = false
40
41
  end
41
42
 
42
43
  def executing?
43
- executing.value
44
- end
45
-
46
- def polling?
47
- polling.value
48
- end
49
-
50
- def scheduled?
51
- scheduled.value
52
- end
53
-
54
- def get_free_processors
55
- free_processors.value
44
+ @executing
56
45
  end
57
46
 
58
47
  private
@@ -63,46 +52,56 @@ module Toiler
63
52
 
64
53
  def processor_finished
65
54
  debug "Fetcher #{queue.name} received processor finished signal..."
66
- free_processors.increment
67
- schedule_poll
55
+ @free_processors += 1
56
+ tell :poll_messages
68
57
  end
69
58
 
70
59
  def max_messages
71
- batch? ? FETCH_LIMIT : [FETCH_LIMIT, free_processors.value].min
60
+ batch? ? FETCH_LIMIT : [FETCH_LIMIT, free_processors].min
72
61
  end
73
62
 
74
- def poll_future
75
-
63
+ def poll_future(max_number_of_messages)
76
64
  Concurrent::Promises.future do
77
- queue.receive_messages attribute_names: %w(All),
78
- message_attribute_names: %w(All),
65
+ queue.receive_messages attribute_names: %w[All],
66
+ message_attribute_names: %w[All],
79
67
  wait_time_seconds: wait,
80
- max_number_of_messages: max_messages
68
+ max_number_of_messages: max_number_of_messages
81
69
  end
82
70
  end
83
71
 
72
+ def release_messages(messages)
73
+ @waiting_messages -= messages
74
+ end
75
+
84
76
  def poll_messages
85
- return unless polling.make_true
86
- poll_future.on_rejection! do
87
- polling.make_false
88
- scheduled.make_false
89
- tell :schedule_poll
77
+ return unless should_poll?
78
+
79
+ max_number_of_messages = max_messages
80
+ return if waiting_messages > 0 && !full_batch?(max_number_of_messages)
81
+
82
+ @waiting_messages += max_number_of_messages
83
+
84
+ debug "Fetcher #{queue.name} polling messages..."
85
+ future = poll_future max_number_of_messages
86
+ future.on_rejection! do
87
+ tell [:release_messages, max_number_of_messages]
88
+ tell :poll_messages
90
89
  end
91
- poll_future.on_fulfillment! do |msgs|
92
- polling.make_false
93
- scheduled.make_false
94
- if !msgs.nil? && !msgs.empty?
95
- tell [:assign_messages, msgs]
96
- else
97
- tell :schedule_poll
98
- end
90
+ future.on_fulfillment! do |msgs|
91
+ tell [:assign_messages, msgs] if !msgs.nil? && !msgs.empty?
92
+ tell [:release_messages, max_number_of_messages]
93
+ tell :poll_messages
99
94
  end
95
+
96
+ poll_messages if should_poll?
100
97
  end
101
98
 
102
- def schedule_poll
103
- return unless free_processors.value > 0 && scheduled.make_true
104
- debug "Fetcher #{queue.name} scheduling polling..."
105
- tell :poll_messages
99
+ def should_poll?
100
+ free_processors / 2 > waiting_messages
101
+ end
102
+
103
+ def full_batch?(max_number_of_messages)
104
+ max_number_of_messages == FETCH_LIMIT || max_number_of_messages >= concurrency * 0.1
106
105
  end
107
106
 
108
107
  def processor_pool
@@ -113,10 +112,9 @@ module Toiler
113
112
  messages = [messages] if batch?
114
113
  messages.each do |m|
115
114
  processor_pool.tell [:process, visibility_timeout, m]
116
- free_processors.decrement
115
+ @free_processors -= 1
117
116
  end
118
117
  debug "Fetcher #{queue.name} assigned #{messages.count} messages"
119
- tell :schedule_poll
120
118
  end
121
119
  end
122
120
  end
@@ -13,17 +13,19 @@ module Toiler
13
13
  def initialize(queue)
14
14
  @queue = queue
15
15
  @worker_class = Toiler.worker_class_registry[queue]
16
- @fetcher = Toiler.fetcher queue
17
16
  @executing = Concurrent::AtomicBoolean.new
18
17
  @thread = nil
19
18
  init_options
20
- processor_finished
21
19
  end
22
20
 
23
21
  def default_executor
24
22
  Concurrent.global_io_executor
25
23
  end
26
24
 
25
+ def fetcher
26
+ @fetcher ||= Toiler.fetcher queue
27
+ end
28
+
27
29
  def on_message(msg)
28
30
  method, *args = msg
29
31
  send(method, *args)
@@ -89,14 +91,16 @@ module Toiler
89
91
 
90
92
  def visibility_extender(queue_visibility, sqs_msg, body)
91
93
  return unless auto_visibility_timeout?
92
- interval = [1,queue_visibility/3].max
94
+
95
+ interval = [1, queue_visibility / 3].max
93
96
  Concurrent::TimerTask.execute execution_interval: interval,
94
- timeout_interval: interval do
97
+ timeout_interval: interval do |task|
95
98
  begin
96
99
  sqs_msg.visibility_timeout = queue_visibility
97
100
  yield sqs_msg, body if block_given?
98
101
  rescue StandardError => e
99
- error "Processor #{queue} failed to extend visibility of message: #{e.message}\n#{e.backtrace.join("\n")}"
102
+ error "Processor #{queue} failed to extend visibility of message - #{e.class}: #{e.message}\n#{e.backtrace.join("\n")}"
103
+ task.shutdown if e.message.include?('ReceiptHandle is invalid')
100
104
  end
101
105
  end
102
106
  end
@@ -116,7 +120,7 @@ module Toiler
116
120
  when :text, nil then sqs_msg.body
117
121
  else body_parser.load sqs_msg.body
118
122
  end
119
- rescue => e
123
+ rescue StandardError => e
120
124
  raise "Error parsing the message body: #{e.message}"
121
125
  end
122
126
  end
@@ -11,8 +11,8 @@ module Toiler
11
11
 
12
12
  def initialize
13
13
  @client = ::Aws::SQS::Client.new
14
- spawn_fetchers
15
14
  spawn_processors
15
+ spawn_fetchers
16
16
  end
17
17
 
18
18
  def on_message(_msg)
@@ -20,10 +20,12 @@ module Toiler
20
20
  end
21
21
 
22
22
  def spawn_fetchers
23
- Toiler.active_worker_class_registry.each do |queue, _klass|
23
+ Toiler.active_worker_class_registry.each do |queue, klass|
24
+ count = klass.concurrency
24
25
  begin
25
26
  fetcher = Actor::Fetcher.spawn! name: "fetcher_#{queue}".to_sym,
26
- supervise: true, args: [queue, client]
27
+ supervise: true,
28
+ args: [queue, client, count]
27
29
  Toiler.set_fetcher queue, fetcher
28
30
  rescue StandardError => e
29
31
  error "Failed to start Fetcher for queue #{queue}: #{e.message}\n#{e.backtrace.join("\n")}"
@@ -37,8 +39,9 @@ module Toiler
37
39
  count = klass.concurrency
38
40
  begin
39
41
  pool = Concurrent::Actor::Utils::Pool.spawn! name, count do |index|
40
- Actor::Processor.spawn name: "processor_#{queue}_#{index}".to_sym,
41
- supervise: true, args: [queue]
42
+ Actor::Processor.spawn name: "processor_#{queue}_#{index}".to_sym,
43
+ supervise: true,
44
+ args: [queue]
42
45
  end
43
46
  Toiler.set_processor_pool queue, pool
44
47
  rescue StandardError => e
@@ -99,7 +99,7 @@ module Toiler
99
99
  processors = processor_pool.instance_variable_get(:@workers).collect{|w| w.send(:core).send(:context)}
100
100
  busy_processors = processors.count{|pr| pr.executing?}
101
101
  message = "Status for [queue:#{queue}]:"
102
- message += "\n[fetcher:#{fetcher.name}] [executing:#{fetcher.executing?}] [polling:#{fetcher.polling?}] [scheduled:#{fetcher.scheduled?}] [free_processors:#{fetcher.get_free_processors}]"
102
+ message += "\n[fetcher:#{fetcher.name}] [executing:#{fetcher.executing?}] [waiting_messages:#{fetcher.waiting_messages}] [free_processors:#{fetcher.free_processors}]"
103
103
  message += "\n[processor_pool:#{processor_pool.name}] [workers:#{processors.count}] [busy:#{busy_processors}]"
104
104
  processors.each do |processor|
105
105
  thread = processor.thread
@@ -1,4 +1,4 @@
1
1
  # Toiler Version
2
2
  module Toiler
3
- VERSION = '0.5.1'.freeze
3
+ VERSION = '0.6.0.pre1'.freeze
4
4
  end
@@ -7,16 +7,16 @@ RSpec.describe Toiler::Actor::Fetcher, type: :model do
7
7
 
8
8
  before do
9
9
  allow_any_instance_of(Toiler::Actor::Fetcher).to receive(:log).and_return(true)
10
+ allow_any_instance_of(Toiler::Actor::Fetcher).to receive(:tell)
10
11
  allow_any_instance_of(Toiler::Aws::Queue).to receive(:visibility_timeout).and_return(100)
11
12
  allow(client).to receive(:get_queue_url).with(queue_name: 'default').and_return double(:queue, queue_url: 'http://aws.fake/queue')
12
13
  end
13
14
 
14
15
  describe "#new" do
15
16
  it 'completes sucessfully' do
16
- fetcher = described_class.new(queue, client)
17
- expect(fetcher.polling?).to eq(false)
17
+ fetcher = described_class.new(queue, client, 1)
18
+ expect(fetcher).to have_received(:tell).with(:poll_messages)
18
19
  expect(fetcher.executing?).to eq(false)
19
- expect(fetcher.scheduled?).to eq(false)
20
20
  end
21
21
  end
22
22
  end
@@ -7,7 +7,6 @@ RSpec.describe Toiler::Actor::Processor, type: :model do
7
7
  describe "#new" do
8
8
  it 'initializes properly' do
9
9
  allow(Toiler).to receive(:fetcher).and_return(fetcher)
10
- expect(fetcher).to receive(:tell).with(:processor_finished)
11
10
  processor = described_class.new('default')
12
11
  expect(processor.executing?).to eq(false)
13
12
  end
@@ -13,7 +13,7 @@ RSpec.describe Toiler::Actor::Supervisor, type: :model do
13
13
 
14
14
  Toiler.options.merge!(active_queues: ['default'])
15
15
  expect(::Aws::SQS::Client).to receive(:new).and_return(sqs_client)
16
- expect(Toiler::Actor::Fetcher).to receive(:spawn!).with(name: :fetcher_default, supervise: true, args: ['default', sqs_client])
16
+ expect(Toiler::Actor::Fetcher).to receive(:spawn!).with(name: :fetcher_default, supervise: true, args: ['default', sqs_client, 1])
17
17
  expect(Concurrent::Actor::Utils::Pool).to receive(:spawn!).with(:processor_pool_default, 1)
18
18
  supervisor = described_class.new
19
19
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: toiler
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.1
4
+ version: 0.6.0.pre1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sebastian Schepens
@@ -136,9 +136,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
136
136
  version: '0'
137
137
  required_rubygems_version: !ruby/object:Gem::Requirement
138
138
  requirements:
139
- - - ">="
139
+ - - ">"
140
140
  - !ruby/object:Gem::Version
141
- version: '0'
141
+ version: 1.3.1
142
142
  requirements: []
143
143
  rubyforge_project:
144
144
  rubygems_version: 2.5.2.1