toiler 0.5.1.pre9 → 0.5.1

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: 61292f1454428dae50787cb722201108250b136b
4
- data.tar.gz: 004ab4f17d203ca40b4f4f74fac79ba52f0449cd
3
+ metadata.gz: 1defeb7538e17ae38a0028878f07de278ad6fc34
4
+ data.tar.gz: 8a233a42c1776663ce3dd04d5f22e318587de752
5
5
  SHA512:
6
- metadata.gz: d9a8db8fcfd8c5986cf096a4d8f598bdef0a8a35f8da739fdecce50f6dc8976a822cd86f6f5caf8ad4d3d51257eb4fc5591dca29a8b91acc42978af8451711b5
7
- data.tar.gz: 1c2552b393d6fde181b4d81fa80a971aa8f2076c44f3f556da2957a9453a5d13df8abaa0a1f90facc097a072592646033f4391682a12756001a5e38c736e2063
6
+ metadata.gz: bc096c97a2efca04256aaf8d7895d73bde781b2a6c961de35a0a188b7db14a92e0eaabb970040f37a8e1063b8de8801f3bae6fd6d38e4fb42937a9f9e9a06acb
7
+ data.tar.gz: 41dc4911884dc11ac9df2b1e55d438effda0c9a752b916699e568e206e3ac6fca5cae0d0551ac306f1aa35dc93f1fc7353e17a873f53516f502da8f38a131a8f
data/Gemfile.lock CHANGED
@@ -1,8 +1,8 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- toiler (0.5.1.pre7)
5
- aws-sdk-sqs (>= 1.0.0, < 2.0.0)
4
+ toiler (0.4.3)
5
+ aws-sdk-sqs (~> 1.0, >= 1.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.105.0)
15
- aws-sdk-core (3.31.0)
14
+ aws-partitions (1.100.0)
15
+ aws-sdk-core (3.24.1)
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.7.0)
21
- aws-sdk-core (~> 3, >= 3.26.0)
20
+ aws-sdk-sqs (1.4.0)
21
+ aws-sdk-core (~> 3)
22
22
  aws-sigv4 (~> 1.0)
23
23
  aws-sigv4 (1.0.3)
24
24
  concurrent-ruby (1.0.5)
@@ -10,38 +10,49 @@ module Toiler
10
10
  FETCH_LIMIT = 10
11
11
 
12
12
  attr_accessor :queue, :wait, :visibility_timeout, :free_processors,
13
- :executing, :waiting_messages, :concurrency
13
+ :scheduled, :executing, :polling
14
14
 
15
- def initialize(queue, client, count)
15
+ def initialize(queue, client)
16
16
  debug "Initializing Fetcher for queue #{queue}..."
17
17
  @queue = Toiler::Aws::Queue.new queue, client
18
- @wait = Toiler.options[:wait] || 60
19
- @free_processors = count
18
+ @wait = Toiler.options[:wait] || 20
19
+ @free_processors = Concurrent::AtomicFixnum.new(0)
20
20
  @batch = Toiler.worker_class_registry[queue].batch?
21
21
  @visibility_timeout = @queue.visibility_timeout
22
- @executing = false
23
- @waiting_messages = 0
24
- @concurrency = count
22
+ @scheduled = Concurrent::AtomicBoolean.new
23
+ @executing = Concurrent::AtomicBoolean.new
24
+ @polling = Concurrent::AtomicBoolean.new
25
25
  debug "Finished initializing Fetcher for queue #{queue}"
26
- tell :poll_messages
27
26
  end
28
27
 
29
28
  def default_executor
30
- Concurrent.global_fast_executor
29
+ Concurrent.global_io_executor
31
30
  end
32
31
 
33
32
  def on_message(msg)
34
- @executing = true
33
+ executing.make_true
35
34
  method, *args = msg
36
35
  send(method, *args)
37
36
  rescue StandardError => e
38
37
  error "Fetcher #{queue.name} raised exception #{e.class}: #{e.message}\n#{e.backtrace.join("\n")}"
39
38
  ensure
40
- @executing = false
39
+ executing.make_false
41
40
  end
42
41
 
43
42
  def executing?
44
- @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
45
56
  end
46
57
 
