nulogy_message_bus_consumer 1.0.0 → 2.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Rakefile +0 -6
- data/lib/nulogy_message_bus_consumer/config.rb +2 -0
- data/lib/nulogy_message_bus_consumer/kafka_utils.rb +5 -4
- data/lib/nulogy_message_bus_consumer/message.rb +3 -3
- data/lib/nulogy_message_bus_consumer/steps/commit_on_success.rb +15 -0
- data/lib/nulogy_message_bus_consumer/steps/stream_messages_until_none_are_left.rb +9 -3
- data/lib/nulogy_message_bus_consumer/tasks/log_consumer_lag.rb +8 -2
- data/lib/nulogy_message_bus_consumer/version.rb +1 -1
- data/lib/nulogy_message_bus_consumer.rb +2 -2
- data/spec/dummy/db/schema.rb +1 -1
- data/spec/dummy/log/test.log +0 -7949
- data/spec/integration/nulogy_message_bus_consumer/end_to_end_spec.rb +54 -0
- data/spec/integration/nulogy_message_bus_consumer/kafka_utils_spec.rb +1 -1
- data/spec/integration/nulogy_message_bus_consumer/steps/commit_on_success_spec.rb +47 -39
- data/spec/integration/nulogy_message_bus_consumer/steps/connect_to_message_bus_spec.rb +12 -26
- data/spec/integration/test_topic_spec.rb +4 -0
- data/spec/support/kafka.rb +20 -12
- data/spec/support/test_topic.rb +1 -1
- data/spec/unit/nulogy_message_bus_consumer/steps/commit_on_success_spec.rb +7 -1
- metadata +7 -26
- data/config/credentials/message-bus-us-east-1.key +0 -1
- data/spec/dummy/config/credentials/message-bus-us-east-1.key +0 -1
- data/spec/dummy/log/development.log +0 -4
- data/spec/dummy/log/production.log +0 -18
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b061b1d275f3492d62c8ecfd64caaae755f9d9b60a0a4f7466ad72833788ea6b
|
4
|
+
data.tar.gz: df51d614f618b900c5cd432668d68bf1843ba74b4bf54a8ad39544812dd56ede
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: dd39cb403d86779b3466f7b95ca1403adc6ed464c36c6270e6a37b5dca92117c0507ca3b9d1e4fb6632f737db57ebb2178cd484e65c163083e2ffab321d0775c
|
7
|
+
data.tar.gz: 8548a30f820e217a2bcfd1467d5967767649f096298c5fe6d5b407f6bc0da4aefd4a9170da9b9109b3621c7facc6bd3b9dcad03d5dcea337c3dd9443c4e61718
|
data/Rakefile
CHANGED
@@ -23,9 +23,3 @@ require "rspec/core/rake_task"
|
|
23
23
|
RSpec::Core::RakeTask.new(:spec)
|
24
24
|
require "standard/rake"
|
25
25
|
task default: %i[spec standard]
|
26
|
-
|
27
|
-
require "rake/release"
|
28
|
-
|
29
|
-
Rake::Release::Task.load_all do |spec|
|
30
|
-
spec.version_tag = "nulogy_message_bus_consumer-v#{spec.version}"
|
31
|
-
end
|
@@ -5,6 +5,7 @@ module NulogyMessageBusConsumer
|
|
5
5
|
:consumer_group_id,
|
6
6
|
:lag_check_interval_seconds,
|
7
7
|
:lag_checks,
|
8
|
+
:lag_timeout_milliseconds,
|
8
9
|
:log_lag_interval_seconds,
|
9
10
|
:prune_interval_seconds,
|
10
11
|
:prune_max_age,
|
@@ -14,6 +15,7 @@ module NulogyMessageBusConsumer
|
|
14
15
|
defaults = {
|
15
16
|
lag_check_interval_seconds: 20,
|
16
17
|
lag_checks: 6,
|
18
|
+
lag_timeout_milliseconds: 200,
|
17
19
|
log_lag_interval_seconds: 1.minute.to_i,
|
18
20
|
prune_interval_seconds: 1.hour.to_i,
|
19
21
|
prune_max_age: 8.days
|
@@ -10,17 +10,18 @@ module NulogyMessageBusConsumer
|
|
10
10
|
wait_for { consumer.assignment.empty? }
|
11
11
|
end
|
12
12
|
|
13
|
-
def wait_for(attempts:
|
13
|
+
def wait_for(attempts: 1000, interval: 0.1)
|
14
14
|
attempts.times do
|
15
|
-
|
15
|
+
return if yield
|
16
16
|
|
17
17
|
sleep interval
|
18
18
|
end
|
19
|
+
raise "wait_for timed out!"
|
19
20
|
end
|
20
21
|
|
21
|
-
def every_message_until_none_are_left(consumer)
|
22
|
+
def every_message_until_none_are_left(consumer, timeout = 250)
|
22
23
|
Enumerator.new do |yielder|
|
23
|
-
while (message = consumer.poll(
|
24
|
+
while (message = consumer.poll(timeout))
|
24
25
|
yielder.yield(message)
|
25
26
|
end
|
26
27
|
end
|
@@ -41,9 +41,9 @@ module NulogyMessageBusConsumer
|
|
41
41
|
end
|
42
42
|
|
43
43
|
def to_h
|
44
|
-
|
45
|
-
attribute_name.
|
46
|
-
hash[attribute_name] = public_send(attribute_name)
|
44
|
+
instance_variables.each_with_object({}) do |instance_variable, hash|
|
45
|
+
attribute_name = instance_variable.to_s.delete("@")
|
46
|
+
hash[attribute_name.to_sym] = public_send(attribute_name)
|
47
47
|
end
|
48
48
|
end
|
49
49
|
end
|
@@ -1,6 +1,10 @@
|
|
1
1
|
module NulogyMessageBusConsumer
|
2
2
|
module Steps
|
3
3
|
class CommitOnSuccess
|
4
|
+
def initialize(logger)
|
5
|
+
@logger = logger
|
6
|
+
end
|
7
|
+
|
4
8
|
def call(kafka_consumer:, message:, **_)
|
5
9
|
result = yield
|
6
10
|
|
@@ -9,8 +13,18 @@ module NulogyMessageBusConsumer
|
|
9
13
|
if result == :success
|
10
14
|
kafka_consumer.store_offset(message)
|
11
15
|
kafka_consumer.commit
|
16
|
+
@logger.info(JSON.dump({
|
17
|
+
event: "message_committed",
|
18
|
+
kafka_message_id: message.id,
|
19
|
+
message: message.to_h
|
20
|
+
}))
|
12
21
|
else
|
13
22
|
reconnect_to_reprocess_same_message(kafka_consumer)
|
23
|
+
@logger.info(JSON.dump({
|
24
|
+
event: "message_failed",
|
25
|
+
kafka_message_id: message.id,
|
26
|
+
message: message.to_h
|
27
|
+
}))
|
14
28
|
end
|
15
29
|
|
16
30
|
result
|
@@ -22,6 +36,7 @@ module NulogyMessageBusConsumer
|
|
22
36
|
subscriptions = kafka_consumer.subscription
|
23
37
|
kafka_consumer.unsubscribe
|
24
38
|
kafka_consumer.subscribe(*subscriptions.to_h.keys)
|
39
|
+
KafkaUtils.wait_for_assignment(kafka_consumer)
|
25
40
|
end
|
26
41
|
|
27
42
|
def raise_if_invalid(result)
|
@@ -1,17 +1,23 @@
|
|
1
1
|
module NulogyMessageBusConsumer
|
2
2
|
module Steps
|
3
3
|
class StreamMessagesUntilNoneAreLeft
|
4
|
-
def initialize(logger)
|
4
|
+
def initialize(logger, timeout = 250)
|
5
5
|
@logger = logger
|
6
|
+
@timeout = timeout
|
6
7
|
end
|
7
8
|
|
8
9
|
def call(kafka_consumer:, **_)
|
9
|
-
KafkaUtils.every_message_until_none_are_left(kafka_consumer).each do |kafka_message|
|
10
|
-
yield(
|
10
|
+
KafkaUtils.every_message_until_none_are_left(kafka_consumer, @timeout).each do |kafka_message|
|
11
|
+
result = yield(
|
11
12
|
message: Message.from_kafka(kafka_message),
|
12
13
|
kafka_message: kafka_message
|
13
14
|
)
|
15
|
+
if result == :failure
|
16
|
+
# stop reading on failure or else we'll get stuck in a loop
|
17
|
+
return :failure
|
18
|
+
end
|
14
19
|
end
|
20
|
+
:success
|
15
21
|
rescue => e
|
16
22
|
@logger.error(JSON.dump({
|
17
23
|
event: "message_processing_errored",
|
@@ -3,9 +3,10 @@ module NulogyMessageBusConsumer
|
|
3
3
|
class LogConsumerLag
|
4
4
|
attr_reader :interval
|
5
5
|
|
6
|
-
def initialize(logger, interval_seconds)
|
6
|
+
def initialize(logger, interval_seconds, lag_timeout)
|
7
7
|
@logger = logger
|
8
8
|
@interval = interval_seconds
|
9
|
+
@lag_timeout = lag_timeout
|
9
10
|
end
|
10
11
|
|
11
12
|
def extract_args(kafka_consumer:, **_)
|
@@ -18,7 +19,12 @@ module NulogyMessageBusConsumer
|
|
18
19
|
# has finished connecting. There appears to be a race condition.
|
19
20
|
KafkaUtils.wait_for_assignment(@kafka_consumer)
|
20
21
|
|
21
|
-
|
22
|
+
# Note: consumer#committed has a timeout of 1200ms. To respect our lag_timeout, use the largest.
|
23
|
+
committed_timeout = [1200, @lag_timeout].max
|
24
|
+
# The first parameter is a TopicPartitionList. When nil, it uses all the assigned ones.
|
25
|
+
committed_offsets = @kafka_consumer.committed(nil, committed_timeout)
|
26
|
+
|
27
|
+
lag_per_topic = @kafka_consumer.lag(committed_offsets, @lag_timeout)
|
22
28
|
|
23
29
|
@logger.info(JSON.dump({
|
24
30
|
event: "consumer_lag",
|
@@ -52,7 +52,7 @@ module NulogyMessageBusConsumer
|
|
52
52
|
# be called once, without any messages.
|
53
53
|
Steps::ConnectToMessageBus.new(config, logger),
|
54
54
|
Steps::TimedTask.new(
|
55
|
-
Tasks::LogConsumerLag.new(logger, config.log_lag_interval_seconds)
|
55
|
+
Tasks::LogConsumerLag.new(logger, config.log_lag_interval_seconds, config.lag_timeout_milliseconds)
|
56
56
|
),
|
57
57
|
Steps::TimedTask.new(
|
58
58
|
Tasks::PruneProcessedMessages.new(logger, config.prune_interval_seconds, config.prune_max_age)
|
@@ -67,7 +67,7 @@ module NulogyMessageBusConsumer
|
|
67
67
|
Steps::StreamMessages.new(logger),
|
68
68
|
# Message processing steps start here.
|
69
69
|
Steps::LogMessages.new(logger),
|
70
|
-
Steps::CommitOnSuccess.new,
|
70
|
+
Steps::CommitOnSuccess.new(logger),
|
71
71
|
Steps::DeduplicateMessages.new(logger)
|
72
72
|
])
|
73
73
|
end
|
data/spec/dummy/db/schema.rb
CHANGED
@@ -10,7 +10,7 @@
|
|
10
10
|
#
|
11
11
|
# It's strongly recommended that you check this file into your version control system.
|
12
12
|
|
13
|
-
ActiveRecord::Schema.define(version:
|
13
|
+
ActiveRecord::Schema.define(version: 2020_06_12_184425) do
|
14
14
|
# These are extensions that must be enabled in order to support this database
|
15
15
|
enable_extension "plpgsql"
|
16
16
|
enable_extension "uuid-ossp"
|