nulogy_message_bus_consumer 0.3.2 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Rakefile +2 -3
- data/config/credentials/message-bus-us-east-1.key +1 -0
- data/config/credentials/message-bus-us-east-1.yml.enc +1 -0
- data/lib/nulogy_message_bus_consumer.rb +27 -6
- data/lib/nulogy_message_bus_consumer/clock.rb +13 -0
- data/lib/nulogy_message_bus_consumer/config.rb +18 -4
- data/lib/nulogy_message_bus_consumer/deployment/ecs.rb +23 -0
- data/lib/nulogy_message_bus_consumer/lag_tracker.rb +53 -0
- data/lib/nulogy_message_bus_consumer/message.rb +1 -1
- data/lib/nulogy_message_bus_consumer/null_logger.rb +6 -3
- data/lib/nulogy_message_bus_consumer/pipeline.rb +1 -1
- data/lib/nulogy_message_bus_consumer/steps/commit_on_success.rb +2 -1
- data/lib/nulogy_message_bus_consumer/steps/connect_to_message_bus.rb +23 -9
- data/lib/nulogy_message_bus_consumer/steps/deduplicate_messages.rb +1 -1
- data/lib/nulogy_message_bus_consumer/steps/log_messages.rb +4 -11
- data/lib/nulogy_message_bus_consumer/steps/stream_messages.rb +2 -2
- data/lib/nulogy_message_bus_consumer/steps/stream_messages_until_none_are_left.rb +2 -2
- data/lib/nulogy_message_bus_consumer/steps/timed_task.rb +42 -0
- data/lib/nulogy_message_bus_consumer/tasks/log_consumer_lag.rb +45 -0
- data/lib/nulogy_message_bus_consumer/tasks/prune_processed_messages.rb +37 -0
- data/lib/nulogy_message_bus_consumer/tasks/supervise_consumer_lag.rb +65 -0
- data/lib/nulogy_message_bus_consumer/version.rb +1 -1
- data/lib/tasks/engine/message_bus_consumer.rake +7 -8
- data/spec/dummy/Rakefile +6 -0
- data/spec/dummy/app/assets/config/manifest.js +3 -0
- data/spec/dummy/app/assets/stylesheets/application.css +15 -0
- data/spec/dummy/app/channels/application_cable/channel.rb +4 -0
- data/spec/dummy/app/channels/application_cable/connection.rb +4 -0
- data/spec/dummy/app/controllers/application_controller.rb +2 -0
- data/spec/dummy/app/helpers/application_helper.rb +2 -0
- data/spec/dummy/app/javascript/packs/application.js +15 -0
- data/spec/dummy/app/jobs/application_job.rb +7 -0
- data/spec/dummy/app/mailers/application_mailer.rb +4 -0
- data/spec/dummy/app/models/application_record.rb +3 -0
- data/spec/dummy/app/views/layouts/application.html.erb +14 -0
- data/spec/dummy/app/views/layouts/mailer.html.erb +13 -0
- data/spec/dummy/app/views/layouts/mailer.text.erb +1 -0
- data/spec/dummy/bin/rails +4 -0
- data/spec/dummy/bin/rake +4 -0
- data/spec/dummy/bin/setup +33 -0
- data/spec/dummy/config.ru +5 -0
- data/spec/dummy/config/application.rb +29 -0
- data/spec/dummy/config/boot.rb +5 -0
- data/spec/dummy/config/cable.yml +10 -0
- data/spec/dummy/config/credentials/message-bus-us-east-1.key +1 -0
- data/spec/dummy/config/credentials/message-bus-us-east-1.yml.enc +1 -0
- data/spec/dummy/config/database.yml +27 -0
- data/spec/dummy/config/environment.rb +5 -0
- data/spec/dummy/config/environments/development.rb +62 -0
- data/spec/dummy/config/environments/production.rb +112 -0
- data/spec/dummy/config/environments/test.rb +49 -0
- data/spec/dummy/config/initializers/application_controller_renderer.rb +8 -0
- data/spec/dummy/config/initializers/assets.rb +12 -0
- data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
- data/spec/dummy/config/initializers/content_security_policy.rb +28 -0
- data/spec/dummy/config/initializers/cookies_serializer.rb +5 -0
- data/spec/dummy/config/initializers/filter_parameter_logging.rb +4 -0
- data/spec/dummy/config/initializers/inflections.rb +16 -0
- data/spec/dummy/config/initializers/message_bus_consumer.rb +5 -0
- data/spec/dummy/config/initializers/mime_types.rb +4 -0
- data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
- data/spec/dummy/config/locales/en.yml +33 -0
- data/spec/dummy/config/puma.rb +36 -0
- data/spec/dummy/config/routes.rb +3 -0
- data/spec/dummy/config/spring.rb +6 -0
- data/spec/dummy/config/storage.yml +34 -0
- data/spec/dummy/db/schema.rb +21 -0
- data/spec/dummy/log/development.log +4 -0
- data/spec/dummy/log/production.log +18 -0
- data/spec/dummy/log/test.log +7949 -0
- data/spec/dummy/public/404.html +67 -0
- data/spec/dummy/public/422.html +67 -0
- data/spec/dummy/public/500.html +66 -0
- data/spec/dummy/public/apple-touch-icon-precomposed.png +0 -0
- data/spec/dummy/public/apple-touch-icon.png +0 -0
- data/spec/dummy/public/favicon.ico +0 -0
- data/spec/dummy/tmp/development_secret.txt +1 -0
- data/spec/integration/nulogy_message_bus_consumer/auditor_spec.rb +59 -0
- data/spec/integration/nulogy_message_bus_consumer/kafka_utils_spec.rb +41 -0
- data/spec/integration/nulogy_message_bus_consumer/steps/commit_on_success_spec.rb +131 -0
- data/spec/integration/nulogy_message_bus_consumer/steps/connect_to_message_bus_spec.rb +53 -0
- data/spec/integration/nulogy_message_bus_consumer/tasks/prune_processed_messages_spec.rb +32 -0
- data/spec/integration/nulogy_message_bus_consumer/tasks/supervise_consumer_lag_spec.rb +33 -0
- data/spec/integration/test_topic_spec.rb +39 -0
- data/spec/spec_helper.rb +50 -0
- data/spec/support/kafka.rb +74 -0
- data/spec/support/middleware_tap.rb +12 -0
- data/spec/support/skip.rb +9 -0
- data/spec/support/test_topic.rb +48 -0
- data/spec/unit/nulogy_message_bus_consumer/config_spec.rb +20 -0
- data/spec/unit/nulogy_message_bus_consumer/lag_tracker.rb +35 -0
- data/spec/unit/nulogy_message_bus_consumer/message_spec.rb +84 -0
- data/spec/unit/nulogy_message_bus_consumer/pipeline_spec.rb +49 -0
- data/spec/unit/nulogy_message_bus_consumer/steps/commit_on_success_spec.rb +58 -0
- data/spec/unit/nulogy_message_bus_consumer/steps/deduplicate_messages_spec.rb +56 -0
- data/spec/unit/nulogy_message_bus_consumer/steps/log_messages_spec.rb +70 -0
- data/spec/unit/nulogy_message_bus_consumer/steps/stream_messages_spec.rb +35 -0
- data/spec/unit/nulogy_message_bus_consumer/tasks/calculator_spec.rb +67 -0
- data/spec/unit/nulogy_message_bus_consumer_spec.rb +30 -0
- metadata +209 -21
- data/lib/nulogy_message_bus_consumer/steps/monitor_replication_lag.rb +0 -51
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: '0989adbcf8a099ac67362106e1724097f7266523a290a36d568d2f8d195a085a'
|
4
|
+
data.tar.gz: da702aedd0055c0fb3ee4269999bc72009bc6d0823f68264770caa11c10c445c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 690db182773fca7bb41d230eb2a5a2c866f61eae733d1afd63b3f7b4693a747b0d1a82d169f7c09b745d70dd5e945e0c745eebb138e7c2405d608ccb736572ba
|
7
|
+
data.tar.gz: 2f5bf0079b7932e15cf9996518d17ec9309882f601cef507a8952ebf22f4c9da01ba5ede9d77674bc491c2dbc6a04b6fb00d095e106075c9d2ad81a001421b49
|
data/Rakefile
CHANGED
@@ -21,9 +21,8 @@ load "rails/tasks/statistics.rake"
|
|
21
21
|
require "rspec/core"
|
22
22
|
require "rspec/core/rake_task"
|
23
23
|
RSpec::Core::RakeTask.new(:spec)
|
24
|
-
require "
|
25
|
-
|
26
|
-
task default: %i[spec rubocop]
|
24
|
+
require "standard/rake"
|
25
|
+
task default: %i[spec standard]
|
27
26
|
|
28
27
|
require "rake/release"
|
29
28
|
|
@@ -0,0 +1 @@
|
|
1
|
+
dfa19863b2709390893da4c2fb85579a
|
@@ -0,0 +1 @@
|
|
1
|
+
/WXdqUYePaHqAq3P0iTEsrLiMfRKzp2qYYh7K+q6LUNgi4eHNv0+SoLdI1bUNb9UaGvDPGNT3fCwAdkurk5Iud16ok3b4wD6yZ7UkfqbXqZaKH/dciQ5s63p9Hiuq1rbfcqoZ3KR1SXYAwvy8vNqbdwbAzz2N66B1wE5fNibZZlrWzXJjLReiTcNyxNbCPz6vwEwFF52RntuYlIJo4Nkm8vEk3No+HWrOkM8xptr5qApbd+RowLCLZ4/kcAMDB/XPiGobf0AOFv1NUR/9ChEy20usa8Fqd6HtEn4A25HnAC0uaN0K8ZRXjxhMpnXtfMBItn7yxyJ1ubjRZK5a1xVBRU7L/CVV9ZuIsqAHL1++gH5FBrEe83ZIUhN7AzngMDlOPKGiCLiZLrm18I1AEQrD7tJLyXos15AeAzj--cER8cN0iMLwu8Le+--FL7dhMTgr6xL6SkMnYKmeg==
|
@@ -1,10 +1,14 @@
|
|
1
|
+
require "active_record/railtie"
|
2
|
+
require "active_support/core_ext/time/zones"
|
1
3
|
require "rdkafka"
|
2
4
|
|
3
5
|
require "nulogy_message_bus_consumer/engine"
|
4
|
-
|
6
|
+
require "nulogy_message_bus_consumer/clock"
|
5
7
|
require "nulogy_message_bus_consumer/config"
|
8
|
+
require "nulogy_message_bus_consumer/deployment/ecs"
|
6
9
|
require "nulogy_message_bus_consumer/handlers/log_unprocessed_messages"
|
7
10
|
require "nulogy_message_bus_consumer/kafka_utils"
|
11
|
+
require "nulogy_message_bus_consumer/lag_tracker"
|
8
12
|
require "nulogy_message_bus_consumer/message"
|
9
13
|
require "nulogy_message_bus_consumer/null_logger"
|
10
14
|
require "nulogy_message_bus_consumer/pipeline"
|
@@ -13,10 +17,13 @@ require "nulogy_message_bus_consumer/steps/commit_on_success"
|
|
13
17
|
require "nulogy_message_bus_consumer/steps/connect_to_message_bus"
|
14
18
|
require "nulogy_message_bus_consumer/steps/deduplicate_messages"
|
15
19
|
require "nulogy_message_bus_consumer/steps/log_messages"
|
16
|
-
require "nulogy_message_bus_consumer/steps/monitor_replication_lag"
|
17
20
|
require "nulogy_message_bus_consumer/steps/seek_beginning_of_topic"
|
18
21
|
require "nulogy_message_bus_consumer/steps/stream_messages"
|
19
22
|
require "nulogy_message_bus_consumer/steps/stream_messages_until_none_are_left"
|
23
|
+
require "nulogy_message_bus_consumer/steps/timed_task"
|
24
|
+
require "nulogy_message_bus_consumer/tasks/log_consumer_lag"
|
25
|
+
require "nulogy_message_bus_consumer/tasks/prune_processed_messages"
|
26
|
+
require "nulogy_message_bus_consumer/tasks/supervise_consumer_lag"
|
20
27
|
|
21
28
|
module NulogyMessageBusConsumer
|
22
29
|
module_function
|
@@ -40,14 +47,28 @@ module NulogyMessageBusConsumer
|
|
40
47
|
|
41
48
|
def recommended_consumer_pipeline(config: self.config, logger: self.logger)
|
42
49
|
Pipeline.new([
|
43
|
-
#
|
50
|
+
# System processing/health steps.
|
51
|
+
# Note: that since they are before `StreamMessages`, they will only
|
52
|
+
# be called once, without any messages.
|
44
53
|
Steps::ConnectToMessageBus.new(config, logger),
|
45
|
-
Steps::
|
54
|
+
Steps::TimedTask.new(
|
55
|
+
Tasks::LogConsumerLag.new(logger, config.log_lag_interval_seconds)
|
56
|
+
),
|
57
|
+
Steps::TimedTask.new(
|
58
|
+
Tasks::PruneProcessedMessages.new(logger, config.prune_interval_seconds, config.prune_max_age)
|
59
|
+
),
|
60
|
+
Steps::TimedTask.new(
|
61
|
+
Tasks::SuperviseConsumerLag.new(
|
62
|
+
logger,
|
63
|
+
check_interval_seconds: config.lag_check_interval_seconds,
|
64
|
+
tracker: LagTracker.new(failing_checks: config.lag_checks)
|
65
|
+
)
|
66
|
+
),
|
46
67
|
Steps::StreamMessages.new(logger),
|
47
68
|
# Message processing steps start here.
|
48
69
|
Steps::LogMessages.new(logger),
|
49
70
|
Steps::CommitOnSuccess.new,
|
50
|
-
Steps::DeduplicateMessages.new(logger)
|
71
|
+
Steps::DeduplicateMessages.new(logger)
|
51
72
|
])
|
52
73
|
end
|
53
74
|
|
@@ -56,7 +77,7 @@ module NulogyMessageBusConsumer
|
|
56
77
|
Steps::ConnectToMessageBus.new(config, logger),
|
57
78
|
Steps::SeekBeginningOfTopic.new,
|
58
79
|
Steps::StreamMessagesUntilNoneAreLeft.new(logger),
|
59
|
-
Handlers::LogUnprocessedMessages.new(logger)
|
80
|
+
Handlers::LogUnprocessedMessages.new(logger)
|
60
81
|
])
|
61
82
|
end
|
62
83
|
end
|
@@ -1,11 +1,25 @@
|
|
1
1
|
module NulogyMessageBusConsumer
|
2
2
|
class Config
|
3
|
-
attr_accessor :
|
4
|
-
|
5
|
-
|
3
|
+
attr_accessor :bootstrap_servers,
|
4
|
+
:client_id,
|
5
|
+
:consumer_group_id,
|
6
|
+
:lag_check_interval_seconds,
|
7
|
+
:lag_checks,
|
8
|
+
:log_lag_interval_seconds,
|
9
|
+
:prune_interval_seconds,
|
10
|
+
:prune_max_age,
|
11
|
+
:topic_name
|
6
12
|
|
7
13
|
def initialize(options = {})
|
8
|
-
|
14
|
+
defaults = {
|
15
|
+
lag_check_interval_seconds: 20,
|
16
|
+
lag_checks: 6,
|
17
|
+
log_lag_interval_seconds: 1.minute.to_i,
|
18
|
+
prune_interval_seconds: 1.hour.to_i,
|
19
|
+
prune_max_age: 8.days
|
20
|
+
}
|
21
|
+
|
22
|
+
update(defaults.merge(options))
|
9
23
|
end
|
10
24
|
|
11
25
|
def update(options = {})
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module NulogyMessageBusConsumer
|
2
|
+
module Deployment
|
3
|
+
module ECS
|
4
|
+
module_function
|
5
|
+
|
6
|
+
# Try to get the TaskID from metadata server:
|
7
|
+
# https://docs.aws.amazon.com/AmazonECS/latest/developerguide/task-metadata-endpoint-v4.html
|
8
|
+
# Otherwise, return nil
|
9
|
+
def task_id
|
10
|
+
data = `curl --silent "$ECS_CONTAINER_METADATA_URI_V4/task"`
|
11
|
+
|
12
|
+
return if data.empty?
|
13
|
+
|
14
|
+
json = JSON.parse(data)
|
15
|
+
arn = json["TaskARN"]
|
16
|
+
|
17
|
+
return unless arn
|
18
|
+
|
19
|
+
arn.split("/").last
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
require "set"
|
2
|
+
|
3
|
+
module NulogyMessageBusConsumer
|
4
|
+
# Keeps track of how many times a topic's partition has not changed (non-zero) lag between update calls.
|
5
|
+
class LagTracker
|
6
|
+
attr_reader :failing_checks
|
7
|
+
|
8
|
+
def initialize(failing_checks: 3)
|
9
|
+
@failing_checks = failing_checks
|
10
|
+
@tracked = Hash.new { |h, topic| h[topic] = {} }
|
11
|
+
@failed = Hash.new { |h, topic| h[topic] = Set.new }
|
12
|
+
end
|
13
|
+
|
14
|
+
def update(topic_partitions)
|
15
|
+
topic_partitions.each_pair do |topic, partitions|
|
16
|
+
partitions.each_pair do |partition, value|
|
17
|
+
update_topic_partition(topic, partition, value)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def failing?
|
23
|
+
@failed.any?
|
24
|
+
end
|
25
|
+
|
26
|
+
def failed
|
27
|
+
@failed.transform_values { |v| v.to_a.sort }
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
def update_topic_partition(topic, partition, value)
|
33
|
+
current_value, count = @tracked.dig(topic, partition)
|
34
|
+
|
35
|
+
new_value, new_count =
|
36
|
+
if current_value == value && !value.zero?
|
37
|
+
[current_value, count + 1]
|
38
|
+
else
|
39
|
+
[value, 0]
|
40
|
+
end
|
41
|
+
|
42
|
+
@tracked[topic][partition] = [new_value, new_count]
|
43
|
+
|
44
|
+
if new_count >= @failing_checks
|
45
|
+
@failed[topic] << partition
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def exists?(topic, partition)
|
50
|
+
@tracked.dig(topic, partition)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -34,7 +34,7 @@ module NulogyMessageBusConsumer
|
|
34
34
|
lambda do |**yielded_args|
|
35
35
|
args_to_be_overridden = existing_args.keys & yielded_args.keys
|
36
36
|
if args_to_be_overridden.any?
|
37
|
-
raise "Cannot override existing argument(s): #{args_to_be_overridden.join(
|
37
|
+
raise "Cannot override existing argument(s): #{args_to_be_overridden.join(", ")}"
|
38
38
|
end
|
39
39
|
|
40
40
|
func.call(**existing_args.merge(yielded_args))
|
@@ -1,12 +1,13 @@
|
|
1
1
|
module NulogyMessageBusConsumer
|
2
2
|
module Steps
|
3
3
|
class CommitOnSuccess
|
4
|
-
def call(kafka_consumer:, **_)
|
4
|
+
def call(kafka_consumer:, message:, **_)
|
5
5
|
result = yield
|
6
6
|
|
7
7
|
raise_if_invalid(result)
|
8
8
|
|
9
9
|
if result == :success
|
10
|
+
kafka_consumer.store_offset(message)
|
10
11
|
kafka_consumer.commit
|
11
12
|
else
|
12
13
|
reconnect_to_reprocess_same_message(kafka_consumer)
|
@@ -9,30 +9,44 @@ module NulogyMessageBusConsumer
|
|
9
9
|
|
10
10
|
def call(**_)
|
11
11
|
@logger.info("Connecting to the MessageBus")
|
12
|
-
consumer = build_consumer
|
13
12
|
@logger.info("Using consumer group id: #{@config.consumer_group_id}")
|
14
13
|
|
15
|
-
|
16
|
-
|
14
|
+
subscribe
|
15
|
+
|
16
|
+
trap("TERM") { kafka_consumer.close }
|
17
17
|
|
18
|
-
|
18
|
+
wait_for_assignment
|
19
19
|
|
20
|
-
|
21
|
-
yield(kafka_consumer: consumer)
|
20
|
+
yield(kafka_consumer: kafka_consumer)
|
22
21
|
end
|
23
22
|
|
24
23
|
private
|
25
24
|
|
26
|
-
def
|
27
|
-
@kafka_consumer
|
25
|
+
def kafka_consumer
|
26
|
+
@kafka_consumer ||= Rdkafka::Config.new(consumer_config).consumer
|
28
27
|
end
|
29
28
|
|
30
29
|
def consumer_config
|
31
|
-
{
|
30
|
+
config = {
|
32
31
|
"bootstrap.servers": @config.bootstrap_servers,
|
33
32
|
"enable.auto.commit": false,
|
34
33
|
"group.id": @config.consumer_group_id,
|
34
|
+
"enable.auto.offset.store": false
|
35
35
|
}
|
36
|
+
|
37
|
+
config["client.id"] = @config.client_id if @config.client_id
|
38
|
+
|
39
|
+
config
|
40
|
+
end
|
41
|
+
|
42
|
+
def subscribe
|
43
|
+
kafka_consumer.subscribe(@config.topic_name)
|
44
|
+
@logger.info("Listening for kafka messages on topic #{@config.topic_name}")
|
45
|
+
end
|
46
|
+
|
47
|
+
def wait_for_assignment
|
48
|
+
KafkaUtils.wait_for_assignment(kafka_consumer)
|
49
|
+
@logger.info("Connected as client: #{kafka_consumer.member_id}")
|
36
50
|
end
|
37
51
|
end
|
38
52
|
end
|
@@ -1,12 +1,5 @@
|
|
1
1
|
module NulogyMessageBusConsumer
|
2
2
|
module Steps
|
3
|
-
class Clock
|
4
|
-
# milliseconds since epoch
|
5
|
-
def now
|
6
|
-
Time.zone.now.to_datetime.strftime("%Q").to_i
|
7
|
-
end
|
8
|
-
end
|
9
|
-
|
10
3
|
class LogMessages
|
11
4
|
def initialize(logger, clock: Clock.new)
|
12
5
|
@logger = logger
|
@@ -17,18 +10,18 @@ module NulogyMessageBusConsumer
|
|
17
10
|
@logger.info(JSON.dump({
|
18
11
|
event: "message_received",
|
19
12
|
kafka_message_id: message.id,
|
20
|
-
message: "Received #{message.id}"
|
13
|
+
message: "Received #{message.id}"
|
21
14
|
}))
|
22
15
|
|
23
16
|
result = yield
|
24
17
|
|
25
|
-
millis = diff_millis(message.created_at, @clock.
|
18
|
+
millis = diff_millis(message.created_at, @clock.ms)
|
26
19
|
@logger.info(JSON.dump({
|
27
20
|
event: "message_processed",
|
28
21
|
kafka_message_id: message.id,
|
29
|
-
message: "Processed #{message.id}",
|
22
|
+
message: "Processed #{message.id} (#{message.topic}##{message.partition}@#{message.offset})",
|
30
23
|
result: result,
|
31
|
-
time_to_processed: millis
|
24
|
+
time_to_processed: millis
|
32
25
|
}))
|
33
26
|
|
34
27
|
result
|
@@ -12,11 +12,11 @@ module NulogyMessageBusConsumer
|
|
12
12
|
kafka_message: kafka_message
|
13
13
|
)
|
14
14
|
end
|
15
|
-
rescue
|
15
|
+
rescue => e
|
16
16
|
@logger.error(JSON.dump({
|
17
17
|
event: "message_processing_errored",
|
18
18
|
class: e.class,
|
19
|
-
message: e.message
|
19
|
+
message: e.message
|
20
20
|
}))
|
21
21
|
|
22
22
|
raise
|
@@ -12,11 +12,11 @@ module NulogyMessageBusConsumer
|
|
12
12
|
kafka_message: kafka_message
|
13
13
|
)
|
14
14
|
end
|
15
|
-
rescue
|
15
|
+
rescue => e
|
16
16
|
@logger.error(JSON.dump({
|
17
17
|
event: "message_processing_errored",
|
18
18
|
class: e.class,
|
19
|
-
message: e.message
|
19
|
+
message: e.message
|
20
20
|
}))
|
21
21
|
|
22
22
|
raise
|
@@ -0,0 +1,42 @@
|
|
1
|
+
module NulogyMessageBusConsumer
|
2
|
+
module Steps
|
3
|
+
# A generic class to run a "task" on a timer (in a separate thread!)
|
4
|
+
# This class runs the code, the Task does the work
|
5
|
+
#
|
6
|
+
# A Task must implement the methods called in this class:
|
7
|
+
# - #extract_args(kwargs)
|
8
|
+
# Called with the keyword arguments (kwargs) that is passed to this step.
|
9
|
+
# This is a chance to pull out references to pipeline variables (e.g. kafka_consumer)
|
10
|
+
# - #call
|
11
|
+
# The work the Task should perform
|
12
|
+
# - #interval
|
13
|
+
# The time, in seconds, between invocations of #call
|
14
|
+
class TimedTask
|
15
|
+
def initialize(task)
|
16
|
+
@task = task
|
17
|
+
end
|
18
|
+
|
19
|
+
def call(**kwargs)
|
20
|
+
@task.extract_args(**kwargs)
|
21
|
+
|
22
|
+
# Ensure that the process is terminated if there is a problem getting the consumption lag.
|
23
|
+
# This also ensures that the process will terminate on-boot if it cannot connect to Kafka,
|
24
|
+
# allowing the container to be terminated by ECS.
|
25
|
+
Thread.abort_on_exception = true
|
26
|
+
Thread.new { run }
|
27
|
+
|
28
|
+
yield
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
def run
|
34
|
+
loop do
|
35
|
+
@task.call
|
36
|
+
|
37
|
+
sleep @task.interval
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|