karafka-web 0.6.3 → 0.7.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data/.github/workflows/ci.yml +13 -4
- data/CHANGELOG.md +119 -5
- data/Gemfile +1 -0
- data/Gemfile.lock +27 -24
- data/README.md +2 -0
- data/bin/rspecs +6 -0
- data/certs/cert_chain.pem +21 -21
- data/docker-compose.yml +22 -0
- data/karafka-web.gemspec +3 -3
- data/lib/karafka/web/app.rb +6 -2
- data/lib/karafka/web/cli.rb +51 -47
- data/lib/karafka/web/config.rb +33 -9
- data/lib/karafka/web/contracts/base.rb +32 -0
- data/lib/karafka/web/contracts/config.rb +63 -0
- data/lib/karafka/web/deserializer.rb +10 -1
- data/lib/karafka/web/errors.rb +29 -7
- data/lib/karafka/web/installer.rb +58 -148
- data/lib/karafka/web/management/base.rb +34 -0
- data/lib/karafka/web/management/clean_boot_file.rb +31 -0
- data/lib/karafka/web/management/create_initial_states.rb +101 -0
- data/lib/karafka/web/management/create_topics.rb +127 -0
- data/lib/karafka/web/management/delete_topics.rb +28 -0
- data/lib/karafka/web/management/enable.rb +82 -0
- data/lib/karafka/web/management/extend_boot_file.rb +37 -0
- data/lib/karafka/web/processing/consumer.rb +73 -17
- data/lib/karafka/web/processing/consumers/aggregators/base.rb +56 -0
- data/lib/karafka/web/processing/consumers/aggregators/metrics.rb +154 -0
- data/lib/karafka/web/processing/consumers/aggregators/state.rb +180 -0
- data/lib/karafka/web/processing/consumers/contracts/aggregated_stats.rb +32 -0
- data/lib/karafka/web/processing/consumers/contracts/metrics.rb +53 -0
- data/lib/karafka/web/processing/consumers/contracts/process.rb +19 -0
- data/lib/karafka/web/processing/consumers/contracts/state.rb +49 -0
- data/lib/karafka/web/processing/consumers/contracts/topic_stats.rb +21 -0
- data/lib/karafka/web/processing/consumers/metrics.rb +29 -0
- data/lib/karafka/web/processing/consumers/schema_manager.rb +56 -0
- data/lib/karafka/web/processing/consumers/state.rb +6 -9
- data/lib/karafka/web/processing/time_series_tracker.rb +130 -0
- data/lib/karafka/web/tracking/consumers/contracts/consumer_group.rb +2 -2
- data/lib/karafka/web/tracking/consumers/contracts/job.rb +2 -1
- data/lib/karafka/web/tracking/consumers/contracts/partition.rb +14 -1
- data/lib/karafka/web/tracking/consumers/contracts/report.rb +10 -8
- data/lib/karafka/web/tracking/consumers/contracts/subscription_group.rb +2 -2
- data/lib/karafka/web/tracking/consumers/contracts/topic.rb +2 -2
- data/lib/karafka/web/tracking/consumers/listeners/processing.rb +6 -2
- data/lib/karafka/web/tracking/consumers/listeners/statistics.rb +15 -1
- data/lib/karafka/web/tracking/consumers/reporter.rb +14 -6
- data/lib/karafka/web/tracking/consumers/sampler.rb +80 -39
- data/lib/karafka/web/tracking/contracts/error.rb +2 -1
- data/lib/karafka/web/ui/app.rb +20 -10
- data/lib/karafka/web/ui/base.rb +56 -6
- data/lib/karafka/web/ui/controllers/base.rb +28 -0
- data/lib/karafka/web/ui/controllers/become_pro.rb +1 -1
- data/lib/karafka/web/ui/controllers/cluster.rb +12 -6
- data/lib/karafka/web/ui/controllers/consumers.rb +4 -2
- data/lib/karafka/web/ui/controllers/dashboard.rb +32 -0
- data/lib/karafka/web/ui/controllers/errors.rb +19 -6
- data/lib/karafka/web/ui/controllers/jobs.rb +4 -2
- data/lib/karafka/web/ui/controllers/requests/params.rb +28 -0
- data/lib/karafka/web/ui/controllers/responses/redirect.rb +29 -0
- data/lib/karafka/web/ui/helpers/application_helper.rb +57 -14
- data/lib/karafka/web/ui/helpers/paths_helper.rb +48 -0
- data/lib/karafka/web/ui/lib/hash_proxy.rb +18 -6
- data/lib/karafka/web/ui/lib/paginations/base.rb +61 -0
- data/lib/karafka/web/ui/lib/paginations/offset_based.rb +96 -0
- data/lib/karafka/web/ui/lib/paginations/page_based.rb +70 -0
- data/lib/karafka/web/ui/lib/paginations/paginators/arrays.rb +33 -0
- data/lib/karafka/web/ui/lib/paginations/paginators/base.rb +23 -0
- data/lib/karafka/web/ui/lib/paginations/paginators/partitions.rb +52 -0
- data/lib/karafka/web/ui/lib/paginations/paginators/sets.rb +85 -0
- data/lib/karafka/web/ui/lib/paginations/watermark_offsets_based.rb +75 -0
- data/lib/karafka/web/ui/lib/ttl_cache.rb +82 -0
- data/lib/karafka/web/ui/models/cluster_info.rb +59 -0
- data/lib/karafka/web/ui/models/consumers_metrics.rb +46 -0
- data/lib/karafka/web/ui/models/{state.rb → consumers_state.rb} +6 -2
- data/lib/karafka/web/ui/models/health.rb +37 -7
- data/lib/karafka/web/ui/models/message.rb +123 -39
- data/lib/karafka/web/ui/models/metrics/aggregated.rb +196 -0
- data/lib/karafka/web/ui/models/metrics/charts/aggregated.rb +50 -0
- data/lib/karafka/web/ui/models/metrics/charts/topics.rb +109 -0
- data/lib/karafka/web/ui/models/metrics/topics.rb +101 -0
- data/lib/karafka/web/ui/models/partition.rb +27 -0
- data/lib/karafka/web/ui/models/process.rb +12 -1
- data/lib/karafka/web/ui/models/status.rb +110 -22
- data/lib/karafka/web/ui/models/visibility_filter.rb +33 -0
- data/lib/karafka/web/ui/pro/app.rb +87 -19
- data/lib/karafka/web/ui/pro/controllers/cluster.rb +11 -0
- data/lib/karafka/web/ui/pro/controllers/consumers.rb +13 -7
- data/lib/karafka/web/ui/pro/controllers/dashboard.rb +54 -0
- data/lib/karafka/web/ui/pro/controllers/dlq.rb +1 -2
- data/lib/karafka/web/ui/pro/controllers/errors.rb +46 -10
- data/lib/karafka/web/ui/pro/controllers/explorer.rb +145 -15
- data/lib/karafka/web/ui/pro/controllers/health.rb +10 -2
- data/lib/karafka/web/ui/pro/controllers/messages.rb +62 -0
- data/lib/karafka/web/ui/pro/controllers/routing.rb +44 -0
- data/lib/karafka/web/ui/pro/views/consumers/_breadcrumbs.erb +7 -1
- data/lib/karafka/web/ui/pro/views/consumers/_consumer.erb +1 -1
- data/lib/karafka/web/ui/pro/views/consumers/_counters.erb +7 -5
- data/lib/karafka/web/ui/pro/views/consumers/consumer/_job.erb +3 -3
- data/lib/karafka/web/ui/pro/views/consumers/consumer/_metrics.erb +5 -4
- data/lib/karafka/web/ui/pro/views/consumers/consumer/_partition.erb +13 -4
- data/lib/karafka/web/ui/pro/views/consumers/consumer/_subscription_group.erb +3 -2
- data/lib/karafka/web/ui/pro/views/consumers/consumer/_tabs.erb +7 -0
- data/lib/karafka/web/ui/pro/views/consumers/details.erb +21 -0
- data/lib/karafka/web/ui/pro/views/consumers/index.erb +4 -2
- data/lib/karafka/web/ui/pro/views/dashboard/_ranges_selector.erb +39 -0
- data/lib/karafka/web/ui/pro/views/dashboard/index.erb +82 -0
- data/lib/karafka/web/ui/pro/views/dlq/_topic.erb +1 -1
- data/lib/karafka/web/ui/pro/views/errors/_breadcrumbs.erb +8 -6
- data/lib/karafka/web/ui/pro/views/errors/_error.erb +2 -2
- data/lib/karafka/web/ui/pro/views/errors/_partition_option.erb +1 -1
- data/lib/karafka/web/ui/pro/views/errors/_table.erb +21 -0
- data/lib/karafka/web/ui/pro/views/errors/_title_with_select.erb +31 -0
- data/lib/karafka/web/ui/pro/views/errors/index.erb +9 -56
- data/lib/karafka/web/ui/pro/views/errors/partition.erb +17 -0
- data/lib/karafka/web/ui/pro/views/errors/show.erb +1 -1
- data/lib/karafka/web/ui/pro/views/explorer/_breadcrumbs.erb +6 -4
- data/lib/karafka/web/ui/pro/views/explorer/_filtered.erb +16 -0
- data/lib/karafka/web/ui/pro/views/explorer/_message.erb +14 -4
- data/lib/karafka/web/ui/pro/views/explorer/_no_topics.erb +7 -0
- data/lib/karafka/web/ui/pro/views/explorer/_partition_option.erb +3 -3
- data/lib/karafka/web/ui/pro/views/explorer/_topic.erb +1 -1
- data/lib/karafka/web/ui/pro/views/explorer/index.erb +12 -8
- data/lib/karafka/web/ui/pro/views/explorer/messages/_headers.erb +15 -0
- data/lib/karafka/web/ui/pro/views/explorer/messages/_key.erb +12 -0
- data/lib/karafka/web/ui/pro/views/explorer/partition/_details.erb +35 -0
- data/lib/karafka/web/ui/pro/views/explorer/partition/_messages.erb +1 -0
- data/lib/karafka/web/ui/pro/views/explorer/partition.erb +6 -4
- data/lib/karafka/web/ui/pro/views/explorer/show.erb +48 -5
- data/lib/karafka/web/ui/pro/views/explorer/topic/_details.erb +23 -0
- data/lib/karafka/web/ui/pro/views/explorer/topic/_empty.erb +3 -0
- data/lib/karafka/web/ui/pro/views/explorer/topic/_limited.erb +4 -0
- data/lib/karafka/web/ui/pro/views/explorer/topic.erb +51 -0
- data/lib/karafka/web/ui/pro/views/health/_breadcrumbs.erb +16 -0
- data/lib/karafka/web/ui/pro/views/health/_no_data.erb +9 -0
- data/lib/karafka/web/ui/pro/views/health/_partition.erb +17 -15
- data/lib/karafka/web/ui/pro/views/health/_partition_offset.erb +40 -0
- data/lib/karafka/web/ui/pro/views/health/_tabs.erb +27 -0
- data/lib/karafka/web/ui/pro/views/health/offsets.erb +71 -0
- data/lib/karafka/web/ui/pro/views/health/overview.erb +68 -0
- data/lib/karafka/web/ui/pro/views/jobs/_job.erb +6 -3
- data/lib/karafka/web/ui/pro/views/jobs/index.erb +4 -1
- data/lib/karafka/web/ui/pro/views/routing/_consumer_group.erb +37 -0
- data/lib/karafka/web/ui/pro/views/routing/_detail.erb +25 -0
- data/lib/karafka/web/ui/pro/views/routing/_topic.erb +23 -0
- data/lib/karafka/web/ui/pro/views/routing/index.erb +10 -0
- data/lib/karafka/web/ui/pro/views/routing/show.erb +26 -0
- data/lib/karafka/web/ui/pro/views/shared/_navigation.erb +7 -10
- data/lib/karafka/web/ui/public/images/logo-gray.svg +28 -0
- data/lib/karafka/web/ui/public/javascripts/application.js +30 -0
- data/lib/karafka/web/ui/public/javascripts/chart.min.js +14 -0
- data/lib/karafka/web/ui/public/javascripts/charts.js +330 -0
- data/lib/karafka/web/ui/public/javascripts/datepicker.js +6 -0
- data/lib/karafka/web/ui/public/javascripts/live_poll.js +39 -12
- data/lib/karafka/web/ui/public/javascripts/offset_datetime.js +74 -0
- data/lib/karafka/web/ui/public/javascripts/tabs.js +59 -0
- data/lib/karafka/web/ui/public/stylesheets/application.css +11 -0
- data/lib/karafka/web/ui/public/stylesheets/datepicker.min.css +12 -0
- data/lib/karafka/web/ui/views/cluster/_no_partitions.erb +3 -0
- data/lib/karafka/web/ui/views/cluster/_partition.erb +20 -22
- data/lib/karafka/web/ui/views/cluster/index.erb +6 -1
- data/lib/karafka/web/ui/views/consumers/_consumer.erb +1 -1
- data/lib/karafka/web/ui/views/consumers/_counters.erb +6 -4
- data/lib/karafka/web/ui/views/consumers/_summary.erb +3 -3
- data/lib/karafka/web/ui/views/consumers/index.erb +3 -1
- data/lib/karafka/web/ui/views/dashboard/_feature_pro.erb +3 -0
- data/lib/karafka/web/ui/views/dashboard/_not_enough_data.erb +15 -0
- data/lib/karafka/web/ui/views/dashboard/_ranges_selector.erb +23 -0
- data/lib/karafka/web/ui/views/dashboard/index.erb +95 -0
- data/lib/karafka/web/ui/views/errors/_detail.erb +12 -0
- data/lib/karafka/web/ui/views/errors/_error.erb +2 -2
- data/lib/karafka/web/ui/views/errors/show.erb +1 -1
- data/lib/karafka/web/ui/views/jobs/index.erb +3 -1
- data/lib/karafka/web/ui/views/layout.erb +10 -3
- data/lib/karafka/web/ui/views/routing/_consumer_group.erb +8 -6
- data/lib/karafka/web/ui/views/routing/_detail.erb +2 -2
- data/lib/karafka/web/ui/views/routing/_topic.erb +1 -1
- data/lib/karafka/web/ui/views/routing/show.erb +1 -1
- data/lib/karafka/web/ui/views/shared/_brand.erb +2 -2
- data/lib/karafka/web/ui/views/shared/_chart.erb +14 -0
- data/lib/karafka/web/ui/views/shared/_content.erb +2 -2
- data/lib/karafka/web/ui/views/shared/_feature_pro.erb +1 -1
- data/lib/karafka/web/ui/views/shared/_flashes.erb +9 -0
- data/lib/karafka/web/ui/views/shared/_footer.erb +22 -0
- data/lib/karafka/web/ui/views/shared/_header.erb +15 -9
- data/lib/karafka/web/ui/views/shared/_live_poll.erb +7 -0
- data/lib/karafka/web/ui/views/shared/_navigation.erb +5 -8
- data/lib/karafka/web/ui/views/shared/_no_paginated_data.erb +9 -0
- data/lib/karafka/web/ui/views/shared/_pagination.erb +17 -13
- data/lib/karafka/web/ui/views/shared/_tab_nav.erb +7 -0
- data/lib/karafka/web/ui/views/shared/exceptions/not_found.erb +34 -32
- data/lib/karafka/web/ui/views/shared/exceptions/pro_only.erb +45 -43
- data/lib/karafka/web/ui/views/status/failures/_consumers_reports_schema_state.erb +15 -0
- data/lib/karafka/web/ui/views/status/failures/_enabled.erb +8 -0
- data/lib/karafka/web/ui/views/status/failures/_initial_consumers_metrics.erb +11 -0
- data/lib/karafka/web/ui/views/status/failures/{_initial_state.erb → _initial_consumers_state.erb} +3 -3
- data/lib/karafka/web/ui/views/status/failures/_partitions.erb +14 -6
- data/lib/karafka/web/ui/views/status/info/_components.erb +21 -1
- data/lib/karafka/web/ui/views/status/show.erb +62 -5
- data/lib/karafka/web/ui/views/status/successes/_enabled.erb +1 -0
- data/lib/karafka/web/ui/views/status/warnings/_replication.erb +19 -0
- data/lib/karafka/web/version.rb +1 -1
- data/lib/karafka/web.rb +11 -0
- data.tar.gz.sig +0 -0
- metadata +124 -39
- metadata.gz.sig +0 -0
- data/lib/karafka/web/processing/consumers/aggregator.rb +0 -130
- data/lib/karafka/web/tracking/contracts/base.rb +0 -34
- data/lib/karafka/web/ui/lib/paginate_array.rb +0 -38
- data/lib/karafka/web/ui/pro/views/explorer/_encryption_enabled.erb +0 -18
- data/lib/karafka/web/ui/pro/views/explorer/partition/_watermark_offsets.erb +0 -10
- data/lib/karafka/web/ui/pro/views/health/index.erb +0 -60
- /data/lib/karafka/web/ui/pro/views/explorer/{_detail.erb → messages/_detail.erb} +0 -0
@@ -6,6 +6,33 @@ module Karafka
|
|
6
6
|
module Models
|
7
7
|
# Single topic partition data representation model
|
8
8
|
class Partition < Lib::HashProxy
|
9
|
+
# @return [Symbol] one of three states in which LSO can be in the correlation to given
|
10
|
+
# partition in the context of a consumer group.
|
11
|
+
#
|
12
|
+
# @note States descriptions:
|
13
|
+
# - `:active` all good. No hanging transactions, processing is ok
|
14
|
+
# - `:at_risk` - there may be hanging transactions but they do not affect processing
|
15
|
+
# before being stuck. This means, that the transaction still may be finished
|
16
|
+
# without affecting the processing, hence not having any impact.
|
17
|
+
# - `:stopped` - we have reached a hanging LSO and we cannot move forward despite more
|
18
|
+
# data being available. Unless the hanging transaction is killed or it finishes,
|
19
|
+
# we will not move forward.
|
20
|
+
def lso_risk_state
|
21
|
+
# If last stable is falling behind the high watermark
|
22
|
+
if ls_offset < hi_offset
|
23
|
+
# But it is changing and moving fast enough, it does not mean it is stuck
|
24
|
+
return :active if ls_offset_fd < ::Karafka::Web.config.ui.lso_threshold
|
25
|
+
|
26
|
+
# If it is stuck but we still have work to do, this is not a tragic situation because
|
27
|
+
# maybe it will unstuck before we reach it
|
28
|
+
return :at_risk if (committed_offset || 0) < ls_offset
|
29
|
+
|
30
|
+
# If it is not changing and falling behind high, it is stuck
|
31
|
+
:stopped
|
32
|
+
else
|
33
|
+
:active
|
34
|
+
end
|
35
|
+
end
|
9
36
|
end
|
10
37
|
end
|
11
38
|
end
|
@@ -40,7 +40,7 @@ module Karafka
|
|
40
40
|
.sort_by(&:started_at)
|
41
41
|
end
|
42
42
|
|
43
|
-
# @return [Integer] collective lag on this process
|
43
|
+
# @return [Integer] collective stored lag on this process
|
44
44
|
def lag_stored
|
45
45
|
consumer_groups
|
46
46
|
.flat_map(&:subscription_groups)
|
@@ -51,6 +51,17 @@ module Karafka
|
|
51
51
|
.sum
|
52
52
|
end
|
53
53
|
|
54
|
+
# @return [Integer] collective lag on this process
|
55
|
+
def lag
|
56
|
+
consumer_groups
|
57
|
+
.flat_map(&:subscription_groups)
|
58
|
+
.flat_map(&:topics)
|
59
|
+
.flat_map(&:partitions)
|
60
|
+
.map(&:lag)
|
61
|
+
.delete_if(&:negative?)
|
62
|
+
.sum
|
63
|
+
end
|
64
|
+
|
54
65
|
# @return [Integer] number of partitions to which we are currently subscribed
|
55
66
|
def subscribed_partitions_count
|
56
67
|
consumer_groups
|
@@ -25,6 +25,7 @@ module Karafka
|
|
25
25
|
when :success then 'successes'
|
26
26
|
when :warning then 'warnings'
|
27
27
|
when :failure then 'failures'
|
28
|
+
when :halted then 'failures'
|
28
29
|
else
|
29
30
|
raise ::Karafka::Errors::UnsupportedCaseError, status
|
30
31
|
end
|
@@ -36,22 +37,40 @@ module Karafka
|
|
36
37
|
end
|
37
38
|
end
|
38
39
|
|
39
|
-
#
|
40
|
-
|
41
|
-
|
40
|
+
# Is karafka-web enabled in the `karafka.rb`
|
41
|
+
# Checks if the consumer group for web-ui is injected.
|
42
|
+
# It does **not** check if the group is active because this may depend on the
|
43
|
+
# configuration details, but for the Web-UI web app to work, the routing needs to be
|
44
|
+
# aware of the deserializer, etc
|
45
|
+
def enabled
|
46
|
+
enabled = ::Karafka::App.routes.map(&:name).include?(
|
47
|
+
::Karafka::Web.config.processing.consumer_group
|
48
|
+
)
|
49
|
+
|
50
|
+
Step.new(
|
51
|
+
enabled ? :success : :failure,
|
52
|
+
nil
|
53
|
+
)
|
42
54
|
end
|
43
55
|
|
44
56
|
# @return [Status::Step] were we able to connect to Kafka or not and how fast.
|
45
57
|
# Some people try to work with Kafka over the internet with really high latency and this
|
46
58
|
# should be highlighted in the UI as often the connection just becomes unstable
|
47
59
|
def connection
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
60
|
+
if enabled.success?
|
61
|
+
# Do not connect more than once during the status object lifetime
|
62
|
+
@connection_time || connect
|
63
|
+
|
64
|
+
level = if @connection_time < 1_000
|
65
|
+
:success
|
66
|
+
elsif @connection_time < 1_000_000
|
67
|
+
:warning
|
68
|
+
else
|
69
|
+
:failure
|
70
|
+
end
|
71
|
+
else
|
72
|
+
level = :halted
|
73
|
+
end
|
55
74
|
|
56
75
|
Step.new(
|
57
76
|
level,
|
@@ -81,6 +100,7 @@ module Karafka
|
|
81
100
|
status = :success
|
82
101
|
status = :failure if topics_details[topics_consumers_states][:partitions] != 1
|
83
102
|
status = :failure if topics_details[topics_consumers_reports][:partitions] != 1
|
103
|
+
status = :failure if topics_details[topics_consumers_metrics][:partitions] != 1
|
84
104
|
details = topics_details
|
85
105
|
else
|
86
106
|
status = :halted
|
@@ -93,10 +113,31 @@ module Karafka
|
|
93
113
|
)
|
94
114
|
end
|
95
115
|
|
96
|
-
# @return [Status::Step]
|
97
|
-
def
|
116
|
+
# @return [Status::Step] do we have correct replication for given env
|
117
|
+
def replication
|
98
118
|
if partitions.success?
|
99
|
-
|
119
|
+
status = :success
|
120
|
+
# low replication is not an error but just a warning and a potential problem
|
121
|
+
# in case of a crash, this is why we do not fail but warn only
|
122
|
+
status = :warning if topics_details.values.any? { |det| det[:replication] < 2 }
|
123
|
+
# Allow for non-production setups to use replication 1 as it is not that relevant
|
124
|
+
status = :success unless Karafka.env.production?
|
125
|
+
details = topics_details
|
126
|
+
else
|
127
|
+
status = :halted
|
128
|
+
details = {}
|
129
|
+
end
|
130
|
+
|
131
|
+
Step.new(
|
132
|
+
status,
|
133
|
+
details
|
134
|
+
)
|
135
|
+
end
|
136
|
+
|
137
|
+
# @return [Status::Step] Is the initial consumers state present in Kafka
|
138
|
+
def initial_consumers_state
|
139
|
+
if replication.success?
|
140
|
+
@current_state ||= Models::ConsumersState.current
|
100
141
|
status = @current_state ? :success : :failure
|
101
142
|
else
|
102
143
|
status = :halted
|
@@ -108,10 +149,25 @@ module Karafka
|
|
108
149
|
)
|
109
150
|
end
|
110
151
|
|
152
|
+
# @return [Status::Step] Is the initial consumers metrics record present in Kafka
|
153
|
+
def initial_consumers_metrics
|
154
|
+
if initial_consumers_state.success?
|
155
|
+
@current_metrics ||= Models::ConsumersMetrics.current
|
156
|
+
status = @current_metrics ? :success : :failure
|
157
|
+
else
|
158
|
+
status = :halted
|
159
|
+
end
|
160
|
+
|
161
|
+
Step.new(
|
162
|
+
status,
|
163
|
+
nil
|
164
|
+
)
|
165
|
+
end
|
166
|
+
|
111
167
|
# @return [Status::Step] Is there at least one active karafka server reporting to the
|
112
168
|
# Web UI
|
113
169
|
def live_reporting
|
114
|
-
if
|
170
|
+
if initial_consumers_metrics.success?
|
115
171
|
@processes ||= Models::Processes.active(@current_state)
|
116
172
|
status = @processes.empty? ? :failure : :success
|
117
173
|
else
|
@@ -128,7 +184,11 @@ module Karafka
|
|
128
184
|
# consumed actively.
|
129
185
|
def state_calculation
|
130
186
|
if live_reporting.success?
|
131
|
-
@subscriptions ||= Models::Health
|
187
|
+
@subscriptions ||= Models::Health
|
188
|
+
.current(@current_state)
|
189
|
+
.values.map { |consumer_group| consumer_group[:topics] }
|
190
|
+
.flat_map(&:keys)
|
191
|
+
|
132
192
|
status = @subscriptions.include?(topics_consumers_reports) ? :success : :failure
|
133
193
|
else
|
134
194
|
status = :halted
|
@@ -140,11 +200,26 @@ module Karafka
|
|
140
200
|
)
|
141
201
|
end
|
142
202
|
|
203
|
+
# @return [Status::Step] Are we able to actually digest the consumers reports with the
|
204
|
+
# consumer that is consuming them.
|
205
|
+
def consumers_reports_schema_state
|
206
|
+
status = if state_calculation.success?
|
207
|
+
@current_state[:schema_state] == 'compatible' ? :success : :failure
|
208
|
+
else
|
209
|
+
:halted
|
210
|
+
end
|
211
|
+
|
212
|
+
Step.new(
|
213
|
+
status,
|
214
|
+
nil
|
215
|
+
)
|
216
|
+
end
|
217
|
+
|
143
218
|
# @return [Status::Step] is Pro enabled with all of its features.
|
144
219
|
# @note It's not an error not to have it but we want to warn, that some of the features
|
145
220
|
# may not work without Pro.
|
146
221
|
def pro_subscription
|
147
|
-
status = if
|
222
|
+
status = if consumers_reports_schema_state.success?
|
148
223
|
::Karafka.pro? ? :success : :warning
|
149
224
|
else
|
150
225
|
:halted
|
@@ -168,6 +243,11 @@ module Karafka
|
|
168
243
|
::Karafka::Web.config.topics.consumers.reports.to_s
|
169
244
|
end
|
170
245
|
|
246
|
+
# @return [String] consumers metrics topic name
|
247
|
+
def topics_consumers_metrics
|
248
|
+
::Karafka::Web.config.topics.consumers.metrics.to_s
|
249
|
+
end
|
250
|
+
|
171
251
|
# @return [String] errors topic name
|
172
252
|
def topics_errors
|
173
253
|
::Karafka::Web.config.topics.errors
|
@@ -175,10 +255,13 @@ module Karafka
|
|
175
255
|
|
176
256
|
# @return [Hash] hash with topics with which we work details (even if don't exist)
|
177
257
|
def topics_details
|
258
|
+
base = { present: false, partitions: 0, replication: 1 }
|
259
|
+
|
178
260
|
topics = {
|
179
|
-
topics_consumers_states =>
|
180
|
-
topics_consumers_reports =>
|
181
|
-
|
261
|
+
topics_consumers_states => base.dup,
|
262
|
+
topics_consumers_reports => base.dup,
|
263
|
+
topics_consumers_metrics => base.dup,
|
264
|
+
topics_errors => base.dup
|
182
265
|
}
|
183
266
|
|
184
267
|
@cluster_info.topics.each do |topic|
|
@@ -186,8 +269,11 @@ module Karafka
|
|
186
269
|
|
187
270
|
next unless topics.key?(name)
|
188
271
|
|
189
|
-
topics[name]
|
190
|
-
|
272
|
+
topics[name].merge!(
|
273
|
+
present: true,
|
274
|
+
partitions: topic[:partition_count],
|
275
|
+
replication: topic[:partitions].map { |part| part[:replica_count] }.max
|
276
|
+
)
|
191
277
|
end
|
192
278
|
|
193
279
|
topics
|
@@ -197,7 +283,9 @@ module Karafka
|
|
197
283
|
# @note If fails, `connection_time` will be 1_000_000
|
198
284
|
def connect
|
199
285
|
started = Time.now.to_f
|
200
|
-
|
286
|
+
# For status we always need uncached data, otherwise status could cache outdated
|
287
|
+
# info
|
288
|
+
@cluster_info = Models::ClusterInfo.fetch(cached: false)
|
201
289
|
@connection_time = (Time.now.to_f - started) * 1_000
|
202
290
|
rescue ::Rdkafka::RdkafkaError
|
203
291
|
@connection_time = 1_000_000
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Karafka
|
4
|
+
module Web
|
5
|
+
module Ui
|
6
|
+
module Models
|
7
|
+
# Allows for a granular control over what parts of messages are being displayed
|
8
|
+
# There are scenarios where payload or other parts of messages should not be presented
|
9
|
+
# because they may contain sensitive data. This API allows to manage that on a per message
|
10
|
+
# basis.
|
11
|
+
class VisibilityFilter
|
12
|
+
# @param _message [::Karafka::Messages::Message]
|
13
|
+
# @return [Boolean] should message key be visible
|
14
|
+
def key?(_message)
|
15
|
+
true
|
16
|
+
end
|
17
|
+
|
18
|
+
# @param _message [::Karafka::Messages::Message]
|
19
|
+
# @return [Boolean] should message headers be visible
|
20
|
+
def headers?(_message)
|
21
|
+
true
|
22
|
+
end
|
23
|
+
|
24
|
+
# @param message [::Karafka::Messages::Message]
|
25
|
+
# @return [Boolean] should message payload be visible
|
26
|
+
def payload?(message)
|
27
|
+
!message.headers.key?('encryption')
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -32,90 +32,158 @@ module Karafka
|
|
32
32
|
]
|
33
33
|
|
34
34
|
route do |r|
|
35
|
-
r.root { r.redirect root_path('
|
35
|
+
r.root { r.redirect root_path('dashboard') }
|
36
36
|
|
37
|
-
|
37
|
+
# Serve current version specific assets to prevent users from fetching old assets
|
38
|
+
# after upgrade
|
39
|
+
r.on(:assets, Karafka::Web::VERSION) do
|
40
|
+
r.public
|
41
|
+
end
|
42
|
+
|
43
|
+
r.get 'dashboard' do
|
44
|
+
@breadcrumbs = false
|
45
|
+
controller = Controllers::Dashboard.new(params)
|
46
|
+
controller.index
|
47
|
+
end
|
38
48
|
|
39
49
|
r.on 'consumers' do
|
40
50
|
controller = Controllers::Consumers.new(params)
|
41
51
|
|
42
52
|
r.get String, 'jobs' do |process_id|
|
43
|
-
|
53
|
+
controller.jobs(process_id)
|
44
54
|
end
|
45
55
|
|
46
56
|
r.get String, 'subscriptions' do |process_id|
|
47
|
-
|
57
|
+
controller.subscriptions(process_id)
|
58
|
+
end
|
59
|
+
|
60
|
+
r.get String, 'details' do |process_id|
|
61
|
+
controller.details(process_id)
|
48
62
|
end
|
49
63
|
|
50
64
|
r.get do
|
51
65
|
@breadcrumbs = false
|
52
|
-
|
66
|
+
controller.index
|
53
67
|
end
|
54
68
|
end
|
55
69
|
|
56
70
|
r.get 'jobs' do
|
57
71
|
controller = Controllers::Jobs.new(params)
|
58
|
-
|
72
|
+
controller.index
|
59
73
|
end
|
60
74
|
|
61
75
|
r.on 'routing' do
|
62
76
|
controller = Controllers::Routing.new(params)
|
63
77
|
|
64
78
|
r.get String do |topic_id|
|
65
|
-
|
79
|
+
controller.show(topic_id)
|
66
80
|
end
|
67
81
|
|
68
82
|
r.get do
|
69
|
-
|
83
|
+
controller.index
|
70
84
|
end
|
71
85
|
end
|
72
86
|
|
73
87
|
r.on 'explorer' do
|
74
88
|
controller = Controllers::Explorer.new(params)
|
75
89
|
|
90
|
+
r.get String, Integer, 'recent' do |topic_id, partition_id|
|
91
|
+
controller.recent(topic_id, partition_id)
|
92
|
+
end
|
93
|
+
|
94
|
+
r.get String, Integer, Integer, 'surrounding' do |topic_id, partition_id, offset|
|
95
|
+
controller.surrounding(topic_id, partition_id, offset)
|
96
|
+
end
|
97
|
+
|
98
|
+
r.get String, 'recent' do |topic_id|
|
99
|
+
controller.recent(topic_id, nil)
|
100
|
+
end
|
101
|
+
|
102
|
+
# Jumps to offset matching the expected time
|
103
|
+
r.get String, Integer, Time do |topic_id, partition_id, time|
|
104
|
+
controller.closest(topic_id, partition_id, time)
|
105
|
+
end
|
106
|
+
|
76
107
|
r.get String, Integer, Integer do |topic_id, partition_id, offset|
|
77
|
-
|
108
|
+
# If when viewing given message we get an offset of different message, we should
|
109
|
+
# redirect there. This allows us to support pagination with the current engine
|
110
|
+
if params.current_offset != -1
|
111
|
+
r.redirect explorer_path(topic_id, partition_id, params.current_offset)
|
112
|
+
else
|
113
|
+
controller.show(topic_id, partition_id, offset)
|
114
|
+
end
|
78
115
|
end
|
79
116
|
|
80
117
|
r.get String, Integer do |topic_id, partition_id|
|
81
|
-
|
118
|
+
controller.partition(topic_id, partition_id)
|
119
|
+
end
|
120
|
+
|
121
|
+
r.get String do |topic_id|
|
122
|
+
controller.topic(topic_id)
|
82
123
|
end
|
83
124
|
|
84
125
|
r.get do
|
85
|
-
|
126
|
+
controller.index
|
86
127
|
end
|
87
128
|
end
|
88
129
|
|
89
|
-
r.
|
130
|
+
r.on 'messages' do
|
131
|
+
controller = Controllers::Messages.new(params)
|
132
|
+
|
133
|
+
r.post String, Integer, Integer, 'republish' do |topic_id, partition_id, offset|
|
134
|
+
controller.republish(topic_id, partition_id, offset)
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
r.on 'health' do
|
90
139
|
controller = Controllers::Health.new(params)
|
91
|
-
|
140
|
+
|
141
|
+
r.get 'offsets' do
|
142
|
+
controller.offsets
|
143
|
+
end
|
144
|
+
|
145
|
+
r.get 'overview' do
|
146
|
+
controller.overview
|
147
|
+
end
|
148
|
+
|
149
|
+
r.get do
|
150
|
+
r.redirect root_path('health/overview')
|
151
|
+
end
|
92
152
|
end
|
93
153
|
|
94
154
|
r.get 'cluster' do
|
95
155
|
controller = Controllers::Cluster.new(params)
|
96
|
-
|
156
|
+
controller.index
|
97
157
|
end
|
98
158
|
|
99
159
|
r.on 'errors' do
|
100
160
|
controller = Controllers::Errors.new(params)
|
101
161
|
|
162
|
+
r.get Integer, Integer do |partition_id, offset|
|
163
|
+
if params.current_offset != -1
|
164
|
+
r.redirect root_path('errors', partition_id, params.current_offset)
|
165
|
+
else
|
166
|
+
controller.show(partition_id, offset)
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
102
170
|
r.get Integer do |partition_id|
|
103
|
-
|
171
|
+
controller.partition(partition_id)
|
104
172
|
end
|
105
173
|
|
106
|
-
r.get
|
107
|
-
|
174
|
+
r.get do
|
175
|
+
controller.index
|
108
176
|
end
|
109
177
|
end
|
110
178
|
|
111
179
|
r.get 'dlq' do
|
112
180
|
controller = Controllers::Dlq.new(params)
|
113
|
-
|
181
|
+
controller.index
|
114
182
|
end
|
115
183
|
|
116
184
|
r.get 'status' do
|
117
185
|
controller = Controllers::Status.new(params)
|
118
|
-
|
186
|
+
controller.show
|
119
187
|
end
|
120
188
|
end
|
121
189
|
end
|
@@ -1,5 +1,16 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
# This Karafka component is a Pro component under a commercial license.
|
4
|
+
# This Karafka component is NOT licensed under LGPL.
|
5
|
+
#
|
6
|
+
# All of the commercial components are present in the lib/karafka/pro directory of this
|
7
|
+
# repository and their usage requires commercial license agreement.
|
8
|
+
#
|
9
|
+
# Karafka has also commercial-friendly license, commercial support and commercial components.
|
10
|
+
#
|
11
|
+
# By sending a pull request to the pro components, you are agreeing to transfer the copyright of
|
12
|
+
# your code to Maciej Mensfeld.
|
13
|
+
|
3
14
|
module Karafka
|
4
15
|
module Web
|
5
16
|
module Ui
|
@@ -20,29 +20,35 @@ module Karafka
|
|
20
20
|
class Consumers < Ui::Controllers::Base
|
21
21
|
# Consumers list
|
22
22
|
def index
|
23
|
-
@current_state = Models::
|
23
|
+
@current_state = Models::ConsumersState.current!
|
24
24
|
@counters = Models::Counters.new(@current_state)
|
25
|
-
@processes,
|
25
|
+
@processes, last_page = Lib::Paginations::Paginators::Arrays.call(
|
26
26
|
Models::Processes.active(@current_state),
|
27
27
|
@params.current_page
|
28
28
|
)
|
29
29
|
|
30
|
+
paginate(@params.current_page, !last_page)
|
31
|
+
|
30
32
|
respond
|
31
33
|
end
|
32
34
|
|
33
35
|
# @param process_id [String] id of the process we're interested in
|
34
|
-
def
|
35
|
-
current_state = Models::
|
36
|
+
def details(process_id)
|
37
|
+
current_state = Models::ConsumersState.current!
|
36
38
|
@process = Models::Process.find(current_state, process_id)
|
37
39
|
|
38
40
|
respond
|
39
41
|
end
|
40
42
|
|
41
43
|
# @param process_id [String] id of the process we're interested in
|
42
|
-
def
|
43
|
-
|
44
|
-
|
44
|
+
def jobs(process_id)
|
45
|
+
details(process_id)
|
46
|
+
respond
|
47
|
+
end
|
45
48
|
|
49
|
+
# @param process_id [String] id of the process we're interested in
|
50
|
+
def subscriptions(process_id)
|
51
|
+
details(process_id)
|
46
52
|
respond
|
47
53
|
end
|
48
54
|
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# This Karafka component is a Pro component under a commercial license.
|
4
|
+
# This Karafka component is NOT licensed under LGPL.
|
5
|
+
#
|
6
|
+
# All of the commercial components are present in the lib/karafka/pro directory of this
|
7
|
+
# repository and their usage requires commercial license agreement.
|
8
|
+
#
|
9
|
+
# Karafka has also commercial-friendly license, commercial support and commercial components.
|
10
|
+
#
|
11
|
+
# By sending a pull request to the pro components, you are agreeing to transfer the copyright of
|
12
|
+
# your code to Maciej Mensfeld.
|
13
|
+
|
14
|
+
module Karafka
|
15
|
+
module Web
|
16
|
+
module Ui
|
17
|
+
module Pro
|
18
|
+
module Controllers
|
19
|
+
# Main Karafka Pro Web-Ui dashboard controller
|
20
|
+
class Dashboard < Ui::Controllers::Base
|
21
|
+
# View with statistics dashboard details
|
22
|
+
def index
|
23
|
+
@current_state = Models::ConsumersState.current!
|
24
|
+
@counters = Models::Counters.new(@current_state)
|
25
|
+
|
26
|
+
current_metrics = Models::ConsumersMetrics.current!
|
27
|
+
|
28
|
+
# Build the charts data using the aggregated metrics
|
29
|
+
@aggregated = Models::Metrics::Aggregated.new(
|
30
|
+
current_metrics.to_h.fetch(:aggregated)
|
31
|
+
)
|
32
|
+
|
33
|
+
# Build the charts data about topics using the consumers groups metrics
|
34
|
+
@topics = Models::Metrics::Topics.new(
|
35
|
+
current_metrics.to_h.fetch(:consumer_groups)
|
36
|
+
)
|
37
|
+
|
38
|
+
# Load only historicals for the selected range
|
39
|
+
@aggregated_charts = Models::Metrics::Charts::Aggregated.new(
|
40
|
+
@aggregated, @params.current_range
|
41
|
+
)
|
42
|
+
|
43
|
+
@topics_charts = Models::Metrics::Charts::Topics.new(
|
44
|
+
@topics, @params.current_range
|
45
|
+
)
|
46
|
+
|
47
|
+
respond
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -28,8 +28,7 @@ module Karafka
|
|
28
28
|
.compact
|
29
29
|
.select(&:itself)
|
30
30
|
|
31
|
-
@dlq_topics =
|
32
|
-
.cluster_info
|
31
|
+
@dlq_topics = Models::ClusterInfo
|
33
32
|
.topics
|
34
33
|
.select { |topic| dlq_topic_names.include?(topic[:topic_name]) }
|
35
34
|
.sort_by { |topic| topic[:topic_name] }
|