toiler 0.5.1 → 0.6.0.pre1

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