47
58
  private
@@ -52,55 +63,46 @@ module Toiler
52
63
 
53
64
  def processor_finished
54
65
  debug "Fetcher #{queue.name} received processor finished signal..."
55
- @free_processors += 1
56
- tell :poll_messages
66
+ free_processors.increment
67
+ schedule_poll
57
68
  end
58
69
 
59
70
  def max_messages
60
- batch? ? FETCH_LIMIT : [FETCH_LIMIT, free_processors].min
71
+ batch? ? FETCH_LIMIT : [FETCH_LIMIT, free_processors.value].min
61
72
  end
62
73
 
63
- def poll_future(max_number_of_messages)
74
+ def poll_future
75
+
64
76
  Concurrent::Promises.future do
65
- queue.receive_messages message_attribute_names: %w[All],
77
+ queue.receive_messages attribute_names: %w(All),
78
+ message_attribute_names: %w(All),
66
79
  wait_time_seconds: wait,
67
- max_number_of_messages: max_number_of_messages
80
+ max_number_of_messages: max_messages
68
81
  end
69
82
  end
70
83
 
71
- def release_messages(messages)
72
- @waiting_messages -= messages
73
- end
74
-
75
84
  def poll_messages
76
- return unless should_poll?
77
-
78
- max_number_of_messages = max_messages
79
- return if waiting_messages > 0 && !full_batch?(max_number_of_messages)
80
-
81
- @waiting_messages += max_number_of_messages
82
-
83
- debug "Fetcher #{queue.name} polling messages..."
84
- future = poll_future max_number_of_messages
85
- future.on_rejection! do
86
- tell [:release_messages, max_number_of_messages]
87
- tell :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
88
90
  end
89
- future.on_fulfillment! do |msgs|
90
- tell [:assign_messages, msgs] if !msgs.nil? && !msgs.empty?
91
- tell [:release_messages, max_number_of_messages]
92
- tell :poll_messages
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
93
99
  end
94
-
95
- poll_messages if should_poll?
96
- end
97
-
98
- def should_poll?
99
- free_processors / 2 > waiting_messages
100
100
  end
101
101
 
102
- def full_batch?(max_number_of_messages)
103
- max_number_of_messages == FETCH_LIMIT || max_number_of_messages >= concurrency * 0.1
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
104
106
  end
105
107
 
106
108
  def processor_pool
@@ -111,9 +113,10 @@ module Toiler
111
113
  messages = [messages] if batch?
112
114
  messages.each do |m|
113
115
  processor_pool.tell [:process, visibility_timeout, m]
114
- @free_processors -= 1
116
+ free_processors.decrement
115
117
  end
116
118
  debug "Fetcher #{queue.name} assigned #{messages.count} messages"
119
+ tell :schedule_poll
117
120
  end
118
121
  end
119
122
  end
@@ -13,19 +13,17 @@ 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
16
17
  @executing = Concurrent::AtomicBoolean.new
17
18
  @thread = nil
18
19
  init_options
20
+ processor_finished
19
21
  end
20
22
 
21
23
  def default_executor
22
24
  Concurrent.global_io_executor
23
25
  end
24
26
 
25
- def fetcher
26
- @fetcher ||= Toiler.fetcher queue
27
- end
28
-
29
27
  def on_message(msg)
30
28
  method, *args = msg
31
29
  send(method, *args)
@@ -91,16 +89,14 @@ module Toiler
91
89
 
92
90
  def visibility_extender(queue_visibility, sqs_msg, body)
93
91
  return unless auto_visibility_timeout?
94
-
95
- interval = [1, queue_visibility / 3].max
92
+ interval = [1,queue_visibility/3].max
96
93
  Concurrent::TimerTask.execute execution_interval: interval,
97
- timeout_interval: interval do |task|
94
+ timeout_interval: interval do
98
95
  begin
99
96
  sqs_msg.visibility_timeout = queue_visibility
100
97
  yield sqs_msg, body if block_given?
101
98
  rescue StandardError => e
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')
99
+ error "Processor #{queue} failed to extend visibility of message: #{e.message}\n#{e.backtrace.join("\n")}"
104
100
  end
105
101
  end
106
102
  end
