alephant-publisher-queue 2.3.1 → 2.4.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 +4 -4
- data/.travis.yml +22 -7
- data/README.md +109 -3
- data/alephant-publisher-queue.gemspec +2 -0
- data/lib/alephant/publisher/queue.rb +4 -86
- data/lib/alephant/publisher/queue/options.rb +28 -19
- data/lib/alephant/publisher/queue/processor.rb +11 -9
- data/lib/alephant/publisher/queue/publisher.rb +89 -0
- data/lib/alephant/publisher/queue/revalidate_processor.rb +115 -0
- data/lib/alephant/publisher/queue/revalidate_writer.rb +96 -0
- data/lib/alephant/publisher/queue/version.rb +1 -1
- data/lib/alephant/publisher/queue/writer.rb +23 -2
- data/spec/alephant/publisher/queue/revalidate_processor_spec.rb +140 -0
- data/spec/alephant/publisher/queue/revalidate_writer_spec.rb +96 -0
- metadata +94 -60
- data/lib/alephant/publisher/queue/base_processor.rb +0 -13
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e90e5d717583700bb627398ffec0f305383ab17b
|
4
|
+
data.tar.gz: 7f6fdf2a4159bb0276e512829bb9d8d4788775e5
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: acdc9518ef0f946002f0e6b07a8cb122076e6d9661b37fcee23339bdc221226b01c9d7300d936879f0fd919591701ce8749bcaf451217eb97c0532f6781f4bd6
|
7
|
+
data.tar.gz: cf959951bd12ec476daf6e473560b0c73ac52a3d6eb29f9ce21419af704298cae47a861eac42e66a2f6f96776d4e168c7c134ac519f768c08ab970810ac62b93
|
data/.travis.yml
CHANGED
@@ -1,9 +1,24 @@
|
|
1
|
+
sudo: false
|
2
|
+
cache: bundler
|
1
3
|
language: ruby
|
4
|
+
|
5
|
+
# Ruby build matrix
|
2
6
|
rvm:
|
3
|
-
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
7
|
+
- 2.0
|
8
|
+
- 2.1
|
9
|
+
- 2.2
|
10
|
+
- 2.3.0
|
11
|
+
- 2.3.1
|
12
|
+
- jruby
|
13
|
+
|
14
|
+
# Environment variables
|
15
|
+
env:
|
16
|
+
- RUBYOPT="-W0"
|
17
|
+
|
18
|
+
# Ensure we don't build for *every* commit (doesn't apply to PR builds)
|
19
|
+
branches:
|
20
|
+
only:
|
21
|
+
- master
|
22
|
+
|
23
|
+
script:
|
24
|
+
- bundle exec rspec --format documentation
|
data/README.md
CHANGED
@@ -11,6 +11,7 @@ Static publishing to S3 based on SQS messages.
|
|
11
11
|
- S3 bucket.
|
12
12
|
- SQS Queue.
|
13
13
|
- Dynamo DB table.
|
14
|
+
- Elasticache (if using the "revalidate" pattern)
|
14
15
|
|
15
16
|
## Migrating from [Alephant::Publisher](https://github.com/BBC-News/alephant-publisher)
|
16
17
|
|
@@ -91,7 +92,7 @@ end
|
|
91
92
|
{{ content }}
|
92
93
|
```
|
93
94
|
|
94
|
-
## Usage
|
95
|
+
## Usage (standard setup - non-revalidate)
|
95
96
|
|
96
97
|
```ruby
|
97
98
|
require "alephant/logger"
|
@@ -101,9 +102,9 @@ module MyApp
|
|
101
102
|
def self.run!
|
102
103
|
loop do
|
103
104
|
Alephant::Publisher::Queue.create(options).run!
|
104
|
-
rescue => e
|
105
|
-
Alephant::Logger.get_logger.warn "Error: #{e.message}"
|
106
105
|
end
|
106
|
+
rescue => e
|
107
|
+
Alephant::Logger.get_logger.error "Error: #{e.message}"
|
107
108
|
end
|
108
109
|
|
109
110
|
private
|
@@ -150,6 +151,111 @@ S3 Path:
|
|
150
151
|
S3 / bucket-id / example-s3-path / renderer-id / foo / 7e0c33c476b1089500d5f172102ec03e / 1
|
151
152
|
```
|
152
153
|
|
154
|
+
## Usage (revalidate pattern)
|
155
|
+
|
156
|
+
```ruby
|
157
|
+
require "addressable/uri"
|
158
|
+
require "alephant/logger"
|
159
|
+
require "alephant/publisher/queue"
|
160
|
+
|
161
|
+
module MyApp
|
162
|
+
class UrlGenerator
|
163
|
+
class << self
|
164
|
+
# This function is called to generate the URL to be requested as
|
165
|
+
# part of the rendering process. The return must be a URL as a string.
|
166
|
+
def generate(opts)
|
167
|
+
"http://example.com/?#{url_params(opts)}"
|
168
|
+
end
|
169
|
+
|
170
|
+
private
|
171
|
+
|
172
|
+
def url_params(params_hash)
|
173
|
+
uri = Addressable::URI.new
|
174
|
+
uri.query_values = params_hash
|
175
|
+
uri.query
|
176
|
+
end
|
177
|
+
end
|
178
|
+
end
|
179
|
+
|
180
|
+
class HttpResponseProcessor
|
181
|
+
class << self
|
182
|
+
# This function is called upon a successful HTTP response.
|
183
|
+
#
|
184
|
+
# Use it to modify or process the response of your HTTP request
|
185
|
+
# as you please, but there is one rule - the return value MUST
|
186
|
+
# be a JSON object.
|
187
|
+
def process(opts, status, body)
|
188
|
+
# our response is already JSON, pass it through
|
189
|
+
body
|
190
|
+
end
|
191
|
+
|
192
|
+
# If you wish to vary your revalidate TTL on a per-endpoint (or
|
193
|
+
# other logic) basis, you can do it here - simply return an Integer
|
194
|
+
# value.
|
195
|
+
#
|
196
|
+
# If nil is returned the 'revalidate_cache_ttl' config setting on the
|
197
|
+
# broker will be used as the default TTL, otherwise the default in
|
198
|
+
# 'alephant-broker' will be used.
|
199
|
+
def self.ttl(opts)
|
200
|
+
# 30s revalidate time for all
|
201
|
+
30
|
202
|
+
end
|
203
|
+
end
|
204
|
+
end
|
205
|
+
|
206
|
+
def self.run!
|
207
|
+
loop do
|
208
|
+
Alephant::Publisher::Queue.create(options, processor).run!
|
209
|
+
end
|
210
|
+
rescue => e
|
211
|
+
Alephant::Logger.get_logger.error "Error: #{e.message}"
|
212
|
+
end
|
213
|
+
|
214
|
+
private
|
215
|
+
|
216
|
+
def self.processor
|
217
|
+
Alephant::Publisher::Queue::RevalidateProcessor.new(options, UrlGenerator, HttpResponseProcessor)
|
218
|
+
end
|
219
|
+
|
220
|
+
def self.options
|
221
|
+
Alephant::Publisher::Queue::Options.new.tap do |opts|
|
222
|
+
opts.add_queue(
|
223
|
+
:aws_account_id => 'example',
|
224
|
+
:sqs_queue_name => 'test_queue'
|
225
|
+
)
|
226
|
+
opts.add_writer(
|
227
|
+
:lookup_table_name => 'lookup-dynamo-table',
|
228
|
+
:s3_bucket_id => 'bucket-id',
|
229
|
+
:s3_object_path => 'example-s3-path',
|
230
|
+
:view_path => 'path/to/views'
|
231
|
+
)
|
232
|
+
opts.add_cache(
|
233
|
+
:elasticache_config_endpoint => 'example',
|
234
|
+
:elasticache_cache_version => '100',
|
235
|
+
:revalidate_cache_ttl => '30'
|
236
|
+
)
|
237
|
+
end
|
238
|
+
end
|
239
|
+
end
|
240
|
+
```
|
241
|
+
|
242
|
+
Add a message to your SQS queue, with the following format (`id`, `batch_id`, `options`):
|
243
|
+
|
244
|
+
```json
|
245
|
+
{
|
246
|
+
"id": "renderer_id",
|
247
|
+
"batch_id": null,
|
248
|
+
"options": {
|
249
|
+
"id": "foo",
|
250
|
+
"type": "chart"
|
251
|
+
}
|
252
|
+
}
|
253
|
+
```
|
254
|
+
|
255
|
+
This will then make a HTTP GET request to the configured endpoint (via `UrlGenerator`), process the response (via `HttpResponseProcessor`), render and store your content.
|
256
|
+
|
257
|
+
You will not ordinarily need to push messages onto SQS manually, this will be handled via the broker in real use.
|
258
|
+
|
153
259
|
## Preview Server
|
154
260
|
|
155
261
|
[Alephant Preview](https://github.com/BBC-News/alephant-preview) allows you to see the HTML generated by your templates, both standalone and in the context of a page.
|
@@ -32,6 +32,8 @@ Gem::Specification.new do |spec|
|
|
32
32
|
spec.add_runtime_dependency "rake"
|
33
33
|
spec.add_runtime_dependency "aws-sdk", "~> 1.0"
|
34
34
|
spec.add_runtime_dependency "crimp"
|
35
|
+
spec.add_runtime_dependency "faraday"
|
36
|
+
spec.add_runtime_dependency "dalli-elasticache"
|
35
37
|
spec.add_runtime_dependency "alephant-support"
|
36
38
|
spec.add_runtime_dependency "alephant-sequencer", "~> 3"
|
37
39
|
spec.add_runtime_dependency "alephant-cache"
|
@@ -2,9 +2,11 @@ require_relative "env"
|
|
2
2
|
|
3
3
|
require "alephant/publisher/queue/version"
|
4
4
|
require "alephant/publisher/queue/options"
|
5
|
+
require "alephant/publisher/queue/publisher"
|
5
6
|
require "alephant/publisher/queue/sqs_helper/queue"
|
6
7
|
require "alephant/publisher/queue/sqs_helper/archiver"
|
7
8
|
require "alephant/publisher/queue/processor"
|
9
|
+
require "alephant/publisher/queue/revalidate_processor"
|
8
10
|
require "alephant/logger"
|
9
11
|
require "alephant/cache"
|
10
12
|
require "json"
|
@@ -12,94 +14,10 @@ require "json"
|
|
12
14
|
module Alephant
|
13
15
|
module Publisher
|
14
16
|
module Queue
|
15
|
-
def self.create(opts
|
16
|
-
processor ||= Processor.new(opts
|
17
|
+
def self.create(opts, processor = nil)
|
18
|
+
processor ||= Processor.new(opts)
|
17
19
|
Publisher.new(opts, processor)
|
18
20
|
end
|
19
|
-
|
20
|
-
class Publisher
|
21
|
-
include Alephant::Logger
|
22
|
-
|
23
|
-
VISIBILITY_TIMEOUT = 60
|
24
|
-
RECEIVE_WAIT_TIME = 15
|
25
|
-
|
26
|
-
attr_reader :queue, :executor, :opts, :processor
|
27
|
-
|
28
|
-
def initialize(opts, processor = nil)
|
29
|
-
@opts = opts
|
30
|
-
@processor = processor
|
31
|
-
|
32
|
-
@queue = Alephant::Publisher::Queue::SQSHelper::Queue.new(
|
33
|
-
aws_queue,
|
34
|
-
archiver,
|
35
|
-
opts.queue[:visibility_timeout] || VISIBILITY_TIMEOUT,
|
36
|
-
opts.queue[:receive_wait_time] || RECEIVE_WAIT_TIME
|
37
|
-
)
|
38
|
-
end
|
39
|
-
|
40
|
-
def run!
|
41
|
-
loop { processor.consume(@queue.message) }
|
42
|
-
end
|
43
|
-
|
44
|
-
private
|
45
|
-
|
46
|
-
def archiver
|
47
|
-
Alephant::Publisher::Queue::SQSHelper::Archiver.new(archive_cache, archiver_opts)
|
48
|
-
end
|
49
|
-
|
50
|
-
def archiver_opts
|
51
|
-
options = {
|
52
|
-
:async_store => true,
|
53
|
-
:log_archive_message => true,
|
54
|
-
:log_validator => opts.queue[:log_validator]
|
55
|
-
}
|
56
|
-
options.each do |key, _value|
|
57
|
-
options[key] = opts.queue[key] == "true" if whitelist_key(opts.queue, key)
|
58
|
-
end
|
59
|
-
end
|
60
|
-
|
61
|
-
def whitelist_key(options, key)
|
62
|
-
options.key?(key) && key != :log_validator
|
63
|
-
end
|
64
|
-
|
65
|
-
def archive_cache
|
66
|
-
Alephant::Cache.new(
|
67
|
-
opts.writer[:s3_bucket_id],
|
68
|
-
opts.writer[:s3_object_path]
|
69
|
-
)
|
70
|
-
end
|
71
|
-
|
72
|
-
def get_region
|
73
|
-
opts.queue[:sqs_account_region] || AWS.config.region
|
74
|
-
end
|
75
|
-
|
76
|
-
def sqs_client
|
77
|
-
@sqs_client ||= AWS::SQS.new(:region => get_region)
|
78
|
-
end
|
79
|
-
|
80
|
-
def sqs_queue_options
|
81
|
-
(opts.queue[:aws_account_id].nil? ? {} : fallback).tap do |ops|
|
82
|
-
logger.info(
|
83
|
-
"event" => "SQSQueueOptionsConfigured",
|
84
|
-
"options" => ops,
|
85
|
-
"method" => "#{self.class}#sqs_queue_options"
|
86
|
-
)
|
87
|
-
end
|
88
|
-
end
|
89
|
-
|
90
|
-
def fallback
|
91
|
-
{
|
92
|
-
:queue_owner_aws_account_id => opts.queue[:aws_account_id]
|
93
|
-
}
|
94
|
-
end
|
95
|
-
|
96
|
-
def aws_queue
|
97
|
-
queue_url = sqs_client.queues.url_for(
|
98
|
-
opts.queue[:sqs_queue_name], sqs_queue_options
|
99
|
-
)
|
100
|
-
sqs_client.queues[queue_url]
|
101
|
-
end
|
102
|
-
end
|
103
21
|
end
|
104
22
|
end
|
105
23
|
end
|
@@ -9,7 +9,7 @@ module Alephant
|
|
9
9
|
class Options
|
10
10
|
include Alephant::Logger
|
11
11
|
|
12
|
-
attr_reader :queue, :writer
|
12
|
+
attr_reader :queue, :writer, :cache
|
13
13
|
|
14
14
|
QUEUE_OPTS = [
|
15
15
|
:receive_wait_time,
|
@@ -20,7 +20,7 @@ module Alephant
|
|
20
20
|
:log_archive_message,
|
21
21
|
:log_validator,
|
22
22
|
:async_store
|
23
|
-
]
|
23
|
+
].freeze
|
24
24
|
|
25
25
|
WRITER_OPTS = [
|
26
26
|
:lookup_table_name,
|
@@ -31,38 +31,47 @@ module Alephant
|
|
31
31
|
:sequence_id_path,
|
32
32
|
:sequencer_table_name,
|
33
33
|
:view_path
|
34
|
-
]
|
34
|
+
].freeze
|
35
|
+
|
36
|
+
CACHE_OPTS = [
|
37
|
+
:elasticache_config_endpoint,
|
38
|
+
:elasticache_cache_version,
|
39
|
+
:revalidate_cache_ttl
|
40
|
+
].freeze
|
35
41
|
|
36
42
|
def initialize
|
37
43
|
@queue = {}
|
38
44
|
@writer = {}
|
45
|
+
@cache = {}
|
39
46
|
end
|
40
47
|
|
41
48
|
def add_queue(opts)
|
42
|
-
execute
|
49
|
+
execute(@queue, QUEUE_OPTS, opts)
|
43
50
|
end
|
44
51
|
|
45
52
|
def add_writer(opts)
|
46
|
-
execute
|
53
|
+
execute(@writer, WRITER_OPTS, opts)
|
54
|
+
end
|
55
|
+
|
56
|
+
def add_cache(opts)
|
57
|
+
execute(@cache, CACHE_OPTS, opts)
|
47
58
|
end
|
48
59
|
|
49
60
|
private
|
50
61
|
|
51
62
|
def execute(instance, type, opts)
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
puts e.message
|
65
|
-
end
|
63
|
+
validate(type, opts)
|
64
|
+
instance.merge!(opts)
|
65
|
+
rescue InvalidKeySpecifiedError => e
|
66
|
+
logger.metric "QueueOptionsInvalidKeySpecified"
|
67
|
+
logger.error(
|
68
|
+
"event" => "QueueOptionsKeyInvalid",
|
69
|
+
"class" => e.class,
|
70
|
+
"message" => e.message,
|
71
|
+
"backtrace" => e.backtrace.join.to_s,
|
72
|
+
"method" => "#{self.class}#validate"
|
73
|
+
)
|
74
|
+
puts e.message
|
66
75
|
end
|
67
76
|
|
68
77
|
def validate(type, opts)
|
@@ -1,25 +1,27 @@
|
|
1
1
|
require "alephant/publisher/queue/writer"
|
2
|
-
require "alephant/publisher/queue/base_processor"
|
3
2
|
|
4
3
|
module Alephant
|
5
4
|
module Publisher
|
6
5
|
module Queue
|
7
|
-
class Processor
|
8
|
-
attr_reader :
|
6
|
+
class Processor
|
7
|
+
attr_reader :opts
|
9
8
|
|
10
|
-
def initialize(
|
11
|
-
@
|
9
|
+
def initialize(opts = nil)
|
10
|
+
@opts = opts
|
12
11
|
end
|
13
12
|
|
14
13
|
def consume(msg)
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
end
|
14
|
+
return if msg.nil?
|
15
|
+
write(msg)
|
16
|
+
msg.delete
|
19
17
|
end
|
20
18
|
|
21
19
|
private
|
22
20
|
|
21
|
+
def writer_config
|
22
|
+
opts ? opts.writer : {}
|
23
|
+
end
|
24
|
+
|
23
25
|
def write(msg)
|
24
26
|
Writer.new(writer_config, msg).run!
|
25
27
|
end
|
@@ -0,0 +1,89 @@
|
|
1
|
+
module Alephant
|
2
|
+
module Publisher
|
3
|
+
module Queue
|
4
|
+
class Publisher
|
5
|
+
include Alephant::Logger
|
6
|
+
|
7
|
+
VISIBILITY_TIMEOUT = 60
|
8
|
+
RECEIVE_WAIT_TIME = 15
|
9
|
+
|
10
|
+
attr_reader :queue, :executor, :opts, :processor
|
11
|
+
|
12
|
+
def initialize(opts, processor = nil)
|
13
|
+
@opts = opts
|
14
|
+
@processor = processor
|
15
|
+
|
16
|
+
@queue = Alephant::Publisher::Queue::SQSHelper::Queue.new(
|
17
|
+
aws_queue,
|
18
|
+
archiver,
|
19
|
+
opts.queue[:visibility_timeout] || VISIBILITY_TIMEOUT,
|
20
|
+
opts.queue[:receive_wait_time] || RECEIVE_WAIT_TIME
|
21
|
+
)
|
22
|
+
end
|
23
|
+
|
24
|
+
def run!
|
25
|
+
loop { processor.consume(@queue.message) }
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
def archiver
|
31
|
+
Alephant::Publisher::Queue::SQSHelper::Archiver.new(archive_cache, archiver_opts)
|
32
|
+
end
|
33
|
+
|
34
|
+
def archiver_opts
|
35
|
+
options = {
|
36
|
+
:async_store => true,
|
37
|
+
:log_archive_message => true,
|
38
|
+
:log_validator => opts.queue[:log_validator]
|
39
|
+
}
|
40
|
+
options.each do |key, _value|
|
41
|
+
options[key] = opts.queue[key] == "true" if whitelist_key(opts.queue, key)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def whitelist_key(options, key)
|
46
|
+
options.key?(key) && key != :log_validator
|
47
|
+
end
|
48
|
+
|
49
|
+
def archive_cache
|
50
|
+
Alephant::Cache.new(
|
51
|
+
opts.writer[:s3_bucket_id],
|
52
|
+
opts.writer[:s3_object_path]
|
53
|
+
)
|
54
|
+
end
|
55
|
+
|
56
|
+
def get_region
|
57
|
+
opts.queue[:sqs_account_region] || AWS.config.region
|
58
|
+
end
|
59
|
+
|
60
|
+
def sqs_client
|
61
|
+
@sqs_client ||= AWS::SQS.new(region: get_region)
|
62
|
+
end
|
63
|
+
|
64
|
+
def sqs_queue_options
|
65
|
+
(opts.queue[:aws_account_id].nil? ? {} : fallback).tap do |ops|
|
66
|
+
logger.info(
|
67
|
+
"event" => "SQSQueueOptionsConfigured",
|
68
|
+
"options" => ops,
|
69
|
+
"method" => "#{self.class}#sqs_queue_options"
|
70
|
+
)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def fallback
|
75
|
+
{
|
76
|
+
:queue_owner_aws_account_id => opts.queue[:aws_account_id]
|
77
|
+
}
|
78
|
+
end
|
79
|
+
|
80
|
+
def aws_queue
|
81
|
+
queue_url = sqs_client.queues.url_for(
|
82
|
+
opts.queue[:sqs_queue_name], sqs_queue_options
|
83
|
+
)
|
84
|
+
sqs_client.queues[queue_url]
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|