karafka 2.0.0.rc1 → 2.0.0.rc4
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
- checksums.yaml.gz.sig +0 -0
- data/CHANGELOG.md +24 -0
- data/CONTRIBUTING.md +4 -8
- data/Gemfile.lock +14 -56
- data/LICENSE-COMM +1 -1
- data/README.md +46 -10
- data/config/errors.yml +52 -5
- data/docker-compose.yml +3 -0
- data/karafka.gemspec +4 -6
- data/lib/karafka/active_job/consumer.rb +2 -0
- data/lib/karafka/active_job/job_options_contract.rb +8 -2
- data/lib/karafka/cli/install.rb +15 -2
- data/lib/karafka/cli/server.rb +4 -2
- data/lib/karafka/connection/client.rb +4 -4
- data/lib/karafka/contracts/base.rb +2 -8
- data/lib/karafka/contracts/config.rb +71 -52
- data/lib/karafka/contracts/consumer_group.rb +25 -18
- data/lib/karafka/contracts/consumer_group_topic.rb +30 -16
- data/lib/karafka/contracts/server_cli_options.rb +18 -7
- data/lib/karafka/errors.rb +0 -3
- data/lib/karafka/helpers/colorize.rb +20 -0
- data/lib/karafka/instrumentation/logger_listener.rb +8 -2
- data/lib/karafka/instrumentation/monitor.rb +14 -59
- data/lib/karafka/instrumentation/notifications.rb +52 -0
- data/lib/karafka/instrumentation/vendors/datadog/dashboard.json +1 -0
- data/lib/karafka/instrumentation/vendors/datadog/listener.rb +232 -0
- data/lib/karafka/pro/active_job/dispatcher.rb +5 -2
- data/lib/karafka/pro/active_job/job_options_contract.rb +11 -6
- data/lib/karafka/pro/contracts/base.rb +21 -0
- data/lib/karafka/pro/contracts/consumer_group.rb +34 -0
- data/lib/karafka/pro/contracts/consumer_group_topic.rb +33 -0
- data/lib/karafka/pro/loader.rb +21 -3
- data/lib/karafka/pro/processing/partitioner.rb +22 -3
- data/lib/karafka/pro/routing/builder_extensions.rb +30 -0
- data/lib/karafka/pro/routing/{extensions.rb → topic_extensions.rb} +1 -1
- data/lib/karafka/processing/jobs_queue.rb +11 -0
- data/lib/karafka/processing/worker.rb +4 -2
- data/lib/karafka/setup/config.rb +7 -3
- data/lib/karafka/templates/example_consumer.rb.erb +2 -2
- data/lib/karafka/version.rb +1 -1
- data/lib/karafka.rb +3 -4
- data.tar.gz.sig +0 -0
- metadata +24 -38
- metadata.gz.sig +0 -0
@@ -9,71 +9,90 @@ module Karafka
|
|
9
9
|
# validated per each route (topic + consumer_group) because they can be overwritten,
|
10
10
|
# so we validate all of that once all the routes are defined and ready.
|
11
11
|
class Config < Base
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
12
|
+
configure do |config|
|
13
|
+
config.error_messages = YAML.safe_load(
|
14
|
+
File.read(
|
15
|
+
File.join(Karafka.gem_root, 'config', 'errors.yml')
|
16
|
+
)
|
17
|
+
).fetch('en').fetch('validations').fetch('config')
|
18
|
+
end
|
19
|
+
|
20
|
+
# License validity happens in the licenser. Here we do only the simple consistency checks
|
21
|
+
nested(:license) do
|
22
|
+
required(:token) { |val| [true, false].include?(val) || val.is_a?(String) }
|
23
|
+
required(:entity) { |val| val.is_a?(String) }
|
24
|
+
required(:expires_on) { |val| val.is_a?(Date) }
|
25
|
+
end
|
26
|
+
|
27
|
+
required(:client_id) { |val| val.is_a?(String) && Contracts::TOPIC_REGEXP.match?(val) }
|
28
|
+
required(:concurrency) { |val| val.is_a?(Integer) && val.positive? }
|
29
|
+
required(:consumer_mapper) { |val| !val.nil? }
|
30
|
+
required(:consumer_persistence) { |val| [true, false].include?(val) }
|
31
|
+
required(:pause_timeout) { |val| val.is_a?(Integer) && val.positive? }
|
32
|
+
required(:pause_max_timeout) { |val| val.is_a?(Integer) && val.positive? }
|
33
|
+
required(:pause_with_exponential_backoff) { |val| [true, false].include?(val) }
|
34
|
+
required(:shutdown_timeout) { |val| val.is_a?(Integer) && val.positive? }
|
35
|
+
required(:max_wait_time) { |val| val.is_a?(Integer) && val.positive? }
|
36
|
+
required(:kafka) { |val| val.is_a?(Hash) && !val.empty? }
|
37
|
+
|
38
|
+
# We validate internals just to be sure, that they are present and working
|
39
|
+
nested(:internal) do
|
40
|
+
required(:status) { |val| !val.nil? }
|
41
|
+
required(:process) { |val| !val.nil? }
|
42
|
+
|
43
|
+
nested(:routing) do
|
44
|
+
required(:builder) { |val| !val.nil? }
|
45
|
+
required(:subscription_groups_builder) { |val| !val.nil? }
|
46
|
+
end
|
47
|
+
|
48
|
+
nested(:processing) do
|
49
|
+
required(:jobs_builder) { |val| !val.nil? }
|
50
|
+
required(:scheduler) { |val| !val.nil? }
|
51
|
+
required(:coordinator_class) { |val| !val.nil? }
|
52
|
+
required(:partitioner_class) { |val| !val.nil? }
|
18
53
|
end
|
19
54
|
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
required(:pause_timeout) { int? & gt?(0) }
|
25
|
-
required(:pause_max_timeout) { int? & gt?(0) }
|
26
|
-
required(:pause_with_exponential_backoff).filled(:bool?)
|
27
|
-
required(:shutdown_timeout) { int? & gt?(0) }
|
28
|
-
required(:max_wait_time) { int? & gt?(0) }
|
29
|
-
required(:kafka).filled(:hash)
|
30
|
-
|
31
|
-
# We validate internals just to be sure, that they are present and working
|
32
|
-
required(:internal).schema do
|
33
|
-
required(:status)
|
34
|
-
required(:process)
|
35
|
-
|
36
|
-
required(:routing).schema do
|
37
|
-
required(:builder)
|
38
|
-
required(:subscription_groups_builder)
|
39
|
-
end
|
40
|
-
|
41
|
-
required(:processing).schema do
|
42
|
-
required(:jobs_builder)
|
43
|
-
required(:scheduler)
|
44
|
-
required(:coordinator_class)
|
45
|
-
required(:partitioner_class)
|
46
|
-
end
|
47
|
-
|
48
|
-
required(:active_job).schema do
|
49
|
-
required(:dispatcher)
|
50
|
-
required(:job_options_contract)
|
51
|
-
required(:consumer_class)
|
52
|
-
end
|
55
|
+
nested(:active_job) do
|
56
|
+
required(:dispatcher) { |val| !val.nil? }
|
57
|
+
required(:job_options_contract) { |val| !val.nil? }
|
58
|
+
required(:consumer_class) { |val| !val.nil? }
|
53
59
|
end
|
54
60
|
end
|
55
61
|
|
56
|
-
|
57
|
-
|
58
|
-
|
62
|
+
virtual do |data, errors|
|
63
|
+
next unless errors.empty?
|
64
|
+
|
65
|
+
detected_errors = []
|
59
66
|
|
60
|
-
|
67
|
+
data.fetch(:kafka).each_key do |key|
|
61
68
|
next if key.is_a?(Symbol)
|
62
69
|
|
63
|
-
|
70
|
+
detected_errors << [[:kafka, key], :key_must_be_a_symbol]
|
64
71
|
end
|
72
|
+
|
73
|
+
detected_errors
|
65
74
|
end
|
66
75
|
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
76
|
+
virtual do |data, errors|
|
77
|
+
next unless errors.empty?
|
78
|
+
|
79
|
+
pause_timeout = data.fetch(:pause_timeout)
|
80
|
+
pause_max_timeout = data.fetch(:pause_max_timeout)
|
81
|
+
|
82
|
+
next if pause_timeout <= pause_max_timeout
|
83
|
+
|
84
|
+
[[%i[pause_timeout], :max_timeout_vs_pause_max_timeout]]
|
71
85
|
end
|
72
86
|
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
87
|
+
virtual do |data, errors|
|
88
|
+
next unless errors.empty?
|
89
|
+
|
90
|
+
shutdown_timeout = data.fetch(:shutdown_timeout)
|
91
|
+
max_wait_time = data.fetch(:max_wait_time)
|
92
|
+
|
93
|
+
next if max_wait_time < shutdown_timeout
|
94
|
+
|
95
|
+
[[%i[shutdown_timeout], :shutdown_timeout_vs_max_wait_time]]
|
77
96
|
end
|
78
97
|
end
|
79
98
|
end
|
@@ -4,32 +4,39 @@ module Karafka
|
|
4
4
|
module Contracts
|
5
5
|
# Contract for single full route (consumer group + topics) validation.
|
6
6
|
class ConsumerGroup < Base
|
7
|
-
|
8
|
-
|
7
|
+
configure do |config|
|
8
|
+
config.error_messages = YAML.safe_load(
|
9
|
+
File.read(
|
10
|
+
File.join(Karafka.gem_root, 'config', 'errors.yml')
|
11
|
+
)
|
12
|
+
).fetch('en').fetch('validations').fetch('consumer_group')
|
13
|
+
end
|
9
14
|
|
10
|
-
|
15
|
+
required(:id) { |id| id.is_a?(String) && Contracts::TOPIC_REGEXP.match?(id) }
|
16
|
+
required(:topics) { |topics| topics.is_a?(Array) && !topics.empty? }
|
11
17
|
|
12
|
-
|
13
|
-
|
14
|
-
required(:topics).value(:array, :filled?)
|
15
|
-
end
|
18
|
+
virtual do |data, errors|
|
19
|
+
next unless errors.empty?
|
16
20
|
|
17
|
-
|
18
|
-
if value.is_a?(Array)
|
19
|
-
names = value.map { |topic| topic[:name] }
|
21
|
+
names = data.fetch(:topics).map { |topic| topic[:name] }
|
20
22
|
|
21
|
-
|
22
|
-
|
23
|
+
next if names.size == names.uniq.size
|
24
|
+
|
25
|
+
[[%i[topics], :names_not_unique]]
|
23
26
|
end
|
24
27
|
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
28
|
+
virtual do |data, errors|
|
29
|
+
next unless errors.empty?
|
30
|
+
|
31
|
+
fetched_errors = []
|
32
|
+
|
33
|
+
data.fetch(:topics).each do |topic|
|
34
|
+
ConsumerGroupTopic.new.call(topic).errors.each do |key, value|
|
35
|
+
fetched_errors << [[topic, key].flatten, value]
|
31
36
|
end
|
32
37
|
end
|
38
|
+
|
39
|
+
fetched_errors
|
33
40
|
end
|
34
41
|
end
|
35
42
|
end
|
@@ -4,24 +4,38 @@ module Karafka
|
|
4
4
|
module Contracts
|
5
5
|
# Consumer group topic validation rules.
|
6
6
|
class ConsumerGroupTopic < Base
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
required(:initial_offset).filled(included_in?: %w[earliest latest])
|
14
|
-
required(:max_wait_time).filled { int? & gteq?(10) }
|
15
|
-
required(:manual_offset_management).filled(:bool?)
|
16
|
-
required(:name).filled(:str?, format?: Karafka::Contracts::TOPIC_REGEXP)
|
7
|
+
configure do |config|
|
8
|
+
config.error_messages = YAML.safe_load(
|
9
|
+
File.read(
|
10
|
+
File.join(Karafka.gem_root, 'config', 'errors.yml')
|
11
|
+
)
|
12
|
+
).fetch('en').fetch('validations').fetch('consumer_group_topic')
|
17
13
|
end
|
18
14
|
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
15
|
+
required(:consumer) { |consumer_group| !consumer_group.nil? }
|
16
|
+
required(:deserializer) { |deserializer| !deserializer.nil? }
|
17
|
+
required(:id) { |id| id.is_a?(String) && Contracts::TOPIC_REGEXP.match?(id) }
|
18
|
+
required(:kafka) { |kafka| kafka.is_a?(Hash) && !kafka.empty? }
|
19
|
+
required(:max_messages) { |mm| mm.is_a?(Integer) && mm >= 1 }
|
20
|
+
required(:initial_offset) { |io| %w[earliest latest].include?(io) }
|
21
|
+
required(:max_wait_time) { |mwt| mwt.is_a?(Integer) && mwt >= 10 }
|
22
|
+
required(:manual_offset_management) { |mmm| [true, false].include?(mmm) }
|
23
|
+
required(:name) { |name| name.is_a?(String) && Contracts::TOPIC_REGEXP.match?(name) }
|
24
|
+
|
25
|
+
virtual do |data, errors|
|
26
|
+
next unless errors.empty?
|
27
|
+
|
28
|
+
value = data.fetch(:kafka)
|
29
|
+
|
30
|
+
begin
|
31
|
+
# This will trigger rdkafka validations that we catch and re-map the info and use dry
|
32
|
+
# compatible format
|
33
|
+
Rdkafka::Config.new(value).send(:native_config)
|
34
|
+
|
35
|
+
nil
|
36
|
+
rescue Rdkafka::Config::ConfigError => e
|
37
|
+
[[%w[kafka], e.message]]
|
38
|
+
end
|
25
39
|
end
|
26
40
|
end
|
27
41
|
end
|
@@ -4,17 +4,28 @@ module Karafka
|
|
4
4
|
module Contracts
|
5
5
|
# Contract for validating correctness of the server cli command options.
|
6
6
|
class ServerCliOptions < Base
|
7
|
-
|
8
|
-
|
7
|
+
configure do |config|
|
8
|
+
config.error_messages = YAML.safe_load(
|
9
|
+
File.read(
|
10
|
+
File.join(Karafka.gem_root, 'config', 'errors.yml')
|
11
|
+
)
|
12
|
+
).fetch('en').fetch('validations').fetch('server_cli_options')
|
9
13
|
end
|
10
14
|
|
11
|
-
|
15
|
+
optional(:consumer_groups) { |cg| cg.is_a?(Array) && !cg.empty? }
|
16
|
+
|
17
|
+
virtual do |data, errors|
|
18
|
+
next unless errors.empty?
|
19
|
+
next unless data.key?(:consumer_groups)
|
20
|
+
|
21
|
+
value = data.fetch(:consumer_groups)
|
22
|
+
|
12
23
|
# If there were no consumer_groups declared in the server cli, it means that we will
|
13
24
|
# run all of them and no need to validate them here at all
|
14
|
-
if
|
15
|
-
|
16
|
-
|
17
|
-
|
25
|
+
next if value.nil?
|
26
|
+
next if (value - Karafka::App.config.internal.routing.builder.map(&:name)).empty?
|
27
|
+
|
28
|
+
[[%i[consumer_groups], :consumer_groups_inclusion]]
|
18
29
|
end
|
19
30
|
end
|
20
31
|
end
|
data/lib/karafka/errors.rb
CHANGED
@@ -24,9 +24,6 @@ module Karafka
|
|
24
24
|
# Raised when we try to use Karafka CLI commands (except install) without a boot file
|
25
25
|
MissingBootFileError = Class.new(BaseError)
|
26
26
|
|
27
|
-
# Raised when want to hook up to an event that is not registered and supported
|
28
|
-
UnregisteredMonitorEventError = Class.new(BaseError)
|
29
|
-
|
30
27
|
# Raised when we've waited enough for shutting down a non-responsive process
|
31
28
|
ForcefulShutdownError = Class.new(BaseError)
|
32
29
|
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Karafka
|
4
|
+
module Helpers
|
5
|
+
# Simple wrapper for adding colors to strings
|
6
|
+
module Colorize
|
7
|
+
# @param string [String] string we want to have in green
|
8
|
+
# @return [String] green string
|
9
|
+
def green(string)
|
10
|
+
"\033[0;32m#{string}\033[0m"
|
11
|
+
end
|
12
|
+
|
13
|
+
# @param string [String] string we want to have in red
|
14
|
+
# @return [String] red string
|
15
|
+
def red(string)
|
16
|
+
"\033[0;31m#{string}\033[0m"
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -18,7 +18,7 @@ module Karafka
|
|
18
18
|
# @param event [Dry::Events::Event] event details including payload
|
19
19
|
def on_connection_listener_fetch_loop(event)
|
20
20
|
listener = event[:caller]
|
21
|
-
|
21
|
+
debug "[#{listener.id}] Polling messages..."
|
22
22
|
end
|
23
23
|
|
24
24
|
# Logs about messages that we've received from Kafka
|
@@ -28,7 +28,13 @@ module Karafka
|
|
28
28
|
listener = event[:caller]
|
29
29
|
time = event[:time]
|
30
30
|
messages_count = event[:messages_buffer].size
|
31
|
-
|
31
|
+
|
32
|
+
message = "[#{listener.id}] Polled #{messages_count} messages in #{time}ms"
|
33
|
+
|
34
|
+
# We don't want the "polled 0" in dev as it would spam the log
|
35
|
+
# Instead we publish only info when there was anything we could poll and fail over to the
|
36
|
+
# zero notifications when in debug mode
|
37
|
+
messages_count.zero? ? debug(message) : info(message)
|
32
38
|
end
|
33
39
|
|
34
40
|
# Prints info about the fact that a given job has started
|
@@ -1,66 +1,21 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module Karafka
|
4
|
-
# Namespace for all the things related with Karafka instrumentation process
|
5
4
|
module Instrumentation
|
6
|
-
#
|
7
|
-
#
|
8
|
-
#
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
#
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
app.initialized
|
21
|
-
app.running
|
22
|
-
app.stopping
|
23
|
-
app.stopped
|
24
|
-
|
25
|
-
consumer.consumed
|
26
|
-
consumer.revoked
|
27
|
-
consumer.shutdown
|
28
|
-
|
29
|
-
process.notice_signal
|
30
|
-
|
31
|
-
connection.listener.before_fetch_loop
|
32
|
-
connection.listener.fetch_loop
|
33
|
-
connection.listener.fetch_loop.received
|
34
|
-
|
35
|
-
worker.process
|
36
|
-
worker.processed
|
37
|
-
|
38
|
-
statistics.emitted
|
39
|
-
|
40
|
-
error.occurred
|
41
|
-
].freeze
|
42
|
-
|
43
|
-
private_constant :BASE_EVENTS
|
44
|
-
|
45
|
-
# @return [Karafka::Instrumentation::Monitor] monitor instance for system instrumentation
|
46
|
-
def initialize
|
47
|
-
super(:karafka)
|
48
|
-
BASE_EVENTS.each(&method(:register_event))
|
49
|
-
end
|
50
|
-
|
51
|
-
# Allows us to subscribe to events with a code that will be yielded upon events
|
52
|
-
# @param event_name_or_listener [String, Object] name of the event we want to subscribe to
|
53
|
-
# or a listener if we decide to go with object listener
|
54
|
-
def subscribe(event_name_or_listener)
|
55
|
-
return super unless event_name_or_listener.is_a?(String)
|
56
|
-
return super if available_events.include?(event_name_or_listener)
|
57
|
-
|
58
|
-
raise Errors::UnregisteredMonitorEventError, event_name_or_listener
|
59
|
-
end
|
60
|
-
|
61
|
-
# @return [Array<String>] names of available events to which we can subscribe
|
62
|
-
def available_events
|
63
|
-
__bus__.events.keys
|
5
|
+
# Karafka instrumentation monitor that we use to publish events
|
6
|
+
# By default uses our internal notifications bus but can be used with
|
7
|
+
# `ActiveSupport::Notifications` as well
|
8
|
+
class Monitor < ::Karafka::Core::Monitoring::Monitor
|
9
|
+
attr_reader :notifications_bus
|
10
|
+
|
11
|
+
# @param notifications_bus [Object] either our internal notifications bus or
|
12
|
+
# `ActiveSupport::Notifications`
|
13
|
+
# @param namespace [String, nil] namespace for events or nil if no namespace
|
14
|
+
def initialize(
|
15
|
+
notifications_bus = ::Karafka::Instrumentation::Notifications.new,
|
16
|
+
namespace = nil
|
17
|
+
)
|
18
|
+
super(notifications_bus, namespace)
|
64
19
|
end
|
65
20
|
end
|
66
21
|
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Karafka
|
4
|
+
# Namespace for all the things related with Karafka instrumentation process
|
5
|
+
module Instrumentation
|
6
|
+
# Monitor is used to hookup external monitoring services to monitor how Karafka works
|
7
|
+
# It provides a standardized API for checking incoming messages/enqueueing etc
|
8
|
+
# Since it is a pub-sub based on dry-monitor, you can use as many subscribers/loggers at the
|
9
|
+
# same time, which means that you might have for example file logging and NewRelic at the same
|
10
|
+
# time
|
11
|
+
# @note This class acts as a singleton because we are only permitted to have single monitor
|
12
|
+
# per running process (just as logger)
|
13
|
+
class Notifications < Karafka::Core::Monitoring::Notifications
|
14
|
+
# List of events that we support in the system and to which a monitor client can hook up
|
15
|
+
# @note The non-error once support timestamp benchmarking
|
16
|
+
# @note Depending on Karafka extensions and additional engines, this might not be the
|
17
|
+
# complete list of all the events. Please use the #available_events on fully loaded
|
18
|
+
# Karafka system to determine all of the events you can use.
|
19
|
+
EVENTS = %w[
|
20
|
+
app.initialized
|
21
|
+
app.running
|
22
|
+
app.stopping
|
23
|
+
app.stopped
|
24
|
+
|
25
|
+
consumer.consumed
|
26
|
+
consumer.revoked
|
27
|
+
consumer.shutdown
|
28
|
+
|
29
|
+
process.notice_signal
|
30
|
+
|
31
|
+
connection.listener.before_fetch_loop
|
32
|
+
connection.listener.fetch_loop
|
33
|
+
connection.listener.fetch_loop.received
|
34
|
+
|
35
|
+
worker.process
|
36
|
+
worker.processed
|
37
|
+
|
38
|
+
statistics.emitted
|
39
|
+
|
40
|
+
error.occurred
|
41
|
+
].freeze
|
42
|
+
|
43
|
+
private_constant :EVENTS
|
44
|
+
|
45
|
+
# @return [Karafka::Instrumentation::Monitor] monitor instance for system instrumentation
|
46
|
+
def initialize
|
47
|
+
super
|
48
|
+
EVENTS.each { |event| register_event(event) }
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
{"title":"Karafka monitoring dashboard","description":"","widgets":[{"id":7444969424381053,"definition":{"title":"Stability & errors","type":"group","show_title":true,"layout_type":"ordered","widgets":[{"id":8304008422587936,"definition":{"title":"Client connects and disconnects","title_size":"16","title_align":"left","show_legend":true,"legend_layout":"auto","legend_columns":["avg","min","max","value","sum"],"type":"timeseries","requests":[{"formulas":[{"alias":"Connects","formula":"query1"},{"alias":"Disconnects","formula":"query2"}],"response_format":"timeseries","queries":[{"query":"sum:karafka.connection.connects{*} by {host}.as_count()","data_source":"metrics","name":"query1"},{"query":"sum:karafka.connection.disconnects{*} by {host}.as_count()","data_source":"metrics","name":"query2"}],"style":{"palette":"dog_classic","line_type":"solid","line_width":"normal"},"display_type":"bars"}]},"layout":{"x":0,"y":0,"width":4,"height":2}},{"id":3722865443336921,"definition":{"title":"Errors encountered (any)","title_size":"16","title_align":"left","show_legend":true,"legend_layout":"auto","legend_columns":["avg","min","max","value","sum"],"type":"timeseries","requests":[{"formulas":[{"alias":"in-karafka errors","formula":"query1"},{"alias":"librdkafka consume errors","formula":"query2"},{"alias":"librdkafka receive errors","formula":"query3"}],"response_format":"timeseries","queries":[{"query":"sum:karafka.error_occurred{*} by {type}.as_count()","data_source":"metrics","name":"query1"},{"query":"sum:karafka.consume.errors{*}.as_count()","data_source":"metrics","name":"query2"},{"query":"sum:karafka.receive.errors{*}.as_count()","data_source":"metrics","name":"query3"}],"style":{"palette":"dog_classic","line_type":"solid","line_width":"normal"},"display_type":"line"}]},"layout":{"x":4,"y":0,"width":4,"height":2}},{"id":5477381252952760,"definition":{"title":"Processing errors","title_size":"16","title_align":"left","show_legend":true,"legend_layout":"auto","legend_columns":["avg","min","max","value","sum"],"type":"timeseries","requests":[{"formulas":[{"formula":"query1"}],"response_format":"timeseries","queries":[{"query":"sum:karafka.error_occurred{type:consumer.consume.error} by {partition,topic}.as_count()","data_source":"metrics","name":"query1"}],"style":{"palette":"dog_classic","line_type":"solid","line_width":"normal"},"display_type":"line"}]},"layout":{"x":8,"y":0,"width":4,"height":2}},{"id":2357301680769076,"definition":{"title":"Processing errors rate per topic","title_size":"16","title_align":"left","show_legend":true,"legend_layout":"auto","legend_columns":["avg","min","max","value","sum"],"type":"timeseries","requests":[{"formulas":[{"alias":"% error rate per topic","formula":"(query1 / (query1 + query2)) * 100"}],"response_format":"timeseries","queries":[{"query":"sum:karafka.error_occurred{type:consumer.consume.error} by {topic,partition}.as_count()","data_source":"metrics","name":"query1"},{"query":"sum:karafka.consumer.batches{*} by {topic,partition}.as_count()","data_source":"metrics","name":"query2"}],"style":{"palette":"dog_classic","line_type":"solid","line_width":"normal"},"display_type":"bars"}]},"layout":{"x":0,"y":2,"width":4,"height":2}},{"id":3902930069982135,"definition":{"title":"Batches successful vs failures","title_size":"16","title_align":"left","show_legend":true,"legend_layout":"auto","legend_columns":["avg","min","max","value","sum"],"type":"timeseries","requests":[{"formulas":[{"alias":"Successfully processed batch","formula":"query1"},{"alias":"Batch processing with error","formula":"query2"}],"response_format":"timeseries","queries":[{"query":"sum:karafka.consumer.batches{*} by {partition,topic}.as_count()","data_source":"metrics","name":"query1"},{"query":"avg:karafka.error_occurred{type:consumer.consume.error} by {partition,topic}.as_count()","data_source":"metrics","name":"query2"}],"style":{"palette":"dog_classic","line_type":"solid","line_width":"normal"},"display_type":"line"}]},"layout":{"x":4,"y":2,"width":4,"height":2}},{"id":718749162159145,"definition":{"title":"Consumer instances revocations and shutdowns","title_size":"16","title_align":"left","show_legend":true,"legend_layout":"auto","legend_columns":["avg","min","max","value","sum"],"type":"timeseries","requests":[{"formulas":[{"alias":"Consumer instances revokations","formula":"query1"},{"alias":"Consumer instances shutdowns","formula":"query2"}],"response_format":"timeseries","queries":[{"query":"sum:karafka.consumer.revoked{*}.as_count()","data_source":"metrics","name":"query1"},{"query":"sum:karafka.consumer.shutdown{*}.as_count()","data_source":"metrics","name":"query2"}],"style":{"palette":"dog_classic","line_type":"solid","line_width":"normal"},"display_type":"line"}]},"layout":{"x":8,"y":2,"width":4,"height":2}}]},"layout":{"x":0,"y":0,"width":12,"height":5}},{"id":5988438511387100,"definition":{"title":"Workers poll","type":"group","show_title":true,"layout_type":"ordered","widgets":[{"id":8769294644934352,"definition":{"title":"Enqueued jobs","title_size":"16","title_align":"left","show_legend":true,"legend_layout":"auto","legend_columns":["avg","min","max","value","sum"],"type":"timeseries","requests":[{"formulas":[{"alias":"Enqueued jobs","formula":"query1"}],"response_format":"timeseries","queries":[{"query":"sum:karafka.worker.enqueued_jobs.avg{*}","data_source":"metrics","name":"query1"}],"style":{"palette":"dog_classic","line_type":"solid","line_width":"normal"},"display_type":"line"}]},"layout":{"x":0,"y":0,"width":4,"height":2}},{"id":2714502141463873,"definition":{"title":"Workers usage","title_size":"16","title_align":"left","show_legend":true,"legend_layout":"auto","legend_columns":["avg","min","max","value","sum"],"type":"timeseries","requests":[{"formulas":[{"alias":"Busy workers (p95)","formula":"query1"},{"alias":"Total workers","formula":"query2"}],"response_format":"timeseries","queries":[{"query":"sum:karafka.worker.processing.95percentile{*}","data_source":"metrics","name":"query1"},{"query":"sum:karafka.worker.total_threads{*}","data_source":"metrics","name":"query2"}],"style":{"palette":"dog_classic","line_type":"solid","line_width":"normal"},"display_type":"line"}]},"layout":{"x":4,"y":0,"width":4,"height":2}},{"id":5370086629441984,"definition":{"title":"Workers % utilization","title_size":"16","title_align":"left","show_legend":true,"legend_layout":"auto","legend_columns":["avg","min","max","value","sum"],"type":"timeseries","requests":[{"formulas":[{"alias":"% workers utilization","formula":"(query1 / query2) * 100"}],"response_format":"timeseries","queries":[{"query":"sum:karafka.worker.processing.95percentile{*}","data_source":"metrics","name":"query1"},{"query":"sum:karafka.worker.total_threads{*}","data_source":"metrics","name":"query2"}],"style":{"palette":"dog_classic","line_type":"solid","line_width":"normal"},"display_type":"line"}]},"layout":{"x":8,"y":0,"width":4,"height":2}}]},"layout":{"x":0,"y":5,"width":12,"height":3}},{"id":8544040083223278,"definition":{"title":"Throughput ","type":"group","show_title":true,"layout_type":"ordered","widgets":[{"id":3740207481939733,"definition":{"title":"Offset lag changes","title_size":"16","title_align":"left","show_legend":true,"legend_layout":"auto","legend_columns":["avg","min","max","value","sum"],"type":"timeseries","requests":[{"formulas":[{"formula":"derivative(query1)"}],"response_format":"timeseries","queries":[{"query":"max:karafka.consumer.offset{*} by {topic,partition}","data_source":"metrics","name":"query1"}],"style":{"palette":"dog_classic","line_type":"solid","line_width":"normal"},"display_type":"line"}]},"layout":{"x":0,"y":0,"width":4,"height":2}},{"id":6319110548544878,"definition":{"title":"Batches processed per topic","title_size":"16","title_align":"left","show_legend":true,"legend_layout":"auto","legend_columns":["avg","min","max","value","sum"],"type":"timeseries","requests":[{"formulas":[{"formula":"query1"}],"response_format":"timeseries","queries":[{"query":"sum:karafka.consumer.batches{*} by {partition,topic}.as_count()","data_source":"metrics","name":"query1"}],"style":{"palette":"dog_classic","line_type":"solid","line_width":"normal"},"display_type":"line"}]},"layout":{"x":4,"y":0,"width":4,"height":2}},{"id":6232784865331443,"definition":{"title":"Messages consumed per topic","title_size":"16","title_align":"left","show_legend":true,"legend_layout":"auto","legend_columns":["avg","min","max","value","sum"],"type":"timeseries","requests":[{"formulas":[{"alias":"Messages consumed","formula":"query1"},{"alias":"Average batch size","formula":"query1 / query2"}],"response_format":"timeseries","queries":[{"query":"sum:karafka.consumer.messages{*} by {partition,topic}.as_count()","data_source":"metrics","name":"query1"},{"query":"sum:karafka.consumer.batches{*} by {partition,topic}.as_count()","data_source":"metrics","name":"query2"}],"style":{"palette":"dog_classic","line_type":"solid","line_width":"normal"},"display_type":"line"}]},"layout":{"x":8,"y":0,"width":4,"height":2}},{"id":2321394598982770,"definition":{"title":"Consumption lag (in seconds)","title_size":"16","title_align":"left","show_legend":true,"legend_layout":"auto","legend_columns":["avg","min","max","value","sum"],"type":"timeseries","requests":[{"formulas":[{"alias":"Consumption lag in s (max)","formula":"query2 / 1000"},{"alias":"Consumption lag in s (avg)","formula":"query3 / 1000"},{"alias":"Consumption lag in s (p95)","formula":"query1 / 1000"}],"response_format":"timeseries","queries":[{"query":"max:karafka.consumer.consumption_lag.max{*}","data_source":"metrics","name":"query2"},{"query":"max:karafka.consumer.consumption_lag.avg{*}","data_source":"metrics","name":"query3"},{"query":"max:karafka.consumer.consumption_lag.95percentile{*}","data_source":"metrics","name":"query1"}],"style":{"palette":"dog_classic","line_type":"solid","line_width":"normal"},"display_type":"line"}]},"layout":{"x":0,"y":2,"width":4,"height":2}},{"id":1062074781483741,"definition":{"title":"Processing lag (in ms)","title_size":"16","title_align":"left","show_legend":true,"legend_layout":"auto","legend_columns":["avg","min","max","value","sum"],"type":"timeseries","requests":[{"formulas":[{"alias":"Processing lag in ms (p95)","formula":"query1"},{"alias":"Processing lag in ms (max)","formula":"query2"},{"alias":"Processing lag in ms (avg)","formula":"query3"}],"response_format":"timeseries","queries":[{"query":"max:karafka.consumer.processing_lag.95percentile{*}","data_source":"metrics","name":"query1"},{"query":"max:karafka.consumer.processing_lag.max{*}","data_source":"metrics","name":"query2"},{"query":"max:karafka.consumer.processing_lag.avg{*}","data_source":"metrics","name":"query3"}],"style":{"palette":"dog_classic","line_type":"solid","line_width":"normal"},"display_type":"line"}]},"layout":{"x":4,"y":2,"width":4,"height":2}},{"id":7497794728674267,"definition":{"title":"Batch processing time","title_size":"16","title_align":"left","show_legend":true,"legend_layout":"auto","legend_columns":["avg","min","max","value","sum"],"type":"timeseries","requests":[{"formulas":[{"formula":"query1"},{"formula":"query2"},{"formula":"query3"}],"response_format":"timeseries","queries":[{"query":"max:karafka.consumer.consumed.time_taken.95percentile{*} by {topic,partition}","data_source":"metrics","name":"query1"},{"query":"max:karafka.consumer.consumed.time_taken.max{*} by {topic,partition}","data_source":"metrics","name":"query2"},{"query":"max:karafka.consumer.consumed.time_taken.avg{*} by {topic,partition}","data_source":"metrics","name":"query3"}],"style":{"palette":"dog_classic","line_type":"solid","line_width":"normal"},"display_type":"line"}]},"layout":{"x":8,"y":2,"width":4,"height":2}},{"id":4192833027984161,"definition":{"title":"Batch size per topic","title_size":"16","title_align":"left","show_legend":true,"legend_layout":"auto","legend_columns":["avg","min","max","value","sum"],"type":"timeseries","requests":[{"formulas":[{"alias":"Batch size p95","formula":"query1"},{"alias":"Batch size avg","formula":"query2"},{"alias":"Batch size max","formula":"query3"}],"response_format":"timeseries","queries":[{"query":"sum:karafka.consumer.batch_size.95percentile{*} by {partition,topic}","data_source":"metrics","name":"query1"},{"query":"sum:karafka.consumer.batch_size.avg{*} by {partition,topic}","data_source":"metrics","name":"query2"},{"query":"sum:karafka.consumer.batch_size.max{*} by {partition,topic}","data_source":"metrics","name":"query3"}],"style":{"palette":"dog_classic","line_type":"solid","line_width":"normal"},"display_type":"line"}]},"layout":{"x":0,"y":4,"width":4,"height":2}},{"id":4741598444771147,"definition":{"title":"Messages consumed overall","title_size":"16","title_align":"left","show_legend":true,"legend_layout":"auto","legend_columns":["avg","min","max","value","sum"],"type":"timeseries","requests":[{"formulas":[{"alias":"Messages consumed","formula":"query1"},{"alias":"Average batch size","formula":"query1 / query2"}],"response_format":"timeseries","queries":[{"query":"sum:karafka.consumer.messages{*}.as_count()","data_source":"metrics","name":"query1"},{"query":"sum:karafka.consumer.batches{*}.as_count()","data_source":"metrics","name":"query2"}],"style":{"palette":"dog_classic","line_type":"solid","line_width":"normal"},"display_type":"line"}]},"layout":{"x":4,"y":4,"width":4,"height":2}},{"id":4502534794102513,"definition":{"title":"Polling times (ms)","title_size":"16","title_align":"left","show_legend":true,"legend_layout":"auto","legend_columns":["avg","min","max","value","sum"],"time":{},"type":"timeseries","requests":[{"formulas":[{"alias":"p95 ms polling time","formula":"query1"},{"alias":"max ms polling time","formula":"query2"},{"alias":"average ms polling time","formula":"query3"}],"queries":[{"name":"query1","data_source":"metrics","query":"avg:karafka.listener.polling.time_taken.95percentile{*}"},{"name":"query2","data_source":"metrics","query":"avg:karafka.listener.polling.time_taken.max{*}"},{"name":"query3","data_source":"metrics","query":"avg:karafka.listener.polling.time_taken.avg{*}"}],"response_format":"timeseries","style":{"palette":"dog_classic","line_type":"solid","line_width":"normal"},"display_type":"line"}]},"layout":{"x":8,"y":4,"width":4,"height":2}}]},"layout":{"x":0,"y":0,"width":12,"height":7,"is_column_break":true}}],"template_variables":[],"layout_type":"ordered","is_read_only":false,"notify_list":[],"reflow_type":"fixed","id":"s3u-z47-i6u"}
|