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 +4 -4
- data/Gemfile.lock +6 -6
- data/lib/toiler/actor/fetcher.rb +50 -47
- data/lib/toiler/actor/processor.rb +6 -10
- data/lib/toiler/actor/supervisor.rb +5 -8
- data/lib/toiler/cli.rb +1 -1
- data/lib/toiler/version.rb +1 -1
- data/spec/models/fetcher_spec.rb +3 -3
- data/spec/models/processor_spec.rb +1 -0
- data/spec/models/supervisor_spec.rb +1 -1
- metadata +5 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 1defeb7538e17ae38a0028878f07de278ad6fc34
|
4
|
+
data.tar.gz: 8a233a42c1776663ce3dd04d5f22e318587de752
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
-
aws-sdk-sqs (
|
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.
|
15
|
-
aws-sdk-core (3.
|
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.
|
21
|
-
aws-sdk-core (~> 3
|
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)
|
data/lib/toiler/actor/fetcher.rb
CHANGED
@@ -10,38 +10,49 @@ module Toiler
|
|
10
10
|
FETCH_LIMIT = 10
|
11
11
|
|
12
12
|
attr_accessor :queue, :wait, :visibility_timeout, :free_processors,
|
13
|
-
:
|
13
|
+
:scheduled, :executing, :polling
|
14
14
|
|
15
|
-
def initialize(queue, client
|
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] ||
|
19
|
-
@free_processors =
|
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
|
-
@
|
23
|
-
@
|
24
|
-
@
|
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.
|
29
|
+
Concurrent.global_io_executor
|
31
30
|
end
|
32
31
|
|
33
32
|
def on_message(msg)
|
34
|
-
|
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
|
-
|
39
|
+
executing.make_false
|
41
40
|
end
|
42
41
|
|
43
42
|
def executing?
|
44
|
-
|
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
|
-
|
56
|
-
|
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
|
74
|
+
def poll_future
|
75
|
+
|
64
76
|
Concurrent::Promises.future do
|
65
|
-
queue.receive_messages
|
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:
|
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
|
77
|
-
|
78
|
-
|
79
|
-
|
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
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
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
|
103
|
-
|
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
|
-
|
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
|
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
|
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
|
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,
|
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
|
-
|
43
|
-
|
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?}] [
|
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
|
data/lib/toiler/version.rb
CHANGED
data/spec/models/fetcher_spec.rb
CHANGED
@@ -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
|
18
|
-
expect(fetcher).to
|
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
|
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
|
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-
|
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:
|
141
|
+
version: '0'
|
142
142
|
requirements: []
|
143
143
|
rubyforge_project:
|
144
|
-
rubygems_version: 2.
|
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
|