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 +4 -4
- data/Gemfile.lock +6 -6
- data/lib/toiler/actor/fetcher.rb +48 -50
- data/lib/toiler/actor/processor.rb +10 -6
- data/lib/toiler/actor/supervisor.rb +8 -5
- 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 +0 -1
- data/spec/models/supervisor_spec.rb +1 -1
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f3b78eabd0468862d06d5a6755a6cef9abed5200
|
4
|
+
data.tar.gz: 75bcd6cf425bab5ab8bf021bb6dd9c59bc2a9a2c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3b710c1dbaf98523837a27eca2cd2ff4a35745846753828e5286cf3830ae066bf08344680bc741cd5dbe30f53a3a7170a55a13ebc8e861dd47c6afc8e2715299
|
7
|
+
data.tar.gz: f11cb35378dac421c0221f7e1ca37a562b31ff89ab9a1ca5bc5de6ed043c0ed234b4eed4447e1c938d07fbf241bd70f59f25d6376e24d9d16607d30110efa86c
|
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.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.
|
15
|
-
aws-sdk-core (3.
|
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.
|
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)
|
data/lib/toiler/actor/fetcher.rb
CHANGED
@@ -10,49 +10,38 @@ module Toiler
|
|
10
10
|
FETCH_LIMIT = 10
|
11
11
|
|
12
12
|
attr_accessor :queue, :wait, :visibility_timeout, :free_processors,
|
13
|
-
:
|
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] ||
|
19
|
-
@free_processors =
|
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
|
-
@
|
23
|
-
@
|
24
|
-
@
|
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.
|
30
|
+
Concurrent.global_fast_executor
|
30
31
|
end
|
31
32
|
|
32
33
|
def on_message(msg)
|
33
|
-
executing
|
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
|
40
|
+
@executing = false
|
40
41
|
end
|
41
42
|
|
42
43
|
def executing?
|
43
|
-
executing
|
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
|
67
|
-
|
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
|
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
|
78
|
-
message_attribute_names: %w
|
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:
|
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
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
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
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
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
|
103
|
-
|
104
|
-
|
105
|
-
|
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
|
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
|
-
|
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,
|
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,
|
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
|
-
|
41
|
-
|
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
|
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?}] [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
|
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)
|
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
|
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.
|
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:
|
141
|
+
version: 1.3.1
|
142
142
|
requirements: []
|
143
143
|
rubyforge_project:
|
144
144
|
rubygems_version: 2.5.2.1
|