govuk_message_queue_consumer 3.1.0 → 3.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 +5 -5
- data/CHANGELOG.md +5 -0
- data/lib/govuk_message_queue_consumer.rb +2 -0
- data/lib/govuk_message_queue_consumer/batch_consumer.rb +69 -0
- data/lib/govuk_message_queue_consumer/consumer.rb +15 -4
- data/lib/govuk_message_queue_consumer/heartbeat_processor.rb +3 -6
- data/lib/govuk_message_queue_consumer/json_processor.rb +2 -5
- data/lib/govuk_message_queue_consumer/message_consumer.rb +27 -0
- data/lib/govuk_message_queue_consumer/version.rb +1 -1
- metadata +33 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 92d5ee51b1ed29b2d86c75475cf767c3cb978d19bfe17cc92cab3cadb9de4bce
|
4
|
+
data.tar.gz: 9f2667dff5e909beccace001dd9f2cef64911253f019ec86009ba210e722a69b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: eedbec227fd052d8427c9b8de008550467b3bc10d57e88f0a2605c1a5053b76488bec2b9d4964aceb5bcda0d67953ba4ec6045ad10bee52224ce4ac7d041efb8
|
7
|
+
data.tar.gz: c7f8c8d4a6b8273a30f8483c88daf3d7f712b13ac933ee9c64b844f72e6713ba763c6fe019e61316562b989a21c390d096a38cb73ed1ebf22267fe6daa6ed659
|
data/CHANGELOG.md
CHANGED
@@ -5,5 +5,7 @@ require 'govuk_message_queue_consumer/version'
|
|
5
5
|
require 'govuk_message_queue_consumer/heartbeat_processor'
|
6
6
|
require 'govuk_message_queue_consumer/json_processor'
|
7
7
|
require 'govuk_message_queue_consumer/message'
|
8
|
+
require 'govuk_message_queue_consumer/message_consumer'
|
8
9
|
require 'govuk_message_queue_consumer/consumer'
|
10
|
+
require 'govuk_message_queue_consumer/batch_consumer'
|
9
11
|
require 'govuk_message_queue_consumer/rabbitmq_config'
|
@@ -0,0 +1,69 @@
|
|
1
|
+
module GovukMessageQueueConsumer
|
2
|
+
class BatchConsumer < Consumer
|
3
|
+
HANDLE_BATCHES = true
|
4
|
+
DEFAULT_BATCH_SIZE = 100
|
5
|
+
DEFAULT_BATCH_TIMEOUT = 5
|
6
|
+
# we want to increase the prefetch size here to the batch size
|
7
|
+
# so that we don't need to do multiple fetches for a batch.
|
8
|
+
NUMBER_OF_MESSAGES_TO_PREFETCH = DEFAULT_BATCH_SIZE
|
9
|
+
|
10
|
+
def initialize(options={})
|
11
|
+
opts = options.dup
|
12
|
+
@batch_size = opts.delete(:batch_size) || DEFAULT_BATCH_SIZE
|
13
|
+
@batch_timeout = opts.delete(:batch_timeout) || DEFAULT_BATCH_TIMEOUT
|
14
|
+
super(opts)
|
15
|
+
end
|
16
|
+
|
17
|
+
def run
|
18
|
+
@rabbitmq_connection.start
|
19
|
+
@running = true
|
20
|
+
while @running do
|
21
|
+
process_batch
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
# used for testing to stop processing in a different thread
|
26
|
+
def stop
|
27
|
+
@running = false
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
def process_batch
|
33
|
+
messages = []
|
34
|
+
with_timeout do
|
35
|
+
while messages.count < @batch_size do
|
36
|
+
delivery_info, headers, payload = queue.pop(manual_ack: true)
|
37
|
+
messages << Message.new(payload, headers, delivery_info) if payload
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
if messages.any?
|
42
|
+
@statsd_client.count("#{@queue_name}.started", messages.count)
|
43
|
+
@statsd_client.increment("#{@queue_name}.batch_started")
|
44
|
+
message_consumer.process(messages)
|
45
|
+
|
46
|
+
status_counts = messages.map(&:status).each_with_object(Hash.new(0)) { |s, h| h[s] += 1 }
|
47
|
+
status_counts.each do |status, count|
|
48
|
+
@statsd_client.count("#{@queue_name}.#{status}", count)
|
49
|
+
end
|
50
|
+
@statsd_client.increment("#{@queue_name}.batch_complete")
|
51
|
+
end
|
52
|
+
rescue Exception => e
|
53
|
+
@statsd_client.increment("#{@queue_name}.uncaught_exception")
|
54
|
+
GovukError.notify(e) if defined?(GovukError)
|
55
|
+
@logger.error "Uncaught exception in processor: \n\n #{e.class}: #{e.message}\n\n#{e.backtrace.join("\n")}"
|
56
|
+
|
57
|
+
exit(1) # Ensure rabbitmq requeues outstanding messages
|
58
|
+
end
|
59
|
+
|
60
|
+
def with_timeout
|
61
|
+
begin
|
62
|
+
Timeout.timeout(@batch_timeout) do
|
63
|
+
yield
|
64
|
+
end
|
65
|
+
rescue Timeout::Error
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
@@ -1,5 +1,6 @@
|
|
1
1
|
module GovukMessageQueueConsumer
|
2
2
|
class Consumer
|
3
|
+
HANDLE_BATCHES = false
|
3
4
|
# Only fetch one message at a time on the channel.
|
4
5
|
#
|
5
6
|
# By default, queues will grab messages eagerly, which reduces latency.
|
@@ -32,7 +33,7 @@ module GovukMessageQueueConsumer
|
|
32
33
|
begin
|
33
34
|
message = Message.new(payload, headers, delivery_info)
|
34
35
|
@statsd_client.increment("#{@queue_name}.started")
|
35
|
-
|
36
|
+
message_consumer.process(message)
|
36
37
|
@statsd_client.increment("#{@queue_name}.#{message.status}")
|
37
38
|
rescue Exception => e
|
38
39
|
@statsd_client.increment("#{@queue_name}.uncaught_exception")
|
@@ -48,15 +49,25 @@ module GovukMessageQueueConsumer
|
|
48
49
|
class NullStatsd
|
49
50
|
def increment(_key)
|
50
51
|
end
|
52
|
+
|
53
|
+
def count(_key, _volume)
|
54
|
+
end
|
51
55
|
end
|
52
56
|
|
53
|
-
def
|
54
|
-
@
|
57
|
+
def message_consumer
|
58
|
+
@message_consumer ||= MessageConsumer.new(
|
59
|
+
processors: [
|
60
|
+
HeartbeatProcessor.new,
|
61
|
+
JSONProcessor.new,
|
62
|
+
@processor,
|
63
|
+
],
|
64
|
+
handle_batches: self.class::HANDLE_BATCHES,
|
65
|
+
)
|
55
66
|
end
|
56
67
|
|
57
68
|
def queue
|
58
69
|
@queue ||= begin
|
59
|
-
channel.prefetch(NUMBER_OF_MESSAGES_TO_PREFETCH)
|
70
|
+
channel.prefetch(self.class::NUMBER_OF_MESSAGES_TO_PREFETCH)
|
60
71
|
channel.queue(@queue_name, no_declare: true)
|
61
72
|
end
|
62
73
|
end
|
@@ -1,16 +1,13 @@
|
|
1
1
|
module GovukMessageQueueConsumer
|
2
2
|
class HeartbeatProcessor
|
3
|
-
def initialize(next_processor)
|
4
|
-
@next_processor = next_processor
|
5
|
-
end
|
6
|
-
|
7
3
|
def process(message)
|
8
4
|
# Ignore heartbeat messages
|
9
5
|
if message.headers.content_type == "application/x-heartbeat"
|
10
6
|
message.ack
|
11
|
-
|
12
|
-
@next_processor.process(message)
|
7
|
+
return false
|
13
8
|
end
|
9
|
+
|
10
|
+
true
|
14
11
|
end
|
15
12
|
end
|
16
13
|
end
|
@@ -2,19 +2,16 @@ module GovukMessageQueueConsumer
|
|
2
2
|
class JSONProcessor
|
3
3
|
JSON_FORMAT = "application/json".freeze
|
4
4
|
|
5
|
-
def initialize(next_processor)
|
6
|
-
@next_processor = next_processor
|
7
|
-
end
|
8
|
-
|
9
5
|
def process(message)
|
10
6
|
if message.headers.content_type == JSON_FORMAT
|
11
7
|
message.payload = JSON.parse(message.payload)
|
12
8
|
end
|
13
9
|
|
14
|
-
|
10
|
+
true
|
15
11
|
rescue JSON::ParserError => e
|
16
12
|
GovukError.notify(e) if defined?(GovukError)
|
17
13
|
message.discard
|
14
|
+
false
|
18
15
|
end
|
19
16
|
end
|
20
17
|
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module GovukMessageQueueConsumer
|
2
|
+
class MessageConsumer
|
3
|
+
def initialize(processors:, handle_batches:)
|
4
|
+
@processors = processors
|
5
|
+
@handle_batches = handle_batches
|
6
|
+
end
|
7
|
+
|
8
|
+
def process(records)
|
9
|
+
@processors.inject(Array(records)) do |remaining_records, processor|
|
10
|
+
if handles_batches?(processor)
|
11
|
+
processor.process(remaining_records)
|
12
|
+
else
|
13
|
+
remaining_records.select { |record| processor.process(record) }
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def handles_batches?(processor)
|
19
|
+
case processor
|
20
|
+
when HeartbeatProcessor,JSONProcessor
|
21
|
+
false
|
22
|
+
else
|
23
|
+
@handle_batches
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: govuk_message_queue_consumer
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 3.
|
4
|
+
version: 3.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- GOV.UK Dev
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2018-01-15 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bunny
|
@@ -66,6 +66,34 @@ dependencies:
|
|
66
66
|
- - ">="
|
67
67
|
- !ruby/object:Gem::Version
|
68
68
|
version: '0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: bunny-mock
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :development
|
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: pry-byebug
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ">="
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
69
97
|
description: Avoid writing boilerplate code in order to consume messages from an AMQP
|
70
98
|
message queue. Plug in queue configuration, and how to process each message.
|
71
99
|
email:
|
@@ -78,10 +106,12 @@ files:
|
|
78
106
|
- LICENCE
|
79
107
|
- README.md
|
80
108
|
- lib/govuk_message_queue_consumer.rb
|
109
|
+
- lib/govuk_message_queue_consumer/batch_consumer.rb
|
81
110
|
- lib/govuk_message_queue_consumer/consumer.rb
|
82
111
|
- lib/govuk_message_queue_consumer/heartbeat_processor.rb
|
83
112
|
- lib/govuk_message_queue_consumer/json_processor.rb
|
84
113
|
- lib/govuk_message_queue_consumer/message.rb
|
114
|
+
- lib/govuk_message_queue_consumer/message_consumer.rb
|
85
115
|
- lib/govuk_message_queue_consumer/rabbitmq_config.rb
|
86
116
|
- lib/govuk_message_queue_consumer/test_helpers.rb
|
87
117
|
- lib/govuk_message_queue_consumer/test_helpers/mock_message.rb
|
@@ -106,7 +136,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
106
136
|
version: '0'
|
107
137
|
requirements: []
|
108
138
|
rubyforge_project:
|
109
|
-
rubygems_version: 2.
|
139
|
+
rubygems_version: 2.7.3
|
110
140
|
signing_key:
|
111
141
|
specification_version: 4
|
112
142
|
summary: AMQP message queue consumption with GOV.UK conventions
|