mimi-messaging-sqs_sns 0.4.3 → 0.8.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/CHANGELOG.md +30 -0
- data/README.md +3 -1
- data/TODO.md +9 -0
- data/examples/event.rb +3 -3
- data/examples/mass-query.rb +53 -0
- data/examples/query.rb +5 -3
- data/examples/responder.rb +13 -7
- data/examples/subscriber.rb +6 -3
- data/lib/mimi/messaging/sqs_sns.rb +1 -0
- data/lib/mimi/messaging/sqs_sns/adapter.rb +154 -62
- data/lib/mimi/messaging/sqs_sns/consumer.rb +55 -12
- data/lib/mimi/messaging/sqs_sns/reply_consumer.rb +9 -11
- data/lib/mimi/messaging/sqs_sns/temporary_queue_consumer.rb +29 -0
- data/lib/mimi/messaging/sqs_sns/timeout_queue.rb +65 -0
- data/lib/mimi/messaging/sqs_sns/version.rb +1 -1
- data/mimi-messaging-sqs_sns.gemspec +2 -1
- metadata +30 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: d8ca2b61a4f7cc802b628397b74d45a19f81dd50cf7d285bfc813922e17e7327
|
4
|
+
data.tar.gz: fea7216f18b7707cedefaac1090349791c01f35b76f0a3019531039e28e8a39e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c4734aef7603919fe1485b1403fe63cfe3751b25ca0c753336b1567b524817ad68d9a0255fb14a993f365d58963b3a5a894bb02b338eac19d2af01303455510b
|
7
|
+
data.tar.gz: 017f5eb8d84cb0b10196c6a76fee01b5c1f3909c3b3eca2883e729992b02370f9be9827639858cbcb7e68beb02c35e4ac6cc9b5f40726b9e8faaccf5c8ee7eb6
|
data/CHANGELOG.md
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
# Changelog
|
2
|
+
|
3
|
+
## UNRELEASED
|
4
|
+
|
5
|
+
## v0.8.1
|
6
|
+
|
7
|
+
* [#5](https://github.com/kukushkin/mimi-messaging-sqs_sns/pull/5)
|
8
|
+
* Refactored worker pool based message processing and error handling
|
9
|
+
|
10
|
+
## v0.8.0
|
11
|
+
|
12
|
+
* [#3](https://github.com/kukushkin/mimi-messaging-sqs_sns/pull/3)
|
13
|
+
* Added a worker pool:
|
14
|
+
* now processing of messages from a single queue can be done in multiple parallel threads (workers)
|
15
|
+
* the worker threads are shared across all message consumers which read messages from different queues
|
16
|
+
* the size of the worker pool is limited, to have at most `mq_worker_pool_max_threads` processing messages in parallel
|
17
|
+
* `mq_worker_pool_min_threads` determines the target minimal number of threads in the pool, waiting for new messages
|
18
|
+
* `mq_worker_pool_max_backlog` controls how many messages which are read from SQS queues can be put in the worker pool backlog; if a new message is read from SQS queue and the backlog is full, this message is NACK-ed (put back into SQS queue for the other consumers to process)
|
19
|
+
* Improved thread-safety of the adapter: reply consumer, TimeoutQueue#pop
|
20
|
+
|
21
|
+
## v0.7.0
|
22
|
+
|
23
|
+
* [#1](https://github.com/kukushkin/mimi-messaging-sqs_sns/pull/1)
|
24
|
+
* Added KMS support for creating queues/topics with sever-side encryption enabled
|
25
|
+
* Optimized stopping of the adapter: stopping all consumers in parallel
|
26
|
+
|
27
|
+
|
28
|
+
## v0.6.x
|
29
|
+
|
30
|
+
* Basic functionality implemented, see missing features in [TODO](TODO.md)
|
data/README.md
CHANGED
@@ -2,6 +2,9 @@
|
|
2
2
|
|
3
3
|
AWS SQS/SNS adapter for [mimi-messaging](https://github.com/kukushkin/mimi-messaging).
|
4
4
|
|
5
|
+
* [Changes](CHANGELOG.md)
|
6
|
+
|
7
|
+
|
5
8
|
## Installation
|
6
9
|
|
7
10
|
Add this line to your application's Gemfile:
|
@@ -43,7 +46,6 @@ Mimi::Messaging.configure(
|
|
43
46
|
Mimi::Messaging.start
|
44
47
|
```
|
45
48
|
|
46
|
-
|
47
49
|
## Contributing
|
48
50
|
|
49
51
|
Bug reports and pull requests are welcome on GitHub at https://github.com/kukushkin/mimi-messaging-sqs_sns. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
|
data/TODO.md
ADDED
data/examples/event.rb
CHANGED
@@ -4,8 +4,8 @@ require "mimi/messaging/sqs_sns"
|
|
4
4
|
|
5
5
|
COUNT = 10
|
6
6
|
AWS_REGION = "eu-west-1"
|
7
|
-
AWS_SQS_ENDPOINT_URL = "http://localstack:
|
8
|
-
AWS_SNS_ENDPOINT_URL = "http://localstack:
|
7
|
+
AWS_SQS_ENDPOINT_URL = "http://localstack:4566"
|
8
|
+
AWS_SNS_ENDPOINT_URL = "http://localstack:4566"
|
9
9
|
AWS_ACCESS_KEY_ID = "foo"
|
10
10
|
AWS_SECRET_ACCESS_KEY = "bar"
|
11
11
|
|
@@ -30,5 +30,5 @@ COUNT.times do |i|
|
|
30
30
|
t = Time.now
|
31
31
|
puts "Publishing event: #{i}"
|
32
32
|
adapter.event("hello#tested", i: i) # rand(100))
|
33
|
-
sleep 1
|
33
|
+
sleep 0.1
|
34
34
|
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "mimi/messaging/sqs_sns"
|
4
|
+
|
5
|
+
COUNT = 10
|
6
|
+
THREADS = 10
|
7
|
+
QUERY_TIMEOUT = 60
|
8
|
+
AWS_REGION = "eu-west-1"
|
9
|
+
AWS_SQS_ENDPOINT_URL = "http://localstack:4566"
|
10
|
+
AWS_SNS_ENDPOINT_URL = "http://localstack:4566"
|
11
|
+
AWS_ACCESS_KEY_ID = "foo"
|
12
|
+
AWS_SECRET_ACCESS_KEY = "bar"
|
13
|
+
AWS_SQS_SNS_KMS_MASTER_KEY_ID = "blah"
|
14
|
+
|
15
|
+
logger = Logger.new(STDOUT)
|
16
|
+
logger.level = Logger::INFO
|
17
|
+
Mimi::Messaging.use(logger: logger, serializer: Mimi::Messaging::JsonSerializer)
|
18
|
+
Mimi::Messaging.configure(
|
19
|
+
mq_adapter: "sqs_sns",
|
20
|
+
mq_aws_access_key_id: AWS_ACCESS_KEY_ID,
|
21
|
+
mq_aws_secret_access_key: AWS_SECRET_ACCESS_KEY,
|
22
|
+
mq_aws_region: AWS_REGION,
|
23
|
+
mq_aws_sqs_endpoint: AWS_SQS_ENDPOINT_URL,
|
24
|
+
mq_aws_sns_endpoint: AWS_SNS_ENDPOINT_URL,
|
25
|
+
mq_aws_sqs_sns_kms_master_key_id: AWS_SQS_SNS_KMS_MASTER_KEY_ID,
|
26
|
+
mq_default_query_timeout: QUERY_TIMEOUT
|
27
|
+
)
|
28
|
+
adapter = Mimi::Messaging.adapter
|
29
|
+
|
30
|
+
adapter.start
|
31
|
+
|
32
|
+
t_start = Time.now
|
33
|
+
t_queries = []
|
34
|
+
threads = []
|
35
|
+
THREADS.times do |ti|
|
36
|
+
threads << Thread.new do
|
37
|
+
COUNT.times do |i|
|
38
|
+
t = Time.now
|
39
|
+
result = adapter.query("test/hello", i: i) # rand(100))
|
40
|
+
t = Time.now - t
|
41
|
+
t_queries << t
|
42
|
+
puts "result: #{result.to_h}, t: %.3fs" % t
|
43
|
+
sleep 0.1
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
threads.each(&:join)
|
49
|
+
|
50
|
+
t_elapsed = Time.now - t_start
|
51
|
+
puts "t_elapsed: %.3fs" % t_elapsed
|
52
|
+
adapter.stop
|
53
|
+
puts "t.avg: %.3fs" % (t_queries.sum / t_queries.count)
|
data/examples/query.rb
CHANGED
@@ -4,10 +4,11 @@ require "mimi/messaging/sqs_sns"
|
|
4
4
|
|
5
5
|
COUNT = 10
|
6
6
|
AWS_REGION = "eu-west-1"
|
7
|
-
AWS_SQS_ENDPOINT_URL = "http://localstack:
|
8
|
-
AWS_SNS_ENDPOINT_URL = "http://localstack:
|
7
|
+
AWS_SQS_ENDPOINT_URL = "http://localstack:4566"
|
8
|
+
AWS_SNS_ENDPOINT_URL = "http://localstack:4566"
|
9
9
|
AWS_ACCESS_KEY_ID = "foo"
|
10
10
|
AWS_SECRET_ACCESS_KEY = "bar"
|
11
|
+
AWS_SQS_SNS_KMS_MASTER_KEY_ID = "blah"
|
11
12
|
|
12
13
|
logger = Logger.new(STDOUT)
|
13
14
|
logger.level = Logger::INFO
|
@@ -18,7 +19,8 @@ Mimi::Messaging.configure(
|
|
18
19
|
mq_aws_secret_access_key: AWS_SECRET_ACCESS_KEY,
|
19
20
|
mq_aws_region: AWS_REGION,
|
20
21
|
mq_aws_sqs_endpoint: AWS_SQS_ENDPOINT_URL,
|
21
|
-
mq_aws_sns_endpoint: AWS_SNS_ENDPOINT_URL
|
22
|
+
mq_aws_sns_endpoint: AWS_SNS_ENDPOINT_URL,
|
23
|
+
mq_aws_sqs_sns_kms_master_key_id: AWS_SQS_SNS_KMS_MASTER_KEY_ID
|
22
24
|
)
|
23
25
|
adapter = Mimi::Messaging.adapter
|
24
26
|
|
data/examples/responder.rb
CHANGED
@@ -2,11 +2,12 @@
|
|
2
2
|
|
3
3
|
require "mimi/messaging/sqs_sns"
|
4
4
|
|
5
|
-
AWS_REGION
|
6
|
-
AWS_SQS_ENDPOINT_URL
|
7
|
-
AWS_SNS_ENDPOINT_URL
|
8
|
-
AWS_ACCESS_KEY_ID
|
5
|
+
AWS_REGION = "eu-west-1"
|
6
|
+
AWS_SQS_ENDPOINT_URL = "http://localstack:4566"
|
7
|
+
AWS_SNS_ENDPOINT_URL = "http://localstack:4566"
|
8
|
+
AWS_ACCESS_KEY_ID = "foo"
|
9
9
|
AWS_SECRET_ACCESS_KEY = "bar"
|
10
|
+
AWS_SQS_SNS_KMS_MASTER_KEY_ID = "blah"
|
10
11
|
|
11
12
|
class Processor
|
12
13
|
def self.call_command(method_name, message, opts)
|
@@ -16,13 +17,14 @@ class Processor
|
|
16
17
|
def self.call_query(method_name, message, opts)
|
17
18
|
# puts "QUERY: #{method_name}, #{message}, headers: #{opts[:headers]}"
|
18
19
|
puts "QUERY: #{method_name}, headers: #{opts[:headers]}"
|
20
|
+
sleep 1 # imitate work
|
19
21
|
{}
|
20
22
|
end
|
21
23
|
end # class Processor
|
22
24
|
|
23
25
|
|
24
26
|
logger = Logger.new(STDOUT)
|
25
|
-
logger.level = Logger::
|
27
|
+
logger.level = Logger::DEBUG
|
26
28
|
Mimi::Messaging.use(logger: logger, serializer: Mimi::Messaging::JsonSerializer)
|
27
29
|
Mimi::Messaging.configure(
|
28
30
|
mq_adapter: "sqs_sns",
|
@@ -30,7 +32,12 @@ Mimi::Messaging.configure(
|
|
30
32
|
mq_aws_secret_access_key: AWS_SECRET_ACCESS_KEY,
|
31
33
|
mq_aws_region: AWS_REGION,
|
32
34
|
mq_aws_sqs_endpoint: AWS_SQS_ENDPOINT_URL,
|
33
|
-
mq_aws_sns_endpoint: AWS_SNS_ENDPOINT_URL
|
35
|
+
mq_aws_sns_endpoint: AWS_SNS_ENDPOINT_URL,
|
36
|
+
mq_aws_sqs_sns_kms_master_key_id: AWS_SQS_SNS_KMS_MASTER_KEY_ID,
|
37
|
+
mq_worker_pool_min_threads: 1,
|
38
|
+
mq_worker_pool_max_threads: 2,
|
39
|
+
mq_worker_pool_max_backlog: 4,
|
40
|
+
mq_log_at_level: :info
|
34
41
|
)
|
35
42
|
adapter = Mimi::Messaging.adapter
|
36
43
|
queue_name = "test"
|
@@ -47,4 +54,3 @@ ensure
|
|
47
54
|
puts "Stopping adapter"
|
48
55
|
adapter.stop
|
49
56
|
end
|
50
|
-
|
data/examples/subscriber.rb
CHANGED
@@ -3,8 +3,8 @@
|
|
3
3
|
require "mimi/messaging/sqs_sns"
|
4
4
|
|
5
5
|
AWS_REGION = "eu-west-1"
|
6
|
-
AWS_SQS_ENDPOINT_URL
|
7
|
-
AWS_SNS_ENDPOINT_URL
|
6
|
+
AWS_SQS_ENDPOINT_URL = "http://localstack:4566"
|
7
|
+
AWS_SNS_ENDPOINT_URL = "http://localstack:4566"
|
8
8
|
AWS_ACCESS_KEY_ID = "foo"
|
9
9
|
AWS_SECRET_ACCESS_KEY = "bar"
|
10
10
|
|
@@ -20,6 +20,7 @@ class Processor
|
|
20
20
|
|
21
21
|
def self.call_event(event_type, message, opts)
|
22
22
|
puts "EVENT: #{event_type}, #{message}, headers: #{message.headers}"
|
23
|
+
sleep 1 # imitate work
|
23
24
|
end
|
24
25
|
end # class Processor
|
25
26
|
|
@@ -34,6 +35,9 @@ Mimi::Messaging.configure(
|
|
34
35
|
mq_aws_region: AWS_REGION,
|
35
36
|
mq_aws_sqs_endpoint: AWS_SQS_ENDPOINT_URL,
|
36
37
|
mq_aws_sns_endpoint: AWS_SNS_ENDPOINT_URL,
|
38
|
+
mq_worker_pool_min_threads: 1,
|
39
|
+
mq_worker_pool_max_threads: 2,
|
40
|
+
mq_worker_pool_max_backlog: 4,
|
37
41
|
mq_log_at_level: :info
|
38
42
|
)
|
39
43
|
adapter = Mimi::Messaging.adapter
|
@@ -52,4 +56,3 @@ ensure
|
|
52
56
|
puts "Stopping adapter"
|
53
57
|
adapter.stop
|
54
58
|
end
|
55
|
-
|
@@ -3,8 +3,9 @@
|
|
3
3
|
require "mimi/messaging"
|
4
4
|
require "aws-sdk-sqs"
|
5
5
|
require "aws-sdk-sns"
|
6
|
-
require "timeout"
|
7
6
|
require "securerandom"
|
7
|
+
require "concurrent"
|
8
|
+
require_relative "timeout_queue"
|
8
9
|
|
9
10
|
module Mimi
|
10
11
|
module Messaging
|
@@ -40,7 +41,7 @@ module Mimi
|
|
40
41
|
"." => "-"
|
41
42
|
}.freeze
|
42
43
|
|
43
|
-
attr_reader :options, :sqs_client, :sns_client
|
44
|
+
attr_reader :options, :sqs_client, :sns_client, :worker_pool
|
44
45
|
|
45
46
|
register_adapter_name "sqs_sns"
|
46
47
|
|
@@ -49,6 +50,11 @@ module Mimi
|
|
49
50
|
mq_default_query_timeout: 15, # seconds,
|
50
51
|
mq_reply_queue_prefix: "reply-",
|
51
52
|
|
53
|
+
# worker pool parameters
|
54
|
+
mq_worker_pool_min_threads: 1,
|
55
|
+
mq_worker_pool_max_threads: 16,
|
56
|
+
mq_worker_pool_max_backlog: 16,
|
57
|
+
|
52
58
|
# if nil, AWS SDK will guess values from environment
|
53
59
|
mq_aws_region: nil,
|
54
60
|
mq_aws_access_key_id: nil,
|
@@ -56,7 +62,8 @@ module Mimi
|
|
56
62
|
mq_aws_sqs_endpoint: nil,
|
57
63
|
mq_aws_sns_endpoint: nil,
|
58
64
|
|
59
|
-
|
65
|
+
mq_aws_sqs_sns_kms_master_key_id: nil,
|
66
|
+
mq_aws_sqs_read_timeout: 20, # seconds
|
60
67
|
}.freeze
|
61
68
|
|
62
69
|
# Initializes SQS/SNS adapter
|
@@ -73,16 +80,19 @@ module Mimi
|
|
73
80
|
#
|
74
81
|
def initialize(options)
|
75
82
|
@options = DEFAULT_OPTIONS.merge(options).dup
|
83
|
+
@reply_consumer_mutex = Mutex.new
|
76
84
|
end
|
77
85
|
|
78
86
|
def start
|
79
87
|
@sqs_client = Aws::SQS::Client.new(sqs_client_config)
|
80
88
|
@sns_client = Aws::SNS::Client.new(sns_client_config)
|
89
|
+
start_worker_pool!
|
81
90
|
check_availability!
|
82
91
|
end
|
83
92
|
|
84
93
|
def stop
|
85
94
|
stop_all_processors
|
95
|
+
stop_worker_pool!
|
86
96
|
@sqs_client = nil
|
87
97
|
@sns_client = nil
|
88
98
|
end
|
@@ -93,6 +103,7 @@ module Mimi
|
|
93
103
|
# for processors.
|
94
104
|
#
|
95
105
|
def stop_all_processors
|
106
|
+
@consumers&.each(&:signal_stop)
|
96
107
|
@consumers&.each(&:stop)
|
97
108
|
@consumers = nil
|
98
109
|
@reply_consumer&.stop
|
@@ -124,7 +135,7 @@ module Mimi
|
|
124
135
|
# @param opts [Hash] additional options, e.g. :timeout
|
125
136
|
#
|
126
137
|
# @return [Hash]
|
127
|
-
# @raise [SomeError,
|
138
|
+
# @raise [SomeError,Timeout::Error]
|
128
139
|
#
|
129
140
|
def query(target, message, opts = {})
|
130
141
|
queue_name, method_name = target.split("/")
|
@@ -140,10 +151,7 @@ module Mimi
|
|
140
151
|
)
|
141
152
|
deliver_message_queue(queue_url, message)
|
142
153
|
timeout = opts[:timeout] || options[:mq_default_query_timeout]
|
143
|
-
response =
|
144
|
-
Timeout::timeout(timeout) do
|
145
|
-
response = reply_queue.pop
|
146
|
-
end
|
154
|
+
response = reply_queue.pop(true, timeout)
|
147
155
|
deserialize(response.body)
|
148
156
|
end
|
149
157
|
|
@@ -178,22 +186,7 @@ module Mimi
|
|
178
186
|
opts = opts.dup
|
179
187
|
queue_url = find_or_create_queue(queue_name)
|
180
188
|
@consumers << Consumer.new(self, queue_url) do |m|
|
181
|
-
|
182
|
-
deserialize(m.body),
|
183
|
-
deserialize_headers(m)
|
184
|
-
)
|
185
|
-
method_name = message.headers[:__method]
|
186
|
-
reply_to = message.headers[:__reply_queue_url]
|
187
|
-
if reply_to
|
188
|
-
response = processor.call_query(method_name, message, {})
|
189
|
-
response_message = Mimi::Messaging::Message.new(
|
190
|
-
response,
|
191
|
-
__request_id: message.headers[:__request_id]
|
192
|
-
)
|
193
|
-
deliver_message_queue(reply_to, response_message)
|
194
|
-
else
|
195
|
-
processor.call_command(method_name, message, {})
|
196
|
-
end
|
189
|
+
process_request_message(processor, m)
|
197
190
|
end
|
198
191
|
end
|
199
192
|
|
@@ -210,15 +203,52 @@ module Mimi
|
|
210
203
|
queue_url = find_or_create_queue(queue_name)
|
211
204
|
subscribe_topic_queue(topic_arn, queue_url)
|
212
205
|
@consumers << Consumer.new(self, queue_url) do |m|
|
213
|
-
|
214
|
-
deserialize(m.body),
|
215
|
-
deserialize_headers(m)
|
216
|
-
)
|
217
|
-
event_type = message.headers[:__event_type]
|
218
|
-
processor.call_event(event_type, message, {})
|
206
|
+
process_event_message(processor, m)
|
219
207
|
end
|
220
208
|
end
|
221
209
|
|
210
|
+
# Creates a new queue
|
211
|
+
#
|
212
|
+
# @param queue_name [String] name of the topic to be created
|
213
|
+
# @return [String] a new queue URL
|
214
|
+
#
|
215
|
+
def create_queue(queue_name)
|
216
|
+
fqn = sqs_sns_converted_full_name(queue_name)
|
217
|
+
Mimi::Messaging.log "Creating a queue: #{fqn}"
|
218
|
+
attrs = {}
|
219
|
+
if options[:mq_aws_sqs_sns_kms_master_key_id]
|
220
|
+
attrs["KmsMasterKeyId"] = options[:mq_aws_sqs_sns_kms_master_key_id]
|
221
|
+
end
|
222
|
+
result = sqs_client.create_queue(queue_name: fqn, attributes: attrs)
|
223
|
+
result.queue_url
|
224
|
+
rescue StandardError => e
|
225
|
+
raise Mimi::Messaging::ConnectionError, "Failed to create queue '#{queue_name}': #{e}"
|
226
|
+
end
|
227
|
+
|
228
|
+
# Finds a queue URL for a queue with given name.
|
229
|
+
#
|
230
|
+
# If an existing queue with this name is not found,
|
231
|
+
# the method will try to create a new one.
|
232
|
+
#
|
233
|
+
# @param queue_name [String]
|
234
|
+
# @return [String] a queue URL
|
235
|
+
#
|
236
|
+
def find_or_create_queue(queue_name)
|
237
|
+
queue_registry(queue_name) || create_queue(queue_name)
|
238
|
+
end
|
239
|
+
|
240
|
+
# Deletes a queue identified by the queue URL
|
241
|
+
#
|
242
|
+
# @param queue_url [String]
|
243
|
+
#
|
244
|
+
def delete_queue(queue_url)
|
245
|
+
Mimi::Messaging.log "Deleting a queue: #{queue_url}"
|
246
|
+
sqs_client.delete_queue(queue_url: queue_url)
|
247
|
+
rescue StandardError => e
|
248
|
+
raise Mimi::Messaging::ConnectionError,
|
249
|
+
"Failed to delete queue with url '#{queue_url}': #{e}"
|
250
|
+
end
|
251
|
+
|
222
252
|
private
|
223
253
|
|
224
254
|
# Returns configuration parameters for AWS SQS client
|
@@ -266,20 +296,6 @@ module Mimi
|
|
266
296
|
end
|
267
297
|
end
|
268
298
|
|
269
|
-
# Creates a new queue
|
270
|
-
#
|
271
|
-
# @param queue_name [String] name of the topic to be created
|
272
|
-
# @return [String] a new queue URL
|
273
|
-
#
|
274
|
-
def create_queue(queue_name)
|
275
|
-
fqn = sqs_sns_converted_full_name(queue_name)
|
276
|
-
Mimi::Messaging.log "Creating a queue: #{fqn}"
|
277
|
-
result = sqs_client.create_queue(queue_name: fqn)
|
278
|
-
result.queue_url
|
279
|
-
rescue StandardError => e
|
280
|
-
raise Mimi::Messaging::ConnectionError, "Failed to create queue '#{queue_name}': #{e}"
|
281
|
-
end
|
282
|
-
|
283
299
|
# Delivers a message to a queue with given URL.
|
284
300
|
#
|
285
301
|
# @param queue_url [String]
|
@@ -290,6 +306,7 @@ module Mimi
|
|
290
306
|
unless message.is_a?(Mimi::Messaging::Message)
|
291
307
|
raise ArgumentError, "Message is expected as argument"
|
292
308
|
end
|
309
|
+
|
293
310
|
Mimi::Messaging.log "Delivering message to: #{queue_url}, headers: #{message.headers}"
|
294
311
|
sqs_client.send_message(
|
295
312
|
queue_url: queue_url,
|
@@ -302,6 +319,47 @@ module Mimi
|
|
302
319
|
raise Mimi::Messaging::ConnectionError, "Failed to deliver message to '#{queue_url}': #{e}"
|
303
320
|
end
|
304
321
|
|
322
|
+
# Processes an incoming COMMAND or QUERY message
|
323
|
+
#
|
324
|
+
# @param processor [#call_query(),#call_command()] request processor object
|
325
|
+
# @param sqs_message
|
326
|
+
#
|
327
|
+
def process_request_message(processor, sqs_message)
|
328
|
+
message = Mimi::Messaging::Message.new(
|
329
|
+
deserialize(sqs_message.body),
|
330
|
+
deserialize_headers(sqs_message)
|
331
|
+
)
|
332
|
+
method_name = message.headers[:__method]
|
333
|
+
reply_to = message.headers[:__reply_queue_url]
|
334
|
+
if reply_to
|
335
|
+
response = processor.call_query(method_name, message, {})
|
336
|
+
response_message = Mimi::Messaging::Message.new(
|
337
|
+
response,
|
338
|
+
__request_id: message.headers[:__request_id]
|
339
|
+
)
|
340
|
+
deliver_query_response(reply_to, response_message)
|
341
|
+
else
|
342
|
+
processor.call_command(method_name, message, {})
|
343
|
+
end
|
344
|
+
end
|
345
|
+
|
346
|
+
# Delivers a message as a response to a QUERY
|
347
|
+
#
|
348
|
+
# Responses are allowed to fail. There can be a number of reasons
|
349
|
+
# why responses fail: reply queue does not exist (anymore?),
|
350
|
+
# response message is too big. In any case the error is reported,
|
351
|
+
# but the QUERY message is acknowledged as a successfully processed.
|
352
|
+
#
|
353
|
+
# @param queue_url [String]
|
354
|
+
# @param message [Mimi::Messaging::Message]
|
355
|
+
#
|
356
|
+
def deliver_query_response(queue_url, message)
|
357
|
+
deliver_message_queue(queue_url, message)
|
358
|
+
rescue Mimi::Messaging::ConnectionError => e
|
359
|
+
Mimi::Messaging.logger&.warn("Failed to deliver QRY response: #{e}")
|
360
|
+
# NOTE: error is recovered
|
361
|
+
end
|
362
|
+
|
305
363
|
# Returns URL of a queue with a given name.
|
306
364
|
#
|
307
365
|
# If the queue with given name does not exist, returns nil
|
@@ -349,27 +407,16 @@ module Mimi
|
|
349
407
|
)
|
350
408
|
end
|
351
409
|
|
352
|
-
# Finds a queue URL for a queue with given name.
|
353
|
-
#
|
354
|
-
# If an existing queue with this name is not found,
|
355
|
-
# the method will try to create a new one.
|
356
|
-
#
|
357
|
-
# @param queue_name [String]
|
358
|
-
# @return [String] a queue URL
|
359
|
-
#
|
360
|
-
def find_or_create_queue(queue_name)
|
361
|
-
queue_registry(queue_name) || create_queue(queue_name)
|
362
|
-
end
|
363
|
-
|
364
410
|
# Returns the configured reply listener for this process
|
365
411
|
#
|
366
412
|
# @return [ReplyConsumer]
|
367
413
|
#
|
368
414
|
def reply_consumer
|
369
|
-
@
|
370
|
-
|
371
|
-
|
372
|
-
|
415
|
+
@reply_consumer_mutex.synchronize do
|
416
|
+
@reply_consumer ||= begin
|
417
|
+
reply_queue_name = options[:mq_reply_queue_prefix] + SecureRandom.hex(8)
|
418
|
+
Mimi::Messaging::SQS_SNS::ReplyConsumer.new(self, reply_queue_name)
|
419
|
+
end
|
373
420
|
end
|
374
421
|
end
|
375
422
|
|
@@ -450,7 +497,11 @@ module Mimi
|
|
450
497
|
def create_topic(topic_name)
|
451
498
|
fqn = sqs_sns_converted_full_name(topic_name)
|
452
499
|
Mimi::Messaging.log "Creating a topic: #{fqn}"
|
453
|
-
|
500
|
+
attrs = {}
|
501
|
+
if options[:mq_aws_sqs_sns_kms_master_key_id]
|
502
|
+
attrs["KmsMasterKeyId"] = options[:mq_aws_sqs_sns_kms_master_key_id]
|
503
|
+
end
|
504
|
+
result = sns_client.create_topic(name: fqn, attributes: attrs)
|
454
505
|
result.topic_arn
|
455
506
|
rescue StandardError => e
|
456
507
|
raise Mimi::Messaging::ConnectionError, "Failed to create topic '#{topic_name}': #{e}"
|
@@ -467,7 +518,7 @@ module Mimi
|
|
467
518
|
)
|
468
519
|
queue_arn = result.attributes["QueueArn"]
|
469
520
|
Mimi::Messaging.log "Subscribing queue to a topic: '#{topic_arn}'->'#{queue_url}'"
|
470
|
-
|
521
|
+
_result = sns_client.subscribe(
|
471
522
|
topic_arn: topic_arn,
|
472
523
|
protocol: "sqs",
|
473
524
|
endpoint: queue_arn,
|
@@ -489,6 +540,7 @@ module Mimi
|
|
489
540
|
unless message.is_a?(Mimi::Messaging::Message)
|
490
541
|
raise ArgumentError, "Message is expected as argument"
|
491
542
|
end
|
543
|
+
|
492
544
|
Mimi::Messaging.log "Delivering message to: #{topic_arn}"
|
493
545
|
sns_client.publish(
|
494
546
|
topic_arn: topic_arn,
|
@@ -500,6 +552,46 @@ module Mimi
|
|
500
552
|
rescue StandardError => e
|
501
553
|
raise Mimi::Messaging::ConnectionError, "Failed to deliver message to '#{topic_arn}': #{e}"
|
502
554
|
end
|
555
|
+
|
556
|
+
# Processes an incoming EVENT message
|
557
|
+
#
|
558
|
+
# @param processor [#call_event()] event processor object
|
559
|
+
# @param sqs_message []
|
560
|
+
#
|
561
|
+
def process_event_message(processor, sqs_message)
|
562
|
+
message = Mimi::Messaging::Message.new(
|
563
|
+
deserialize(sqs_message.body),
|
564
|
+
deserialize_headers(sqs_message)
|
565
|
+
)
|
566
|
+
event_type = message.headers[:__event_type]
|
567
|
+
processor.call_event(event_type, message, {})
|
568
|
+
end
|
569
|
+
|
570
|
+
# Starts the worker pool using current configuration
|
571
|
+
#
|
572
|
+
# @return [Concurrent::ThreadPoolExecutor]
|
573
|
+
#
|
574
|
+
def start_worker_pool!
|
575
|
+
Mimi::Messaging.log "Starting worker pool, " \
|
576
|
+
"min_threads:#{options[:mq_worker_pool_min_threads]}, " \
|
577
|
+
"max_threads:#{options[:mq_worker_pool_max_threads]}, " \
|
578
|
+
"max_backlog:#{options[:mq_worker_pool_max_backlog]}"
|
579
|
+
|
580
|
+
@worker_pool = Concurrent::ThreadPoolExecutor.new(
|
581
|
+
min_threads: options[:mq_worker_pool_min_threads],
|
582
|
+
max_threads: options[:mq_worker_pool_max_threads],
|
583
|
+
max_queue: options[:mq_worker_pool_max_backlog],
|
584
|
+
fallback_policy: :abort
|
585
|
+
)
|
586
|
+
end
|
587
|
+
|
588
|
+
# Gracefully stops the worker pool, allowing all threads to finish their jobs
|
589
|
+
#
|
590
|
+
def stop_worker_pool!
|
591
|
+
Mimi::Messaging.log "Stopping worker pool"
|
592
|
+
@worker_pool.shutdown
|
593
|
+
@worker_pool.wait_for_termination
|
594
|
+
end
|
503
595
|
end # class Adapter
|
504
596
|
end # module SQS_SNS
|
505
597
|
end # module Messaging
|
@@ -4,25 +4,31 @@ module Mimi
|
|
4
4
|
module Messaging
|
5
5
|
module SQS_SNS
|
6
6
|
#
|
7
|
-
# Message
|
7
|
+
# Message consumer for SQS queues
|
8
8
|
#
|
9
9
|
class Consumer
|
10
|
+
# (seconds) determines how soon the NACK-ed message becomes visible to other consumers
|
11
|
+
NACK_VISIBILITY_TIMEOUT = 1
|
12
|
+
|
10
13
|
def initialize(adapter, queue_url, &block)
|
11
|
-
@block = block
|
12
14
|
@stop_requested = false
|
13
15
|
Mimi::Messaging.log "Starting consumer for: #{queue_url}"
|
14
16
|
@consumer_thread = Thread.new do
|
15
|
-
while not @stop_requested
|
16
|
-
|
17
|
-
next unless message
|
18
|
-
Mimi::Messaging.log "Read message from: #{queue_url}"
|
19
|
-
block.call(message)
|
20
|
-
ack_message(adapter, queue_url, message)
|
17
|
+
while not @stop_requested
|
18
|
+
read_and_process_message(adapter, queue_url, block)
|
21
19
|
end
|
22
20
|
Mimi::Messaging.log "Stopping consumer for: #{queue_url}"
|
23
21
|
end
|
24
22
|
end
|
25
23
|
|
24
|
+
# Requests the Consumer to stop, without actually waiting for it
|
25
|
+
#
|
26
|
+
def signal_stop
|
27
|
+
@stop_requested = true
|
28
|
+
end
|
29
|
+
|
30
|
+
# Requests the Consumer to stop AND waits until it does
|
31
|
+
#
|
26
32
|
def stop
|
27
33
|
@stop_requested = true
|
28
34
|
@consumer_thread.join
|
@@ -35,16 +41,24 @@ module Mimi
|
|
35
41
|
#
|
36
42
|
# @param adapter [Mimi::Messaging::SQS_SNS::Adapter]
|
37
43
|
# @param queue_url [String]
|
44
|
+
# @param block [Proc] a block to be invoked when a message is received
|
38
45
|
#
|
39
|
-
def read_and_process_message(adapter, queue_url)
|
46
|
+
def read_and_process_message(adapter, queue_url, block)
|
40
47
|
message = read_message(adapter, queue_url)
|
41
48
|
return unless message
|
49
|
+
|
42
50
|
Mimi::Messaging.log "Read message from: #{queue_url}"
|
43
|
-
|
44
|
-
|
51
|
+
adapter.worker_pool.post do
|
52
|
+
process_message(adapter, queue_url, message, block)
|
53
|
+
end
|
54
|
+
rescue Concurrent::RejectedExecutionError
|
55
|
+
# the backlog is overflown, put the message back
|
56
|
+
Mimi::Messaging.log "Worker pool backlog is full, nack-ing the message " \
|
57
|
+
"(workers:#{adapter.worker_pool.length}, backlog:#{adapter.worker_pool.queue_length})"
|
58
|
+
nack_message(adapter, queue_url, message)
|
45
59
|
rescue StandardError => e
|
46
60
|
Mimi::Messaging.logger&.error(
|
47
|
-
"#{self.class}: failed to read
|
61
|
+
"#{self.class}: failed to read or process message from: #{queue_url}," \
|
48
62
|
" error: (#{e.class}) #{e}"
|
49
63
|
)
|
50
64
|
end
|
@@ -58,14 +72,43 @@ module Mimi
|
|
58
72
|
)
|
59
73
|
return nil if result.messages.count == 0
|
60
74
|
return result.messages.first if result.messages.count == 1
|
75
|
+
|
61
76
|
raise Mimi::Messaging::ConnectionError, "Unexpected number of messages read"
|
62
77
|
end
|
63
78
|
|
79
|
+
def process_message(adapter, queue_url, message, block)
|
80
|
+
block.call(message)
|
81
|
+
ack_message(adapter, queue_url, message)
|
82
|
+
rescue Mimi::Messaging::NACK
|
83
|
+
Mimi::Messaging.log "NACK-ing message from: #{queue_url}"
|
84
|
+
nack_message(adapter, queue_url, message)
|
85
|
+
rescue StandardError => e
|
86
|
+
Mimi::Messaging.logger&.error(
|
87
|
+
"#{self.class}: failed to process message from: #{queue_url}," \
|
88
|
+
" error: (#{e.class}) #{e}"
|
89
|
+
)
|
90
|
+
# NOTE: message is neither ACKed or NACKed
|
91
|
+
end
|
92
|
+
|
93
|
+
# ACK-ing the message indicates successfull processing of it
|
94
|
+
# and removes the message from the queue
|
95
|
+
#
|
64
96
|
def ack_message(adapter, queue_url, msg)
|
65
97
|
adapter.sqs_client.delete_message(
|
66
98
|
queue_url: queue_url, receipt_handle: msg.receipt_handle
|
67
99
|
)
|
68
100
|
end
|
101
|
+
|
102
|
+
# NACK-ing the message indicates a failure to process the message.
|
103
|
+
# The message becomes immediately available to other consumers.
|
104
|
+
#
|
105
|
+
def nack_message(adapter, queue_url, msg)
|
106
|
+
adapter.sqs_client.change_message_visibility(
|
107
|
+
queue_url: queue_url,
|
108
|
+
receipt_handle: msg.receipt_handle,
|
109
|
+
visibility_timeout: NACK_VISIBILITY_TIMEOUT
|
110
|
+
)
|
111
|
+
end
|
69
112
|
end # class Consumer
|
70
113
|
end # module SQS_SNS
|
71
114
|
end # module Messaging
|
@@ -8,25 +8,23 @@ module Mimi
|
|
8
8
|
# and passes them to registered Queues (see Ruby ::Queue class).
|
9
9
|
#
|
10
10
|
class ReplyConsumer
|
11
|
-
attr_reader :reply_queue_url
|
11
|
+
attr_reader :reply_queue_name, :reply_queue_url
|
12
12
|
|
13
|
-
def initialize(adapter,
|
13
|
+
def initialize(adapter, reply_queue_name)
|
14
14
|
@mutex = Mutex.new
|
15
15
|
@queues = {}
|
16
16
|
@adapter = adapter
|
17
|
-
@
|
18
|
-
@consumer =
|
17
|
+
@reply_queue_name = reply_queue_name
|
18
|
+
@consumer = TemporaryQueueConsumer.new(adapter, reply_queue_name) do |message|
|
19
19
|
dispatch_message(message)
|
20
20
|
end
|
21
|
+
@reply_queue_url = @consumer.queue_url
|
21
22
|
end
|
22
23
|
|
23
24
|
def stop
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
raise Mimi::Messaging::Error, "Failed to stop consumer: #{e}"
|
28
|
-
end
|
29
|
-
# TODO: adapter.sqs_client.delete_queue(reply_queue_url)
|
25
|
+
@consumer.stop
|
26
|
+
rescue StandardError => e
|
27
|
+
raise Mimi::Messaging::Error, "Failed to stop reply consumer: #{e}"
|
30
28
|
end
|
31
29
|
|
32
30
|
# Register a new request_id to listen for.
|
@@ -38,7 +36,7 @@ module Mimi
|
|
38
36
|
# @return [Queue] a new Queue object registered for this request_id
|
39
37
|
#
|
40
38
|
def register_request_id(request_id)
|
41
|
-
queue =
|
39
|
+
queue = TimeoutQueue.new
|
42
40
|
@mutex.synchronize do
|
43
41
|
queue = @queues[request_id] ||= queue
|
44
42
|
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Mimi
|
4
|
+
module Messaging
|
5
|
+
module SQS_SNS
|
6
|
+
#
|
7
|
+
# Temporary queue consumer creates a temporary queue
|
8
|
+
# and attaches to it. The queue will be deleted
|
9
|
+
# on consumer shutdown.
|
10
|
+
#
|
11
|
+
class TemporaryQueueConsumer
|
12
|
+
attr_reader :queue_url
|
13
|
+
|
14
|
+
def initialize(adapter, queue_name, &block)
|
15
|
+
@adapter = adapter
|
16
|
+
@queue_url = adapter.find_or_create_queue(queue_name)
|
17
|
+
@consumer = Consumer.new(adapter, @queue_url, &block)
|
18
|
+
end
|
19
|
+
|
20
|
+
def stop
|
21
|
+
@consumer.stop
|
22
|
+
@adapter.delete_queue(queue_url)
|
23
|
+
rescue StandardError => e
|
24
|
+
raise Mimi::Messaging::Error, "Failed to stop temporary queue consumer: #{e}"
|
25
|
+
end
|
26
|
+
end # class TemporaryQueueConsumer
|
27
|
+
end # module SQS_SNS
|
28
|
+
end # module Messaging
|
29
|
+
end # module Mimi
|
@@ -0,0 +1,65 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "timeout"
|
4
|
+
|
5
|
+
module Mimi
|
6
|
+
module Messaging
|
7
|
+
module SQS_SNS
|
8
|
+
# TimeoutQueue solves the problem the native Ruby Queue class has with waiting for elements.
|
9
|
+
#
|
10
|
+
# See the excellent blog post discussing the issue:
|
11
|
+
# https://medium.com/workday-engineering/ruby-concurrency-building-a-timeout-queue-5d7c588ca80d
|
12
|
+
#
|
13
|
+
# TLDR -- using Ruby standard Timeout.timeout() around Queue#pop() is unsafe
|
14
|
+
#
|
15
|
+
#
|
16
|
+
class TimeoutQueue
|
17
|
+
def initialize
|
18
|
+
@elems = []
|
19
|
+
@mutex = Mutex.new
|
20
|
+
@cond_var = ConditionVariable.new
|
21
|
+
end
|
22
|
+
|
23
|
+
# Pushes an element into the queue
|
24
|
+
#
|
25
|
+
# @param elem [Object]
|
26
|
+
#
|
27
|
+
def <<(elem)
|
28
|
+
@mutex.synchronize do
|
29
|
+
@elems << elem
|
30
|
+
@cond_var.signal
|
31
|
+
end
|
32
|
+
end
|
33
|
+
alias push <<
|
34
|
+
|
35
|
+
# Pops an element from the queue in either non-blocking
|
36
|
+
# or a blocking (with an optional timeout) way.
|
37
|
+
#
|
38
|
+
# @param blocking [true,false] wait for a new element (true) or return immediately
|
39
|
+
# @param timeout [nil,Integer] if in blocking mode,
|
40
|
+
# wait at most given number of seconds or forever (nil)
|
41
|
+
# @raise [Timeout::Error] if a timeout in blocking mode was reached
|
42
|
+
#
|
43
|
+
def pop(blocking = true, timeout = nil)
|
44
|
+
@mutex.synchronize do
|
45
|
+
if blocking
|
46
|
+
if timeout.nil?
|
47
|
+
while @elems.empty?
|
48
|
+
@cond_var.wait(@mutex)
|
49
|
+
end
|
50
|
+
else
|
51
|
+
timeout_time = Time.now.to_f + timeout
|
52
|
+
while @elems.empty? && (remaining_time = timeout_time - Time.now.to_f) > 0
|
53
|
+
@cond_var.wait(@mutex, remaining_time)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
raise Timeout::Error, "queue timeout expired" if @elems.empty?
|
58
|
+
|
59
|
+
@elems.shift
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end # class TimeoutQueue
|
63
|
+
end # module SQS_SNS
|
64
|
+
end # module Messaging
|
65
|
+
end # module Mimi
|
@@ -36,9 +36,10 @@ Gem::Specification.new do |spec|
|
|
36
36
|
spec.add_dependency "mimi-messaging", "~> 1.2"
|
37
37
|
spec.add_dependency "aws-sdk-sqs", "~> 1.22"
|
38
38
|
spec.add_dependency "aws-sdk-sns", "~> 1.19"
|
39
|
+
spec.add_dependency "concurrent-ruby"
|
39
40
|
|
40
41
|
spec.add_development_dependency "bundler", "~> 2.0"
|
41
42
|
spec.add_development_dependency "pry", "~> 0.12"
|
42
|
-
spec.add_development_dependency "rake", "~>
|
43
|
+
spec.add_development_dependency "rake", "~> 12.0", ">= 12.3.3"
|
43
44
|
spec.add_development_dependency "rspec", "~> 3.0"
|
44
45
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: mimi-messaging-sqs_sns
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.8.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Alex Kukushkin
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2021-03-15 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: mimi-messaging
|
@@ -52,6 +52,20 @@ dependencies:
|
|
52
52
|
- - "~>"
|
53
53
|
- !ruby/object:Gem::Version
|
54
54
|
version: '1.19'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: concurrent-ruby
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
55
69
|
- !ruby/object:Gem::Dependency
|
56
70
|
name: bundler
|
57
71
|
requirement: !ruby/object:Gem::Requirement
|
@@ -86,14 +100,20 @@ dependencies:
|
|
86
100
|
requirements:
|
87
101
|
- - "~>"
|
88
102
|
- !ruby/object:Gem::Version
|
89
|
-
version: '
|
103
|
+
version: '12.0'
|
104
|
+
- - ">="
|
105
|
+
- !ruby/object:Gem::Version
|
106
|
+
version: 12.3.3
|
90
107
|
type: :development
|
91
108
|
prerelease: false
|
92
109
|
version_requirements: !ruby/object:Gem::Requirement
|
93
110
|
requirements:
|
94
111
|
- - "~>"
|
95
112
|
- !ruby/object:Gem::Version
|
96
|
-
version: '
|
113
|
+
version: '12.0'
|
114
|
+
- - ">="
|
115
|
+
- !ruby/object:Gem::Version
|
116
|
+
version: 12.3.3
|
97
117
|
- !ruby/object:Gem::Dependency
|
98
118
|
name: rspec
|
99
119
|
requirement: !ruby/object:Gem::Requirement
|
@@ -118,14 +138,17 @@ files:
|
|
118
138
|
- ".gitignore"
|
119
139
|
- ".rspec"
|
120
140
|
- ".travis.yml"
|
141
|
+
- CHANGELOG.md
|
121
142
|
- CODE_OF_CONDUCT.md
|
122
143
|
- Gemfile
|
123
144
|
- LICENSE.txt
|
124
145
|
- README.md
|
125
146
|
- Rakefile
|
147
|
+
- TODO.md
|
126
148
|
- bin/console
|
127
149
|
- bin/setup
|
128
150
|
- examples/event.rb
|
151
|
+
- examples/mass-query.rb
|
129
152
|
- examples/query.rb
|
130
153
|
- examples/responder.rb
|
131
154
|
- examples/subscriber.rb
|
@@ -133,6 +156,8 @@ files:
|
|
133
156
|
- lib/mimi/messaging/sqs_sns/adapter.rb
|
134
157
|
- lib/mimi/messaging/sqs_sns/consumer.rb
|
135
158
|
- lib/mimi/messaging/sqs_sns/reply_consumer.rb
|
159
|
+
- lib/mimi/messaging/sqs_sns/temporary_queue_consumer.rb
|
160
|
+
- lib/mimi/messaging/sqs_sns/timeout_queue.rb
|
136
161
|
- lib/mimi/messaging/sqs_sns/version.rb
|
137
162
|
- mimi-messaging-sqs_sns.gemspec
|
138
163
|
homepage: https://github.com/kukushkin/mimi-messaging-sqs_sns
|
@@ -157,8 +182,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
157
182
|
- !ruby/object:Gem::Version
|
158
183
|
version: '0'
|
159
184
|
requirements: []
|
160
|
-
|
161
|
-
rubygems_version: 2.6.14.4
|
185
|
+
rubygems_version: 3.1.2
|
162
186
|
signing_key:
|
163
187
|
specification_version: 4
|
164
188
|
summary: AWS SQS/SNS adapter for mimi-messaging
|