toiler 0.3.1.beta1 → 0.3.1.beta2

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.
@@ -1,104 +1,120 @@
1
- require 'toiler/actor/utils/actor_logging'
2
- require 'toiler/aws/queue'
3
-
4
- module Toiler
5
- module Actor
6
- # Actor polling for messages only when processors are ready, otherwise idle
7
- class Fetcher < Concurrent::Actor::RestartingContext
8
- include Utils::ActorLogging
9
-
10
- FETCH_LIMIT = 10
11
-
12
- attr_accessor :queue, :wait, :visibility_timeout, :free_processors,
13
- :scheduled, :executing
14
-
15
- def initialize(queue, client)
16
- debug "Initializing Fetcher for queue #{queue}..."
17
- @queue = Toiler::Aws::Queue.new queue, client
18
- @wait = Toiler.options[:wait] || 20
19
- @free_processors = Concurrent::AtomicFixnum.new(0)
20
- @batch = Toiler.worker_class_registry[queue].batch?
21
- @visibility_timeout = @queue.visibility_timeout
22
- @scheduled = Concurrent::AtomicBoolean.new
23
- @executing = Concurrent::AtomicBoolean.new
24
- debug "Finished initializing Fetcher for queue #{queue}"
25
- end
26
-
27
- def default_executor
28
- Concurrent.global_io_executor
29
- end
30
-
31
- def on_message(msg)
32
- method, *args = msg
33
- send(method, *args)
34
- rescue StandardError => e
35
- error "Fetcher #{queue.name} raised exception #{e.class}"
36
- end
37
-
38
- def executing?
39
- executing.value
40
- end
41
-
42
- def scheduled?
43
- scheduled.value
44
- end
45
-
46
- def get_free_processors
47
- free_processors.value
48
- end
49
-
50
- private
51
-
52
- def batch?
53
- @batch
54
- end
55
-
56
- def processor_finished
57
- debug "Fetcher #{queue.name} received processor finished signal..."
58
- free_processors.increment
59
- schedule_poll
60
- end
61
-
62
- def max_messages
63
- batch? ? FETCH_LIMIT : [FETCH_LIMIT, free_processors.value].min
64
- end
65
-
66
- def poll_future
67
- Concurrent.future do
68
- @executing.make_true
69
- queue.receive_messages message_attribute_names: %w(All),
70
- wait_time_seconds: wait,
71
- max_number_of_messages: max_messages
72
- @executing.make_false
73
- end
74
- end
75
-
76
- def poll_messages
77
- poll_future.on_completion! do |_success, msgs|
78
- tell [:assign_messages, msgs] unless msgs.nil? || msgs.empty?
79
- scheduled.make_false
80
- tell :schedule_poll
81
- end
82
- end
83
-
84
- def schedule_poll
85
- return unless free_processors.value > 0 && scheduled.make_true
86
- debug "Fetcher #{queue.name} scheduling polling..."
87
- tell :poll_messages
88
- end
89
-
90
- def processor_pool
91
- @processor_pool ||= Toiler.processor_pool queue.name
92
- end
93
-
94
- def assign_messages(messages)
95
- messages = [messages] if batch?
96
- messages.each do |m|
97
- processor_pool.tell [:process, visibility_timeout, m]
98
- free_processors.decrement
99
- end
100
- debug "Fetcher #{queue.name} assigned #{messages.count} messages"
101
- end
102
- end
103
- end
104
- end
1
+ require 'toiler/actor/utils/actor_logging'
2
+ require 'toiler/aws/queue'
3
+
4
+ module Toiler
5
+ module Actor
6
+ # Actor polling for messages only when processors are ready, otherwise idle
7
+ class Fetcher < Concurrent::Actor::RestartingContext
8
+ include Utils::ActorLogging
9
+
10
+ FETCH_LIMIT = 10
11
+
12
+ attr_accessor :queue, :wait, :visibility_timeout, :free_processors,
13
+ :scheduled, :executing
14
+
15
+ def initialize(queue, client)
16
+ debug "Initializing Fetcher for queue #{queue}..."
17
+ @queue = Toiler::Aws::Queue.new queue, client
18
+ @wait = Toiler.options[:wait] || 20
19
+ @free_processors = Concurrent::AtomicFixnum.new(0)
20
+ @batch = Toiler.worker_class_registry[queue].batch?
21
+ @visibility_timeout = @queue.visibility_timeout
22
+ @scheduled = Concurrent::AtomicBoolean.new
23
+ @executing = Concurrent::AtomicBoolean.new
24
+ @polling = Concurrent::AtomicBoolean.new
25
+ debug "Finished initializing Fetcher for queue #{queue}"
26
+ end
27
+
28
+ def default_executor
29
+ Concurrent.global_io_executor
30
+ end
31
+
32
+ def on_message(msg)
33
+ executing.make_true
34
+ method, *args = msg
35
+ send(method, *args)
36
+ rescue StandardError => e
37
+ error "Fetcher #{queue.name} raised exception #{e.class}"
38
+ ensure
39
+ executing.make_false
40
+ end
41
+
42
+ 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
56
+ end
57
+
58
+ private
59
+
60
+ def batch?
61
+ @batch
62
+ end
63
+
64
+ def processor_started
65
+ debug "Fetcher #{queue.name} received processor started signal..."
66
+ free_processors.decrement
67
+ end
68
+
69
+ def processor_finished
70
+ debug "Fetcher #{queue.name} received processor finished signal..."
71
+ free_processors.increment
72
+ schedule_poll
73
+ end
74
+
75
+ def max_messages
76
+ batch? ? FETCH_LIMIT : [FETCH_LIMIT, free_processors.value].min
77
+ end
78
+
79
+ def poll_future
80
+ Concurrent.future do
81
+ queue.receive_messages message_attribute_names: %w(All),
82
+ wait_time_seconds: wait,
83
+ max_number_of_messages: max_messages
84
+ end
85
+ end
86
+
87
+ def poll_messages
88
+ polling.make_true
89
+ poll_future.on_completion! do |success, msgs, error|
90
+ polling.make_false
91
+ scheduled.make_false
92
+ if success && !msgs.nil? && !msgs.empty?
93
+ tell [:assign_messages, msgs]
94
+ else
95
+ tell :schedule_poll
96
+ end
97
+ end
98
+ end
99
+
100
+ def schedule_poll
101
+ return unless free_processors.value > 0 && scheduled.make_true
102
+ debug "Fetcher #{queue.name} scheduling polling..."
103
+ tell :poll_messages
104
+ end
105
+
106
+ def processor_pool
107
+ @processor_pool ||= Toiler.processor_pool queue.name
108
+ end
109
+
110
+ def assign_messages(messages)
111
+ messages = [messages] if batch?
112
+ messages.each do |m|
113
+ processor_pool.tell [:process, visibility_timeout, m]
114
+ end
115
+ debug "Fetcher #{queue.name} assigned #{messages.count} messages"
116
+ tell :schedule_poll
117
+ end
118
+ end
119
+ end
120
+ end
@@ -1,119 +1,128 @@
1
- require 'json'
2
- require 'toiler/actor/utils/actor_logging'
3
-
4
- module Toiler
5
- module Actor
6
- # Responsible for processing sqs messages and notifying Fetcher when done
7
- class Processor < Concurrent::Actor::RestartingContext
8
- include Utils::ActorLogging
9
-
10
- attr_accessor :queue, :worker, :fetcher, :body_parser,
11
- :extend_callback, :executing, :thread
12
-
13
- def initialize(queue)
14
- @queue = queue
15
- @worker = Toiler.worker_class_registry[queue].new
16
- @fetcher = Toiler.fetcher queue
17
- @executing = Concurrent::AtomicBoolean.new
18
- @thread = nil
19
- init_options
20
- processor_finished
21
- end
22
-
23
- def default_executor
24
- Concurrent.global_io_executor
25
- end
26
-
27
- def on_message(msg)
28
- method, *args = msg
29
- send(method, *args)
30
- rescue StandardError => e
31
- error "Processor #{queue} failed processing, reason: #{e.class}\n#{e.backtrace.join("\n")}"
32
- end
33
-
34
- def executing?
35
- executing.value
36
- end
37
-
38
- private
39
-
40
- def init_options
41
- @auto_visibility_timeout = @worker.class.auto_visibility_timeout?
42
- @auto_delete = @worker.class.auto_delete?
43
- toiler_options = @worker.class.toiler_options
44
- @body_parser = toiler_options[:parser]
45
- @extend_callback = toiler_options[:on_visibility_extend]
46
- end
47
-
48
- def auto_visibility_timeout?
49
- @auto_visibility_timeout
50
- end
51
-
52
- def auto_delete?
53
- @auto_delete
54
- end
55
-
56
- def process(visibility, sqs_msg)
57
- executing.make_true
58
- @thread = Thread.current
59
- debug "Processor #{queue} begins processing..."
60
- body = get_body(sqs_msg)
61
- timer = visibility_extender visibility, sqs_msg, body, &extend_callback
62
-
63
- debug "Worker #{queue} starts performing..."
64
- worker.perform sqs_msg, body
65
- debug "Worker #{queue} finishes performing..."
66
- sqs_msg.delete if auto_delete?
67
- ensure
68
- process_cleanup timer
69
- executing.make_false
70
- @thread = nil
71
- end
72
-
73
- def process_cleanup(timer)
74
- debug "Processor #{queue} starts cleanup after perform..."
75
- timer.shutdown if timer
76
- ::ActiveRecord::Base.clear_active_connections! if defined? ActiveRecord
77
- processor_finished
78
- debug "Processor #{queue} finished cleanup after perform..."
79
- end
80
-
81
- def processor_finished
82
- fetcher.tell :processor_finished
83
- end
84
-
85
- def visibility_extender(queue_visibility, sqs_msg, body)
86
- return unless auto_visibility_timeout?
87
- interval = [1,queue_visibility/3].max
88
- Concurrent::TimerTask.execute execution_interval: interval,
89
- timeout_interval: interval do
90
- begin
91
- sqs_msg.visibility_timeout = queue_visibility
92
- yield sqs_msg, body if block_given?
93
- rescue StandardError => e
94
- error "Processor #{queue} failed to extend visibility of message: #{e.message}\n#{e.backtrace.join("\n")}"
95
- end
96
- end
97
- end
98
-
99
- def get_body(sqs_msg)
100
- if sqs_msg.is_a? Array
101
- sqs_msg.map { |m| parse_body m }
102
- else
103
- parse_body sqs_msg
104
- end
105
- end
106
-
107
- def parse_body(sqs_msg)
108
- case body_parser
109
- when :json then JSON.parse sqs_msg.body
110
- when Proc then body_parser.call sqs_msg
111
- when :text, nil then sqs_msg.body
112
- else body_parser.load sqs_msg.body
113
- end
114
- rescue => e
115
- raise "Error parsing the message body: #{e.message}"
116
- end
117
- end
118
- end
119
- end
1
+ require 'json'
2
+ require 'toiler/actor/utils/actor_logging'
3
+
4
+ module Toiler
5
+ module Actor
6
+ # Responsible for processing sqs messages and notifying Fetcher when done
7
+ class Processor < Concurrent::Actor::RestartingContext
8
+ include Utils::ActorLogging
9
+
10
+ attr_accessor :queue, :worker, :fetcher, :body_parser,
11
+ :extend_callback, :executing, :thread
12
+
13
+ def initialize(queue)
14
+ @queue = queue
15
+ @worker = Toiler.worker_class_registry[queue].new
16
+ @fetcher = Toiler.fetcher queue
17
+ @executing = Concurrent::AtomicBoolean.new
18
+ @thread = nil
19
+ init_options
20
+ processor_finished
21
+ end
22
+
23
+ def default_executor
24
+ Concurrent.global_io_executor
25
+ end
26
+
27
+ def on_message(msg)
28
+ method, *args = msg
29
+ send(method, *args)
30
+ rescue StandardError => e
31
+ error "Processor #{queue} failed processing, reason: #{e.class}\n#{e.backtrace.join("\n")}"
32
+ end
33
+
34
+ def executing?
35
+ executing.value
36
+ end
37
+
38
+ private
39
+
40
+ def init_options
41
+ @auto_visibility_timeout = @worker.class.auto_visibility_timeout?
42
+ @auto_delete = @worker.class.auto_delete?
43
+ toiler_options = @worker.class.toiler_options
44
+ @body_parser = toiler_options[:parser]
45
+ @extend_callback = toiler_options[:on_visibility_extend]
46
+ end
47
+
48
+ def auto_visibility_timeout?
49
+ @auto_visibility_timeout
50
+ end
51
+
52
+ def auto_delete?
53
+ @auto_delete
54
+ end
55
+
56
+ def process(visibility, sqs_msg)
57
+ process_init
58
+ body = get_body(sqs_msg)
59
+ timer = visibility_extender visibility, sqs_msg, body, &extend_callback
60
+
61
+ debug "Worker #{queue} starts performing..."
62
+ worker.perform sqs_msg, body
63
+ debug "Worker #{queue} finishes performing..."
64
+ sqs_msg.delete if auto_delete?
65
+ ensure
66
+ process_cleanup timer
67
+ end
68
+
69
+ def process_init
70
+ processor_started
71
+ @executing.make_true
72
+ @thread = Thread.current
73
+ debug "Processor #{queue} begins processing..."
74
+ end
75
+
76
+ def process_cleanup(timer)
77
+ debug "Processor #{queue} starts cleanup after perform..."
78
+ timer.shutdown if timer
79
+ ::ActiveRecord::Base.clear_active_connections! if defined? ActiveRecord
80
+ processor_finished
81
+ @executing.make_false
82
+ @thread = nil
83
+ debug "Processor #{queue} finished cleanup after perform..."
84
+ end
85
+
86
+ def processor_finished
87
+ fetcher.tell :processor_finished
88
+ end
89
+
90
+ def processor_started
91
+ fetcher.tell :processor_started
92
+ end
93
+
94
+ def visibility_extender(queue_visibility, sqs_msg, body)
95
+ return unless auto_visibility_timeout?
96
+ interval = [1,queue_visibility/3].max
97
+ Concurrent::TimerTask.execute execution_interval: interval,
98
+ timeout_interval: interval do
99
+ begin
100
+ sqs_msg.visibility_timeout = queue_visibility
101
+ yield sqs_msg, body if block_given?
102
+ rescue StandardError => e
103
+ error "Processor #{queue} failed to extend visibility of message: #{e.message}\n#{e.backtrace.join("\n")}"
104
+ end
105
+ end
106
+ end
107
+
108
+ def get_body(sqs_msg)
109
+ if sqs_msg.is_a? Array
110
+ sqs_msg.map { |m| parse_body m }
111
+ else
112
+ parse_body sqs_msg
113
+ end
114
+ end
115
+
116
+ def parse_body(sqs_msg)
117
+ case body_parser
118
+ when :json then JSON.parse sqs_msg.body
119
+ when Proc then body_parser.call sqs_msg
120
+ when :text, nil then sqs_msg.body
121
+ else body_parser.load sqs_msg.body
122
+ end
123
+ rescue => e
124
+ raise "Error parsing the message body: #{e.message}"
125
+ end
126
+ end
127
+ end
128
+ end
@@ -1,55 +1,55 @@
1
- require 'toiler/actor/fetcher'
2
- require 'toiler/actor/processor'
3
-
4
- module Toiler
5
- module Actor
6
- # Actor that starts and supervises Toiler's actors
7
- class Supervisor < Concurrent::Actor::RestartingContext
8
- include Utils::ActorLogging
9
-
10
- attr_accessor :client
11
-
12
- def initialize
13
- @client = ::Aws::SQS::Client.new
14
- spawn_fetchers
15
- spawn_processors
16
- end
17
-
18
- def on_message(_msg)
19
- pass
20
- end
21
-
22
- def queues
23
- Toiler.worker_class_registry
24
- end
25
-
26
- def spawn_fetchers
27
- queues.each do |queue, _klass|
28
- begin
29
- fetcher = Actor::Fetcher.spawn! name: "fetcher_#{queue}".to_sym,
30
- supervise: true, args: [queue, client]
31
- Toiler.set_fetcher queue, fetcher
32
- rescue StandardError => e
33
- error "Failed to start Fetcher for queue #{queue}: #{e.message}\n#{e.backtrace.join("\n")}"
34
- end
35
- end
36
- end
37
-
38
- def spawn_processors
39
- queues.each do |queue, klass|
40
- name = "processor_pool_#{queue}".to_sym
41
- count = klass.concurrency
42
- begin
43
- pool = Concurrent::Actor::Utils::Pool.spawn! name, count do |index|
44
- Actor::Processor.spawn name: "processor_#{queue}_#{index}".to_sym,
45
- supervise: true, args: [queue]
46
- end
47
- Toiler.set_processor_pool queue, pool
48
- rescue StandardError => e
49
- error "Failed to spawn Processor Pool for queue #{queue}: #{e.message}\n#{e.backtrace.join("\n")}"
50
- end
51
- end
52
- end
53
- end
54
- end
55
- end
1
+ require 'toiler/actor/fetcher'
2
+ require 'toiler/actor/processor'
3
+
4
+ module Toiler
5
+ module Actor
6
+ # Actor that starts and supervises Toiler's actors
7
+ class Supervisor < Concurrent::Actor::RestartingContext
8
+ include Utils::ActorLogging
9
+
10
+ attr_accessor :client
11
+
12
+ def initialize
13
+ @client = ::Aws::SQS::Client.new
14
+ spawn_fetchers
15
+ spawn_processors
16
+ end
17
+
18
+ def on_message(_msg)
19
+ pass
20
+ end
21
+
22
+ def queues
23
+ Toiler.worker_class_registry
24
+ end
25
+
26
+ def spawn_fetchers
27
+ queues.each do |queue, _klass|
28
+ begin
29
+ fetcher = Actor::Fetcher.spawn! name: "fetcher_#{queue}".to_sym,
30
+ supervise: true, args: [queue, client]
31
+ Toiler.set_fetcher queue, fetcher
32
+ rescue StandardError => e
33
+ error "Failed to start Fetcher for queue #{queue}: #{e.message}\n#{e.backtrace.join("\n")}"
34
+ end
35
+ end
36
+ end
37
+
38
+ def spawn_processors
39
+ queues.each do |queue, klass|
40
+ name = "processor_pool_#{queue}".to_sym
41
+ count = klass.concurrency
42
+ begin
43
+ pool = Concurrent::Actor::Utils::Pool.spawn! name, count do |index|
44
+ Actor::Processor.spawn name: "processor_#{queue}_#{index}".to_sym,
45
+ supervise: true, args: [queue]
46
+ end
47
+ Toiler.set_processor_pool queue, pool
48
+ rescue StandardError => e
49
+ error "Failed to spawn Processor Pool for queue #{queue}: #{e.message}\n#{e.backtrace.join("\n")}"
50
+ end
51
+ end
52
+ end
53
+ end
54
+ end
55
+ end
@@ -1,28 +1,28 @@
1
- module Toiler
2
- module Actor
3
- module Utils
4
- # Provides helper methods for logging
5
- module ActorLogging
6
- def error(msg)
7
- log Logger::Severity::ERROR, msg
8
- end
9
-
10
- def info(msg)
11
- log Logger::Severity::INFO, msg
12
- end
13
-
14
- def debug(msg)
15
- log Logger::Severity::DEBUG, msg
16
- end
17
-
18
- def warn(msg)
19
- log Logger::Severity::WARN, smsg
20
- end
21
-
22
- def fatal(msg)
23
- log Logger::Severity::FATAL, msg
24
- end
25
- end
26
- end
27
- end
28
- end
1
+ module Toiler
2
+ module Actor
3
+ module Utils
4
+ # Provides helper methods for logging
5
+ module ActorLogging
6
+ def error(msg)
7
+ log Logger::Severity::ERROR, msg
8
+ end
9
+
10
+ def info(msg)
11
+ log Logger::Severity::INFO, msg
12
+ end
13
+
14
+ def debug(msg)
15
+ log Logger::Severity::DEBUG, msg
16
+ end
17
+
18
+ def warn(msg)
19
+ log Logger::Severity::WARN, smsg
20
+ end
21
+
22
+ def fatal(msg)
23
+ log Logger::Severity::FATAL, msg
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end