eventq_aws 0.1.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/bin/console +14 -0
- data/bin/setup +8 -0
- data/lib/eventq_aws/aws_eventq_client.rb +28 -0
- data/lib/eventq_aws/aws_queue_client.rb +28 -0
- data/lib/eventq_aws/aws_queue_manager.rb +35 -0
- data/lib/eventq_aws/aws_queue_worker.rb +145 -0
- data/lib/eventq_aws/aws_subscription_manager.rb +53 -0
- data/lib/eventq_aws/version.rb +3 -0
- data/lib/eventq_aws.rb +6 -0
- metadata +152 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 72dc6e5eedbb1a486df5fb6ae630c9ada26a08b5
|
4
|
+
data.tar.gz: 953364700ea24b683038d3fbd4b2dd5dffbfa7bd
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 66b70231f26a2723ff5a37104991d90d562ae6c97e52619472e174aff95e49b53a5f3362bb97718668fc8e2c070746dde9a9426a1086fb784176dfeb87518814
|
7
|
+
data.tar.gz: 0633ab90abfc5d3accfd9803ce68967f10ce5de746ef29fa40b49335e7bd73805c7790c32d1e0f9707872f930e7138a0c512ad7e6b4caf3e9ffbd5633b6b3f0c
|
data/bin/console
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "bundler/setup"
|
4
|
+
require "eventq_aws"
|
5
|
+
|
6
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
7
|
+
# with your gem easier. You can also use a different console, if you like.
|
8
|
+
|
9
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
10
|
+
# require "pry"
|
11
|
+
# Pry.start
|
12
|
+
|
13
|
+
require "irb"
|
14
|
+
IRB.start
|
data/bin/setup
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'oj'
|
2
|
+
|
3
|
+
class AwsEventQClient
|
4
|
+
def initialize
|
5
|
+
@client = AwsQueueClient.new
|
6
|
+
end
|
7
|
+
|
8
|
+
def raise(event_type, event)
|
9
|
+
|
10
|
+
topic_arn = @client.get_topic_arn(event_type)
|
11
|
+
|
12
|
+
qm = QueueMessage.new
|
13
|
+
qm.content = event
|
14
|
+
qm.type = event_type
|
15
|
+
|
16
|
+
message = Oj.dump(qm)
|
17
|
+
|
18
|
+
response = @client.sns.publish({
|
19
|
+
topic_arn: topic_arn,
|
20
|
+
message: message,
|
21
|
+
subject: event_type
|
22
|
+
})
|
23
|
+
|
24
|
+
return response.message_id
|
25
|
+
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
class AwsQueueClient
|
2
|
+
|
3
|
+
attr_reader :sns
|
4
|
+
attr_reader :sqs
|
5
|
+
|
6
|
+
def initialize
|
7
|
+
|
8
|
+
Aws.config[:credentials] = Aws::Credentials.new(ENV['AWS_KEY'], ENV['AWS_SECRET'])
|
9
|
+
Aws.config[:region] = ENV['AWS_REGION'] || 'us-west-2'
|
10
|
+
|
11
|
+
@sns = Aws::SNS::Client.new
|
12
|
+
@sqs = Aws::SQS::Client.new
|
13
|
+
|
14
|
+
@aws_account = ENV['AWS_ACCOUNT_NUMBER']
|
15
|
+
@aws_region = ENV['AWS_REGION'] || 'us-west-2'
|
16
|
+
|
17
|
+
end
|
18
|
+
|
19
|
+
def get_topic_arn(event_type)
|
20
|
+
response = @sns.create_topic({ name: event_type })
|
21
|
+
return response.topic_arn
|
22
|
+
end
|
23
|
+
|
24
|
+
def get_queue_arn(queue)
|
25
|
+
return "arn:aws:sqs:#{@aws_region}:#{@aws_account}:#{queue.name}"
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
class AwsQueueManager
|
2
|
+
|
3
|
+
@@dead_letter_queue = 'dead_letter_archive'
|
4
|
+
|
5
|
+
def initialize
|
6
|
+
@client = AwsQueueClient.new
|
7
|
+
end
|
8
|
+
|
9
|
+
def get_queue(queue)
|
10
|
+
|
11
|
+
response = @client.sqs.create_queue({
|
12
|
+
queue_name: queue.name,
|
13
|
+
attributes: {
|
14
|
+
'VisibilityTimeout' => (queue.retry_delay / 1000).to_s
|
15
|
+
}
|
16
|
+
})
|
17
|
+
|
18
|
+
return response.queue_url
|
19
|
+
|
20
|
+
end
|
21
|
+
|
22
|
+
def drop_queue(queue)
|
23
|
+
|
24
|
+
q = get_queue(queue)
|
25
|
+
|
26
|
+
@client.sqs.delete_queue({ queue_url: q})
|
27
|
+
|
28
|
+
end
|
29
|
+
|
30
|
+
def drop_topic(event_type)
|
31
|
+
topic_arn = @client.get_topic_arn(event_type)
|
32
|
+
@client.sns.delete_topic({ topic_arn: topic_arn})
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
@@ -0,0 +1,145 @@
|
|
1
|
+
class AwsQueueWorker
|
2
|
+
|
3
|
+
attr_accessor :is_running
|
4
|
+
|
5
|
+
def initialize
|
6
|
+
@threads = []
|
7
|
+
@is_running = false
|
8
|
+
|
9
|
+
@retry_exceeded_block = nil
|
10
|
+
end
|
11
|
+
|
12
|
+
def start(queue, options = {}, &block)
|
13
|
+
configure(queue, options)
|
14
|
+
|
15
|
+
puts '[QUEUE_WORKER] Listening for messages.'
|
16
|
+
|
17
|
+
raise 'Worker is already running.' if running?
|
18
|
+
|
19
|
+
@is_running = true
|
20
|
+
@threads = []
|
21
|
+
|
22
|
+
#loop through each thread count
|
23
|
+
@thread_count.times do
|
24
|
+
thr = Thread.new do
|
25
|
+
|
26
|
+
client = AwsQueueClient.new
|
27
|
+
manager = AwsQueueManager.new
|
28
|
+
|
29
|
+
#begin the queue loop for this thread
|
30
|
+
while true do
|
31
|
+
|
32
|
+
#check if the worker is still allowed to run and break out of thread loop if not
|
33
|
+
if !@is_running
|
34
|
+
break
|
35
|
+
end
|
36
|
+
|
37
|
+
#get the queue
|
38
|
+
q = manager.get_queue(queue)
|
39
|
+
|
40
|
+
received = false
|
41
|
+
error = false
|
42
|
+
|
43
|
+
begin
|
44
|
+
|
45
|
+
#request a message from the queue
|
46
|
+
response = client.sqs.receive_message({
|
47
|
+
queue_url: q,
|
48
|
+
max_number_of_messages: 1,
|
49
|
+
wait_time_seconds: 1,
|
50
|
+
message_attribute_names: ['ApproximateReceiveCount']
|
51
|
+
})
|
52
|
+
|
53
|
+
#check that a message was received
|
54
|
+
if response.messages.length > 0
|
55
|
+
|
56
|
+
msg = response.messages[0]
|
57
|
+
retry_attempts = msg.message_attributes['ApproximateReceiveCount'].to_i
|
58
|
+
|
59
|
+
#deserialize the message payload
|
60
|
+
message = Oj.load(msg.body)
|
61
|
+
payload = Oj.load(message["Message"])
|
62
|
+
|
63
|
+
puts "[QUEUE_WORKER] Message received. Retry Attempts: #{retry_attempts}"
|
64
|
+
|
65
|
+
#begin worker block for queue message
|
66
|
+
begin
|
67
|
+
block.call(payload.content, payload.type, retry_attempts)
|
68
|
+
|
69
|
+
#accept the message as processed
|
70
|
+
client.sqs.delete_message({ queue_url: q, receipt_handle: msg.receipt_handle })
|
71
|
+
puts '[QUEUE_WORKER] Message acknowledged.'
|
72
|
+
received = true
|
73
|
+
rescue => e
|
74
|
+
puts '[QUEUE_WORKER] An unhandled error happened attempting to process a queue message.'
|
75
|
+
puts "Error: #{e}"
|
76
|
+
|
77
|
+
error = true
|
78
|
+
puts '[QUEUE_WORKER] Message rejected.'
|
79
|
+
|
80
|
+
if !queue.allow_retry
|
81
|
+
#remove the message from the queue so that it does not get retried
|
82
|
+
client.sqs.delete_message({ queue_url: q, receipt_handle: msg.receipt_handle })
|
83
|
+
end
|
84
|
+
|
85
|
+
end
|
86
|
+
|
87
|
+
end
|
88
|
+
|
89
|
+
rescue
|
90
|
+
puts 'An error occured attempting to retrieve a message from the queue.'
|
91
|
+
end
|
92
|
+
|
93
|
+
#check if any message was received
|
94
|
+
if !received && !error
|
95
|
+
puts "[QUEUE_WORKER] No message received. Sleeping for #{@sleep} seconds"
|
96
|
+
#no message received so sleep before attempting to pop another message from the queue
|
97
|
+
sleep(@sleep)
|
98
|
+
end
|
99
|
+
|
100
|
+
end
|
101
|
+
|
102
|
+
end
|
103
|
+
@threads.push(thr)
|
104
|
+
|
105
|
+
end
|
106
|
+
|
107
|
+
if options.key?(:wait) && options[:wait] == true
|
108
|
+
@threads.each { |thr| thr.join }
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
def stop
|
113
|
+
@is_running = false
|
114
|
+
@threads.each { |thr| thr.join }
|
115
|
+
end
|
116
|
+
|
117
|
+
def on_retry_exceeded(&block)
|
118
|
+
@retry_exceeded_block = block
|
119
|
+
end
|
120
|
+
|
121
|
+
def running?
|
122
|
+
@is_running
|
123
|
+
end
|
124
|
+
|
125
|
+
private
|
126
|
+
|
127
|
+
def configure(queue, options = {})
|
128
|
+
|
129
|
+
@queue = queue
|
130
|
+
|
131
|
+
#default thread count
|
132
|
+
@thread_count = 5
|
133
|
+
if options.key?(:thread_count)
|
134
|
+
@thread_count = options[:thread_count]
|
135
|
+
end
|
136
|
+
|
137
|
+
#default sleep time in seconds
|
138
|
+
@sleep = 15
|
139
|
+
if options.key?(:sleep)
|
140
|
+
@sleep = options[:sleep]
|
141
|
+
end
|
142
|
+
|
143
|
+
end
|
144
|
+
|
145
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
class AwsSubscriptionManager
|
2
|
+
|
3
|
+
def initialize
|
4
|
+
@client = AwsQueueClient.new
|
5
|
+
@manager = AwsQueueManager.new
|
6
|
+
end
|
7
|
+
|
8
|
+
def subscribe(event_type, queue)
|
9
|
+
|
10
|
+
topic_arn = @client.get_topic_arn(event_type)
|
11
|
+
|
12
|
+
q = @manager.get_queue(queue)
|
13
|
+
queue_arn = @client.get_queue_arn(queue)
|
14
|
+
|
15
|
+
@client.sqs.set_queue_attributes({
|
16
|
+
queue_url: q,
|
17
|
+
attributes:{
|
18
|
+
'Policy' => '{
|
19
|
+
"Version": "2012-10-17",
|
20
|
+
"Id": "SNStoSQS",
|
21
|
+
"Statement": [
|
22
|
+
{
|
23
|
+
"Sid":"rule1",
|
24
|
+
"Effect": "Allow",
|
25
|
+
"Principal": "*",
|
26
|
+
"Action": "sqs:*",
|
27
|
+
"Resource": "' + queue_arn + '",
|
28
|
+
"Condition" : {
|
29
|
+
"ArnEquals" : {
|
30
|
+
"aws:SourceArn":"' + topic_arn + '"
|
31
|
+
}
|
32
|
+
}
|
33
|
+
}
|
34
|
+
]
|
35
|
+
}'
|
36
|
+
}
|
37
|
+
})
|
38
|
+
|
39
|
+
@client.sns.subscribe({
|
40
|
+
topic_arn: topic_arn,
|
41
|
+
protocol: 'sqs',
|
42
|
+
endpoint: queue_arn
|
43
|
+
})
|
44
|
+
|
45
|
+
end
|
46
|
+
|
47
|
+
def unsubscribe(queue)
|
48
|
+
|
49
|
+
raise 'Not implemented. Please unsubscribe the queue from the topic inside the AWS Management Console.'
|
50
|
+
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
data/lib/eventq_aws.rb
ADDED
metadata
ADDED
@@ -0,0 +1,152 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: eventq_aws
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- vaughanbrittonsage
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2016-04-29 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.11'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.11'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '10.0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '10.0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rspec
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: pry
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: oj
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :runtime
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: aws-sdk-core
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
type: :runtime
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ">="
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: eventq_base
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - ">="
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '0'
|
104
|
+
type: :runtime
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - ">="
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0'
|
111
|
+
description: This is the aws implementation for EventQ
|
112
|
+
email:
|
113
|
+
- vaughanbritton@gmail.com
|
114
|
+
executables: []
|
115
|
+
extensions: []
|
116
|
+
extra_rdoc_files: []
|
117
|
+
files:
|
118
|
+
- bin/console
|
119
|
+
- bin/setup
|
120
|
+
- lib/eventq_aws.rb
|
121
|
+
- lib/eventq_aws/aws_eventq_client.rb
|
122
|
+
- lib/eventq_aws/aws_queue_client.rb
|
123
|
+
- lib/eventq_aws/aws_queue_manager.rb
|
124
|
+
- lib/eventq_aws/aws_queue_worker.rb
|
125
|
+
- lib/eventq_aws/aws_subscription_manager.rb
|
126
|
+
- lib/eventq_aws/version.rb
|
127
|
+
homepage: https://github.com/vaughanbrittonsage/eventq
|
128
|
+
licenses:
|
129
|
+
- MIT
|
130
|
+
metadata: {}
|
131
|
+
post_install_message:
|
132
|
+
rdoc_options: []
|
133
|
+
require_paths:
|
134
|
+
- lib
|
135
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
136
|
+
requirements:
|
137
|
+
- - ">="
|
138
|
+
- !ruby/object:Gem::Version
|
139
|
+
version: '0'
|
140
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
141
|
+
requirements:
|
142
|
+
- - ">="
|
143
|
+
- !ruby/object:Gem::Version
|
144
|
+
version: '0'
|
145
|
+
requirements: []
|
146
|
+
rubyforge_project:
|
147
|
+
rubygems_version: 2.5.1
|
148
|
+
signing_key:
|
149
|
+
specification_version: 4
|
150
|
+
summary: This is the aws implementation for EventQ
|
151
|
+
test_files: []
|
152
|
+
has_rdoc:
|