sqs_simplify 0.2.0
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 +7 -0
- data/.gitignore +19 -0
- data/.rspec +3 -0
- data/.travis.yml +6 -0
- data/CHANGELOG.md +14 -0
- data/Gemfile +14 -0
- data/LICENSE.txt +21 -0
- data/README.md +383 -0
- data/README.pt-br.md +384 -0
- data/Rakefile +8 -0
- data/bin/console +15 -0
- data/bin/setup +8 -0
- data/lib/sqs_simplify/base.rb +212 -0
- data/lib/sqs_simplify/client.rb +34 -0
- data/lib/sqs_simplify/command.rb +89 -0
- data/lib/sqs_simplify/consumer.rb +154 -0
- data/lib/sqs_simplify/dead_queue.rb +65 -0
- data/lib/sqs_simplify/errors/execution_expired.rb +11 -0
- data/lib/sqs_simplify/errors/non_existent_queue.rb +11 -0
- data/lib/sqs_simplify/errors/reserved_method_name.rb +11 -0
- data/lib/sqs_simplify/execution_hook.rb +44 -0
- data/lib/sqs_simplify/faker_client.rb +78 -0
- data/lib/sqs_simplify/job.rb +193 -0
- data/lib/sqs_simplify/message.rb +24 -0
- data/lib/sqs_simplify/scheduler.rb +72 -0
- data/lib/sqs_simplify/version.rb +5 -0
- data/lib/sqs_simplify/worker.rb +99 -0
- data/lib/sqs_simplify.rb +68 -0
- data/rubocop.yml +3 -0
- data/sqs_simplify.gemspec +34 -0
- metadata +112 -0
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'optparse'
|
|
4
|
+
|
|
5
|
+
module SqsSimplify
|
|
6
|
+
class Command
|
|
7
|
+
attr_reader :env, :queues, :with_priority, :parallel_type, :worker_size
|
|
8
|
+
|
|
9
|
+
def initialize(args = [])
|
|
10
|
+
args = [] unless args.is_a? Array
|
|
11
|
+
|
|
12
|
+
OptionParser.new do |opt|
|
|
13
|
+
opt.on('-h', '--help', 'Show help') do
|
|
14
|
+
puts opt
|
|
15
|
+
exit 1
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
opt.on('-n', '--number_of_workers=workers', 'Number of unique workers to spawn') do |worker_count|
|
|
19
|
+
@worker_size = build_work_count(worker_count)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
opt.on('-e', '--environment=environment', 'Environment') { |env| @env = env }
|
|
23
|
+
opt.on('--queues=queues', 'queues that will be consumed') { |queues| @queues = queues.split(',') }
|
|
24
|
+
opt.on('--priority', 'with priority in the queues') { |priority| @with_priority = priority }
|
|
25
|
+
opt.on('-f', '--fork', 'parallel in processes') { @parallel_type = :in_processes }
|
|
26
|
+
opt.on('-t', '--thread', 'parallel in threads') { @parallel_type = :in_threads }
|
|
27
|
+
end.parse! args
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def run(&block)
|
|
31
|
+
@running_process = true
|
|
32
|
+
block&.call(self)
|
|
33
|
+
run!
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
private
|
|
37
|
+
|
|
38
|
+
def build_work_count(number)
|
|
39
|
+
worker_size = number.to_i
|
|
40
|
+
worker_size.positive? && worker_size <= 11 ? worker_size : 1
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def running_process?
|
|
44
|
+
@running_process
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def run!
|
|
48
|
+
time = 0
|
|
49
|
+
trap('SIGINT') { stop! }
|
|
50
|
+
|
|
51
|
+
while running_process?
|
|
52
|
+
if time.zero?
|
|
53
|
+
amount_process = execute
|
|
54
|
+
time = calculate_sleep(amount_process)
|
|
55
|
+
else
|
|
56
|
+
sleep 1
|
|
57
|
+
time -= 1
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def stop!
|
|
63
|
+
puts ''
|
|
64
|
+
puts 'this process will stop after consuming all messages!'
|
|
65
|
+
@running_process = false
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def execute
|
|
69
|
+
worker = SqsSimplify::Worker.new(
|
|
70
|
+
queues: queues,
|
|
71
|
+
priority: with_priority,
|
|
72
|
+
worker_size: worker_size,
|
|
73
|
+
parallel_type: parallel_type
|
|
74
|
+
) do
|
|
75
|
+
continues_the_process?
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
worker.perform
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def calculate_sleep(amount)
|
|
82
|
+
amount.to_i.zero? ? 10 : 0
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def continues_the_process?
|
|
86
|
+
running_process?
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
end
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module SqsSimplify
|
|
4
|
+
class Consumer < SqsSimplify::Base
|
|
5
|
+
include SqsSimplify::ExecutionHook
|
|
6
|
+
|
|
7
|
+
private_class_method :new
|
|
8
|
+
attr_accessor :delete_sqs_message
|
|
9
|
+
attr_reader :visibility_timeout, :errors
|
|
10
|
+
|
|
11
|
+
def initialize(sqs_message, visibility_timeout)
|
|
12
|
+
@sqs_message = sqs_message
|
|
13
|
+
@message = load_message(sqs_message)
|
|
14
|
+
@visibility_timeout = visibility_timeout
|
|
15
|
+
self.delete_sqs_message = visibility_timeout.positive?
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def perform
|
|
19
|
+
raise 'Must implement this method'
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def delete_sqs_message?
|
|
23
|
+
delete_sqs_message
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
protected
|
|
27
|
+
|
|
28
|
+
attr_reader :message, :sqs_message
|
|
29
|
+
|
|
30
|
+
class << self
|
|
31
|
+
def consume_messages(amount = nil, worker_size: nil, parallel_type: nil)
|
|
32
|
+
amount ||= maximum_message_quantity
|
|
33
|
+
@worker_size = worker_size.to_i
|
|
34
|
+
@parallel_type = parallel_type
|
|
35
|
+
|
|
36
|
+
visibility_timeout # load
|
|
37
|
+
sqs_messages = fetch_messages(amount)
|
|
38
|
+
consume_sqs_messages(sqs_messages, Time.now)
|
|
39
|
+
rescue Exception => e
|
|
40
|
+
call_hook :resolver_exception, e, sqs_messages
|
|
41
|
+
ensure
|
|
42
|
+
return sqs_messages&.count || 0
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def message_not_deleted(&block)
|
|
46
|
+
hooks[:message_not_deleted] = block
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def around(&block)
|
|
50
|
+
hooks[:around] = block
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
protected
|
|
54
|
+
|
|
55
|
+
def maximum_message_quantity
|
|
56
|
+
number = settings[:maximum_message_quantity].to_i
|
|
57
|
+
number.positive? && number <= 10 ? number : 10
|
|
58
|
+
rescue StandardError
|
|
59
|
+
10
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
private
|
|
63
|
+
|
|
64
|
+
def fetch_messages(amount = 10)
|
|
65
|
+
client.receive_message(
|
|
66
|
+
queue_url: queue_url,
|
|
67
|
+
message_attribute_names: ['All'], # Receive all custom attributes.
|
|
68
|
+
max_number_of_messages: amount, # Receive at most one message.
|
|
69
|
+
wait_time_seconds: 0 # Do not wait to check for the message.
|
|
70
|
+
).messages
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def consume_sqs_messages(sqs_messages, start_time)
|
|
74
|
+
call_hook(:before_all, sqs_messages)
|
|
75
|
+
benchmark { choose_process(sqs_messages, start_time) }
|
|
76
|
+
call_hook(:after_all, sqs_messages)
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def choose_process(messages, start_time)
|
|
80
|
+
parallel_type, amount_processes = build_parallel_parameter(messages.size)
|
|
81
|
+
|
|
82
|
+
logger.info "Started loop with: { queue_name: #{queue_name}, #{parallel_type}: #{amount_processes}, messages: #{messages.count} }"
|
|
83
|
+
if amount_processes == 1
|
|
84
|
+
messages.each { |sqs_message| consume_sqs_message(sqs_message, start_time) }
|
|
85
|
+
else
|
|
86
|
+
Parallel.each(messages, parallel_type => amount_processes) do |sqs_message|
|
|
87
|
+
consume_sqs_message(sqs_message, start_time)
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
def build_parallel_parameter(messages_size)
|
|
93
|
+
process_in_parallel = messages_size > 1 &&
|
|
94
|
+
@worker_size > 1 &&
|
|
95
|
+
%w[in_threads in_processes].include?(@parallel_type.to_s)
|
|
96
|
+
|
|
97
|
+
process_in_parallel ? [@parallel_type, @worker_size] : [:without_parallel, 1]
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
def consume_sqs_message(sqs_message, start_time)
|
|
101
|
+
time_left = build_time_left(start_time)
|
|
102
|
+
consumer = new(sqs_message, time_left)
|
|
103
|
+
call_hook(:before_each, consumer)
|
|
104
|
+
|
|
105
|
+
started = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
|
106
|
+
Timeout.timeout(time_left, Errors::ExecutionExpired) do
|
|
107
|
+
around.is_a?(Proc) ? around.call(consumer) : consumer.perform
|
|
108
|
+
end
|
|
109
|
+
raise Errors::ExecutionExpired if Process.clock_gettime(Process::CLOCK_MONOTONIC) - started >= time_left
|
|
110
|
+
rescue Exception => e
|
|
111
|
+
if consumer
|
|
112
|
+
consumer.delete_sqs_message = false
|
|
113
|
+
consumer.instance_variable_set :@errors, ["Exception: #{e.message}"] + e.backtrace
|
|
114
|
+
end
|
|
115
|
+
call_hook :resolver_exception, e, consumer
|
|
116
|
+
ensure
|
|
117
|
+
call_hook(:after_each, consumer)
|
|
118
|
+
delete_message(consumer)
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
def build_time_left(start_time)
|
|
122
|
+
time_left = visibility_timeout - (Time.now - start_time).to_f
|
|
123
|
+
time_left = -0.1 if time_left.zero?
|
|
124
|
+
[time_left, 0.1].max
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
def delete_message(consumer)
|
|
128
|
+
if consumer&.delete_sqs_message
|
|
129
|
+
delete_sqs_message(consumer.send(:sqs_message))
|
|
130
|
+
else
|
|
131
|
+
call_hook :message_not_deleted, consumer
|
|
132
|
+
end
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
def delete_sqs_message(message)
|
|
136
|
+
client.delete_message(
|
|
137
|
+
queue_url: queue_url,
|
|
138
|
+
receipt_handle: message.receipt_handle
|
|
139
|
+
)
|
|
140
|
+
true
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
def benchmark(&block)
|
|
144
|
+
start_time = Time.now
|
|
145
|
+
block.call
|
|
146
|
+
logger.info "Finished in #{(Time.now - start_time).to_f} seconds"
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
def logger
|
|
150
|
+
SqsSimplify.logger
|
|
151
|
+
end
|
|
152
|
+
end
|
|
153
|
+
end
|
|
154
|
+
end
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module SqsSimplify
|
|
4
|
+
class DeadQueue < SqsSimplify::Base
|
|
5
|
+
VISIBILITY_TIMEOUT = 60 * 60
|
|
6
|
+
MESSAGE_RETENTION_PERIOD = 14 * 24 * 60 * 60
|
|
7
|
+
DELAY_SECONDS = 0
|
|
8
|
+
|
|
9
|
+
class << self
|
|
10
|
+
undef_method :max_receive_count
|
|
11
|
+
|
|
12
|
+
def main_queue
|
|
13
|
+
@main_queue ||= const_get(name.gsub('::DeadQueue', ''))
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def create_queue
|
|
17
|
+
dead_queue = find_queue_by_name(queue_full_name)
|
|
18
|
+
dead_queue ||= super
|
|
19
|
+
|
|
20
|
+
# Use a redrive policy to specify the dead letter queue and its behavior.
|
|
21
|
+
redrive_policy = {
|
|
22
|
+
'maxReceiveCount' => main_queue.max_receive_count.to_s, # After the queue receives the same message 1 times, send that message to the dead letter queue.
|
|
23
|
+
'deadLetterTargetArn' => queue_arn
|
|
24
|
+
}.to_json
|
|
25
|
+
|
|
26
|
+
main_queue.update_attributes(RedrivePolicy: redrive_policy)
|
|
27
|
+
dead_queue
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def send_message_to_main_queue(amount)
|
|
31
|
+
raise "Invalid argument #{amount}" unless amount.is_a?(Numeric) ||
|
|
32
|
+
amount.positive?
|
|
33
|
+
|
|
34
|
+
dead_queue_to_queue_loop(amount)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
private
|
|
38
|
+
|
|
39
|
+
def dead_queue_to_queue_loop(amount)
|
|
40
|
+
count = 0
|
|
41
|
+
|
|
42
|
+
loop do
|
|
43
|
+
messages = dead_queue_messages([amount, 10].min)
|
|
44
|
+
count += messages.count
|
|
45
|
+
|
|
46
|
+
messages.each do |message|
|
|
47
|
+
client.send_message(queue_url: main_queue.queue_url, message_body: message[:body])
|
|
48
|
+
client.delete_message(queue_url: queue_url, receipt_handle: message.receipt_handle)
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
return count if messages.count.zero? || count >= amount
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def dead_queue_messages(amount)
|
|
56
|
+
client.receive_message(
|
|
57
|
+
queue_url: queue_url,
|
|
58
|
+
message_attribute_names: ['All'],
|
|
59
|
+
max_number_of_messages: amount,
|
|
60
|
+
wait_time_seconds: 0
|
|
61
|
+
).messages
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module SqsSimplify
|
|
4
|
+
module ExecutionHook
|
|
5
|
+
module ClassMethods
|
|
6
|
+
def after(type = :each, &block)
|
|
7
|
+
type = "after_#{type}".to_sym
|
|
8
|
+
hooks[type] = block
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def before(type = :each, &block)
|
|
12
|
+
type = "before_#{type}".to_sym
|
|
13
|
+
hooks[type] = block
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def call_hook(type, arg = nil, args = {})
|
|
17
|
+
block = hooks[type.to_sym]
|
|
18
|
+
SqsSimplify.call_hook(type, arg, args) unless self == SqsSimplify
|
|
19
|
+
block&.call(arg, args)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def hooks
|
|
23
|
+
@hooks ||= {}
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def resolver_exception(&block)
|
|
27
|
+
hooks[:resolver_exception] = block
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def add_hook(hook_name, *hook_names)
|
|
31
|
+
hook_names.unshift(hook_name)
|
|
32
|
+
hook_names.each do |name|
|
|
33
|
+
define_singleton_method(name) do |&block|
|
|
34
|
+
hooks[name.to_sym] = block
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def self.included(base)
|
|
41
|
+
base.extend ClassMethods
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module SqsSimplify
|
|
4
|
+
class FakerClient
|
|
5
|
+
def send_message(sqs_message)
|
|
6
|
+
id = next_id
|
|
7
|
+
url = sqs_message[:queue_url]
|
|
8
|
+
body = sqs_message[:message_body]
|
|
9
|
+
message = OpenStruct.new(receipt_handle: id, body: body, queue_url: url)
|
|
10
|
+
|
|
11
|
+
if pool[url]
|
|
12
|
+
pool[url] << message
|
|
13
|
+
else
|
|
14
|
+
pool[url] = [message]
|
|
15
|
+
end
|
|
16
|
+
OpenStruct.new(message_id: id)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def receive_message(queue_url:, max_number_of_messages:, **_args)
|
|
20
|
+
amount =
|
|
21
|
+
if max_number_of_messages.nil? || !max_number_of_messages.is_a?(Integer)
|
|
22
|
+
10
|
|
23
|
+
elsif max_number_of_messages.positive? && max_number_of_messages < 11
|
|
24
|
+
max_number_of_messages
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
messages = (pool[queue_url] || [])[0, amount]
|
|
28
|
+
OpenStruct.new(messages: messages)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def delete_message(queue_url:, receipt_handle:)
|
|
32
|
+
messages = pool[queue_url] || []
|
|
33
|
+
messages.delete_if do |message|
|
|
34
|
+
message.receipt_handle == receipt_handle && message.queue_url == queue_url
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
true
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def get_queue_attributes(queue_url:, **_args)
|
|
41
|
+
messages = pool[queue_url] || []
|
|
42
|
+
OpenStruct.new(attributes: {
|
|
43
|
+
'ApproximateNumberOfMessages' => messages.count,
|
|
44
|
+
'VisibilityTimeout' => 60
|
|
45
|
+
})
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def get_queue_url(queue_name:)
|
|
49
|
+
OpenStruct.new queue_url: "http//sqs_faker/#{queue_name}"
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
private
|
|
53
|
+
|
|
54
|
+
def pool
|
|
55
|
+
self.class.pool
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def next_id
|
|
59
|
+
self.class.next_id
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
class << self
|
|
63
|
+
def next_id
|
|
64
|
+
@next_id ||= 0
|
|
65
|
+
@next_id += 1
|
|
66
|
+
@next_id.to_s(16)
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def pool
|
|
70
|
+
@pool ||= {}
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def purger!
|
|
74
|
+
@pool = {}
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
end
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'zlib'
|
|
4
|
+
|
|
5
|
+
module SqsSimplify
|
|
6
|
+
class Job < SqsSimplify::Base
|
|
7
|
+
set :scheduler, true
|
|
8
|
+
private_class_method :new
|
|
9
|
+
|
|
10
|
+
def perform(*_rest, **_keyrest)
|
|
11
|
+
raise 'NotImplemented'
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def new_job(**options)
|
|
15
|
+
self.class.new_job(**options)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def dump(parameters)
|
|
19
|
+
self.class.send :_dump, parameters
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
class << self
|
|
23
|
+
def new_job(**options)
|
|
24
|
+
self::ProxyJob.new(new, **options)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def perform(*rest, **keyrest)
|
|
28
|
+
new_job.perform(*rest, **keyrest)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def perform_later(*rest, **keyrest)
|
|
32
|
+
new_job.perform_later(*rest, **keyrest)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def consume_messages(amount = nil, **options)
|
|
36
|
+
consumer.consume_messages amount, **options
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def scheduler
|
|
40
|
+
@scheduler ||= _build_scheduler
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def consumer
|
|
44
|
+
@consumer ||= _build_consumer
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def call_validator_parameters!(array, hash)
|
|
48
|
+
_validator_parameters.call(*array, **hash)
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
private
|
|
52
|
+
|
|
53
|
+
def _validator_parameters
|
|
54
|
+
@_validator_parameters ||= _build_validator_parameters
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def _build_validator_parameters
|
|
58
|
+
parameters = new.method(:perform).parameters
|
|
59
|
+
params = parameters.map do |opt, name|
|
|
60
|
+
_parameters_to_args opt, name
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
class_eval "->(#{params.join(', ')}) {}", __FILE__, __LINE__
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def _parameters_to_args(opt, name)
|
|
67
|
+
case opt
|
|
68
|
+
when :req
|
|
69
|
+
name.to_s
|
|
70
|
+
when :opt
|
|
71
|
+
"#{name}=nil"
|
|
72
|
+
when :rest
|
|
73
|
+
"*#{name}"
|
|
74
|
+
when :keyreq
|
|
75
|
+
"#{name}:"
|
|
76
|
+
when :key
|
|
77
|
+
"#{name}:nil"
|
|
78
|
+
when :keyrest
|
|
79
|
+
"**#{name}"
|
|
80
|
+
when :block
|
|
81
|
+
"&#{name}"
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def inherited(sub)
|
|
86
|
+
super
|
|
87
|
+
|
|
88
|
+
if sub.superclass == SqsSimplify::Job
|
|
89
|
+
sub.set :scheduler, true
|
|
90
|
+
else
|
|
91
|
+
sub.instance_variable_set :@settings, settings.clone
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
def _execute(args)
|
|
96
|
+
class_name = args['class']
|
|
97
|
+
array_params, hash_params = _read_parameters args['parameters']
|
|
98
|
+
|
|
99
|
+
source = const_get(class_name)
|
|
100
|
+
instance = source.send :new
|
|
101
|
+
instance.send(:perform, *array_params, **hash_params)
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def _read_parameters(parameters)
|
|
105
|
+
args = _load(parameters)
|
|
106
|
+
[args[:array], args[:hash]]
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
def _build_scheduler
|
|
110
|
+
klass = _build_sub_class 'Scheduler'
|
|
111
|
+
klass.private_class_method :send_message
|
|
112
|
+
klass
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
def _build_consumer
|
|
116
|
+
klass = _build_sub_class 'Consumer'
|
|
117
|
+
klass.define_method(:perform) { SqsSimplify::Job.send :_execute, message }
|
|
118
|
+
klass
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
def _build_sub_class(module_name)
|
|
122
|
+
class_eval <<~M, __FILE__, __LINE__ + 1
|
|
123
|
+
class #{self}::#{module_name}Job < SqsSimplify::#{module_name}
|
|
124
|
+
def self.settings
|
|
125
|
+
#{self}.settings
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
def self.client
|
|
129
|
+
#{self}.client
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
def self.queue_name
|
|
133
|
+
#{self}.queue_name
|
|
134
|
+
end
|
|
135
|
+
end
|
|
136
|
+
M
|
|
137
|
+
|
|
138
|
+
const_get "#{self}::#{module_name}Job"
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
def _dump(value)
|
|
142
|
+
Base64.encode64(Zlib::Deflate.deflate(Marshal.dump(value)))
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
def _load(value)
|
|
146
|
+
Marshal.load(Zlib::Inflate.inflate(Base64.decode64(value)))
|
|
147
|
+
end
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
class ProxyJob
|
|
151
|
+
def initialize(job, **options)
|
|
152
|
+
@job = job
|
|
153
|
+
@after = options[:after]
|
|
154
|
+
@queue_url = options[:queue_url]
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
def perform(*array, **hash)
|
|
158
|
+
@job.perform(*array, **hash)
|
|
159
|
+
:executed
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
def perform_later(*array, **hash)
|
|
163
|
+
return perform(*array, **hash) unless scheduler?
|
|
164
|
+
|
|
165
|
+
@job.class.call_validator_parameters! array, hash
|
|
166
|
+
message = {
|
|
167
|
+
'class' => @job.class.name,
|
|
168
|
+
'parameters' => @job.dump(array: array, hash: hash)
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
send_message message
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
private
|
|
175
|
+
|
|
176
|
+
def send_message(message)
|
|
177
|
+
@job.class.scheduler.send(:send_message, message: message, after: @after, queue_url: @queue_url)
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
def respond_to_missing?(symbol, value = nil)
|
|
181
|
+
@job.respond_to? symbol, value
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
def method_missing(symbol, *rest, **keyrest)
|
|
185
|
+
@job.send symbol, *rest, **keyrest
|
|
186
|
+
end
|
|
187
|
+
|
|
188
|
+
def scheduler?
|
|
189
|
+
SqsSimplify::Job.settings[:scheduler] && @job.class.settings[:scheduler]
|
|
190
|
+
end
|
|
191
|
+
end
|
|
192
|
+
end
|
|
193
|
+
end
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module SqsSimplify
|
|
4
|
+
class Message
|
|
5
|
+
attr_accessor :body, :delay_seconds, :queue_url, :group_id
|
|
6
|
+
|
|
7
|
+
def initialize(body:, queue_url:, delay_seconds: 0, group_id: nil)
|
|
8
|
+
self.body = body
|
|
9
|
+
self.delay_seconds = delay_seconds
|
|
10
|
+
self.queue_url = queue_url
|
|
11
|
+
self.group_id = group_id
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def to_send
|
|
15
|
+
hash = {
|
|
16
|
+
queue_url: queue_url,
|
|
17
|
+
message_body: body,
|
|
18
|
+
delay_seconds: delay_seconds
|
|
19
|
+
}
|
|
20
|
+
hash[:message_group_id] = group_id.to_s if group_id
|
|
21
|
+
hash
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|