@@ -120,7 +116,7 @@ module Toiler
120
116
  when :text, nil then sqs_msg.body
121
117
  else body_parser.load sqs_msg.body
122
118
  end
123
- rescue StandardError => e
119
+ rescue => e
124
120
  raise "Error parsing the message body: #{e.message}"
125
121
  end
126
122
  end
@@ -11,8 +11,8 @@ module Toiler
11
11
 
12
12
  def initialize
13
13
  @client = ::Aws::SQS::Client.new
14
- spawn_processors
15
14
  spawn_fetchers
15
+ spawn_processors
16
16
  end
17
17
 
18
18
  def on_message(_msg)
@@ -20,12 +20,10 @@ module Toiler
20
20
  end
21
21
 
22
22
  def spawn_fetchers
23
- Toiler.active_worker_class_registry.each do |queue, klass|
24
- count = klass.concurrency
23
+ Toiler.active_worker_class_registry.each do |queue, _klass|
25
24
  begin
26
25
  fetcher = Actor::Fetcher.spawn! name: "fetcher_#{queue}".to_sym,
27
- supervise: true,
28
- args: [queue, client, count]
26
+ supervise: true, args: [queue, client]
29
27
  Toiler.set_fetcher queue, fetcher
30
28
  rescue StandardError => e
31
29
  error "Failed to start Fetcher for queue #{queue}: #{e.message}\n#{e.backtrace.join("\n")}"
@@ -39,9 +37,8 @@ module Toiler
39
37
  count = klass.concurrency
40
38
  begin
41
39
  pool = Concurrent::Actor::Utils::Pool.spawn! name, count do |index|
42
- Actor::Processor.spawn name: "processor_#{queue}_#{index}".to_sym,
43
- supervise: true,
44
- args: [queue]
40
+ Actor::Processor.spawn name: "processor_#{queue}_#{index}".to_sym,
41
+ supervise: true, args: [queue]
45
42
  end
46
43
  Toiler.set_processor_pool queue, pool
47
44
  rescue StandardError => e
data/lib/toiler/cli.rb CHANGED
@@ -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?}] [waiting_messages:#{fetcher.waiting_messages}] [free_processors:#{fetcher.free_processors}]"
102
+ message += "\n[fetcher:#{fetcher.name}] [executing:#{fetcher.executing?}] [polling:#{fetcher.polling?}] [scheduled:#{fetcher.scheduled?}] [free_processors:#{fetcher.get_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.pre9'.freeze
3
+ VERSION = '0.5.1'.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)
11
10
  allow_any_instance_of(Toiler::Aws::Queue).to receive(:visibility_timeout).and_return(100)
12
11
  allow(client).to receive(:get_queue_url).with(queue_name: 'default').and_return double(:queue, queue_url: 'http://aws.fake/queue')
13
12
  end
14
13
 
15
14
  describe "#new" do
16
15
  it 'completes sucessfully' do
17
- fetcher = described_class.new(queue, client, 1)
18
- expect(fetcher).to have_received(:tell).with(:poll_messages)
16
+ fetcher = described_class.new(queue, client)
17
+ expect(fetcher.polling?).to eq(false)
19
18
  expect(fetcher.executing?).to eq(false)
19
+ expect(fetcher.scheduled?).to eq(false)
20
20
  end
21
21
  end
22
22
  end
@@ -7,6 +7,7 @@ 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)
10
11
  processor = described_class.new('default')
11
12
  expect(processor.executing?).to eq(false)
12
13
  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, 1])
16
+ expect(Toiler::Actor::Fetcher).to receive(:spawn!).with(name: :fetcher_default, supervise: true, args: ['default', sqs_client])
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,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: toiler
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.1.pre9
4
+ version: 0.5.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sebastian Schepens
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-10-25 00:00:00.000000000 Z
11
+ date: 2018-11-06 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rspec
@@ -136,12 +136,12 @@ 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: 1.3.1
141
+ version: '0'
142
142
  requirements: []
143
143
  rubyforge_project:
144
- rubygems_version: 2.6.11
144
+ rubygems_version: 2.5.2.1
145
145
  signing_key:
146
146
  specification_version: 4
147
147
  summary: Toiler is a super efficient AWS SQS thread based message processor