karafka-web 0.7.10 → 0.8.0.rc1
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 +18 -5
- data/.ruby-version +1 -1
- data/CHANGELOG.md +63 -0
- data/Gemfile.lock +22 -22
- data/docker-compose.yml +3 -1
- data/karafka-web.gemspec +2 -2
- data/lib/karafka/web/config.rb +16 -3
- data/lib/karafka/web/contracts/config.rb +7 -2
- data/lib/karafka/web/errors.rb +12 -0
- data/lib/karafka/web/inflector.rb +33 -0
- data/lib/karafka/web/installer.rb +20 -11
- data/lib/karafka/web/management/actions/base.rb +36 -0
- data/lib/karafka/web/management/actions/clean_boot_file.rb +33 -0
- data/lib/karafka/web/management/actions/create_initial_states.rb +77 -0
- data/lib/karafka/web/management/actions/create_topics.rb +139 -0
- data/lib/karafka/web/management/actions/delete_topics.rb +30 -0
- data/lib/karafka/web/management/actions/enable.rb +117 -0
- data/lib/karafka/web/management/actions/extend_boot_file.rb +39 -0
- data/lib/karafka/web/management/actions/migrate_states_data.rb +18 -0
- data/lib/karafka/web/management/migrations/0_base.rb +58 -0
- data/lib/karafka/web/management/migrations/0_set_initial_consumers_metrics.rb +36 -0
- data/lib/karafka/web/management/migrations/0_set_initial_consumers_state.rb +43 -0
- data/lib/karafka/web/management/migrations/1699543515_fill_missing_received_and_sent_bytes_in_consumers_metrics.rb +26 -0
- data/lib/karafka/web/management/migrations/1699543515_fill_missing_received_and_sent_bytes_in_consumers_state.rb +23 -0
- data/lib/karafka/web/management/migrations/1700234522_introduce_waiting_in_consumers_metrics.rb +24 -0
- data/lib/karafka/web/management/migrations/1700234522_introduce_waiting_in_consumers_state.rb +20 -0
- data/lib/karafka/web/management/migrations/1700234522_remove_processing_from_consumers_metrics.rb +24 -0
- data/lib/karafka/web/management/migrations/1700234522_remove_processing_from_consumers_state.rb +20 -0
- data/lib/karafka/web/management/migrations/1704722380_split_listeners_into_active_and_paused_in_metrics.rb +36 -0
- data/lib/karafka/web/management/migrations/1704722380_split_listeners_into_active_and_paused_in_states.rb +32 -0
- data/lib/karafka/web/management/migrator.rb +117 -0
- data/lib/karafka/web/processing/consumer.rb +39 -38
- data/lib/karafka/web/processing/consumers/aggregators/metrics.rb +2 -3
- data/lib/karafka/web/processing/consumers/aggregators/state.rb +8 -3
- data/lib/karafka/web/processing/consumers/contracts/aggregated_stats.rb +5 -1
- data/lib/karafka/web/processing/publisher.rb +59 -0
- data/lib/karafka/web/tracking/consumers/contracts/job.rb +3 -2
- data/lib/karafka/web/tracking/consumers/contracts/partition.rb +1 -0
- data/lib/karafka/web/tracking/consumers/contracts/report.rb +6 -1
- data/lib/karafka/web/tracking/consumers/contracts/subscription_group.rb +10 -1
- data/lib/karafka/web/tracking/consumers/listeners/connections.rb +49 -0
- data/lib/karafka/web/tracking/consumers/listeners/pausing.rb +7 -4
- data/lib/karafka/web/tracking/consumers/listeners/processing.rb +78 -70
- data/lib/karafka/web/tracking/consumers/listeners/statistics.rb +40 -13
- data/lib/karafka/web/tracking/consumers/sampler.rb +82 -25
- data/lib/karafka/web/tracking/helpers/ttls/array.rb +72 -0
- data/lib/karafka/web/tracking/helpers/ttls/hash.rb +34 -0
- data/lib/karafka/web/tracking/helpers/ttls/stats.rb +49 -0
- data/lib/karafka/web/tracking/helpers/ttls/windows.rb +32 -0
- data/lib/karafka/web/tracking/reporter.rb +1 -0
- data/lib/karafka/web/ui/app.rb +22 -4
- data/lib/karafka/web/ui/base.rb +18 -2
- data/lib/karafka/web/ui/controllers/base.rb +34 -4
- data/lib/karafka/web/ui/controllers/become_pro.rb +1 -1
- data/lib/karafka/web/ui/controllers/cluster.rb +33 -9
- data/lib/karafka/web/ui/controllers/consumers.rb +8 -2
- data/lib/karafka/web/ui/controllers/dashboard.rb +2 -2
- data/lib/karafka/web/ui/controllers/errors.rb +2 -2
- data/lib/karafka/web/ui/controllers/jobs.rb +55 -5
- data/lib/karafka/web/ui/controllers/requests/params.rb +5 -0
- data/lib/karafka/web/ui/controllers/responses/deny.rb +15 -0
- data/lib/karafka/web/ui/controllers/responses/file.rb +23 -0
- data/lib/karafka/web/ui/controllers/responses/{data.rb → render.rb} +3 -3
- data/lib/karafka/web/ui/controllers/routing.rb +11 -2
- data/lib/karafka/web/ui/controllers/status.rb +1 -1
- data/lib/karafka/web/ui/helpers/application_helper.rb +70 -0
- data/lib/karafka/web/ui/lib/hash_proxy.rb +29 -14
- data/lib/karafka/web/ui/lib/sorter.rb +170 -0
- data/lib/karafka/web/ui/models/counters.rb +6 -0
- data/lib/karafka/web/ui/models/health.rb +23 -2
- data/lib/karafka/web/ui/models/jobs.rb +48 -0
- data/lib/karafka/web/ui/models/metrics/charts/aggregated.rb +33 -0
- data/lib/karafka/web/ui/models/metrics/charts/topics.rb +1 -1
- data/lib/karafka/web/ui/models/process.rb +2 -1
- data/lib/karafka/web/ui/models/status.rb +23 -7
- data/lib/karafka/web/ui/models/topic.rb +3 -1
- data/lib/karafka/web/ui/models/visibility_filter.rb +16 -0
- data/lib/karafka/web/ui/pro/app.rb +44 -6
- data/lib/karafka/web/ui/pro/controllers/cluster.rb +1 -0
- data/lib/karafka/web/ui/pro/controllers/consumers.rb +52 -6
- data/lib/karafka/web/ui/pro/controllers/dashboard.rb +1 -1
- data/lib/karafka/web/ui/pro/controllers/dlq.rb +1 -1
- data/lib/karafka/web/ui/pro/controllers/errors.rb +3 -3
- data/lib/karafka/web/ui/pro/controllers/explorer.rb +8 -8
- data/lib/karafka/web/ui/pro/controllers/health.rb +34 -2
- data/lib/karafka/web/ui/pro/controllers/jobs.rb +11 -0
- data/lib/karafka/web/ui/pro/controllers/messages.rb +42 -0
- data/lib/karafka/web/ui/pro/controllers/routing.rb +11 -2
- data/lib/karafka/web/ui/pro/views/consumers/_breadcrumbs.erb +8 -2
- data/lib/karafka/web/ui/pro/views/consumers/_consumer.erb +14 -8
- data/lib/karafka/web/ui/pro/views/consumers/_counters.erb +8 -6
- data/lib/karafka/web/ui/pro/views/consumers/consumer/_job.erb +4 -1
- data/lib/karafka/web/ui/pro/views/consumers/consumer/_no_jobs.erb +1 -1
- data/lib/karafka/web/ui/pro/views/consumers/consumer/_partition.erb +1 -3
- data/lib/karafka/web/ui/pro/views/consumers/consumer/_subscription_group.erb +28 -11
- data/lib/karafka/web/ui/pro/views/consumers/consumer/_tabs.erb +10 -3
- data/lib/karafka/web/ui/pro/views/consumers/index.erb +3 -3
- data/lib/karafka/web/ui/pro/views/consumers/pending_jobs.erb +43 -0
- data/lib/karafka/web/ui/pro/views/consumers/{jobs.erb → running_jobs.erb} +11 -10
- data/lib/karafka/web/ui/pro/views/dashboard/index.erb +7 -1
- data/lib/karafka/web/ui/pro/views/explorer/message/_message_actions.erb +18 -0
- data/lib/karafka/web/ui/pro/views/explorer/message/_metadata.erb +43 -0
- data/lib/karafka/web/ui/pro/views/explorer/message/_payload.erb +21 -0
- data/lib/karafka/web/ui/pro/views/explorer/message/_payload_actions.erb +19 -0
- data/lib/karafka/web/ui/pro/views/explorer/show.erb +9 -84
- data/lib/karafka/web/ui/pro/views/health/_breadcrumbs.erb +8 -0
- data/lib/karafka/web/ui/pro/views/health/_partition.erb +1 -3
- data/lib/karafka/web/ui/pro/views/health/_partition_offset.erb +4 -4
- data/lib/karafka/web/ui/pro/views/health/_partition_times.erb +32 -0
- data/lib/karafka/web/ui/pro/views/health/_tabs.erb +9 -0
- data/lib/karafka/web/ui/pro/views/health/changes.erb +66 -0
- data/lib/karafka/web/ui/pro/views/health/offsets.erb +14 -14
- data/lib/karafka/web/ui/pro/views/health/overview.erb +11 -11
- data/lib/karafka/web/ui/pro/views/jobs/_job.erb +1 -1
- data/lib/karafka/web/ui/pro/views/jobs/_no_jobs.erb +1 -1
- data/lib/karafka/web/ui/pro/views/jobs/pending.erb +39 -0
- data/lib/karafka/web/ui/pro/views/jobs/running.erb +39 -0
- data/lib/karafka/web/ui/pro/views/routing/_consumer_group.erb +2 -2
- data/lib/karafka/web/ui/pro/views/routing/_topic.erb +9 -0
- data/lib/karafka/web/ui/pro/views/routing/show.erb +12 -0
- data/lib/karafka/web/ui/pro/views/shared/_navigation.erb +1 -1
- data/lib/karafka/web/ui/public/javascripts/application.js +10 -0
- data/lib/karafka/web/ui/public/stylesheets/application.css +4 -0
- data/lib/karafka/web/ui/views/cluster/_breadcrumbs.erb +16 -0
- data/lib/karafka/web/ui/views/cluster/_tabs.erb +27 -0
- data/lib/karafka/web/ui/views/cluster/brokers.erb +27 -0
- data/lib/karafka/web/ui/views/cluster/topics.erb +35 -0
- data/lib/karafka/web/ui/views/consumers/_counters.erb +8 -6
- data/lib/karafka/web/ui/views/consumers/_summary.erb +2 -2
- data/lib/karafka/web/ui/views/consumers/index.erb +3 -3
- data/lib/karafka/web/ui/views/dashboard/_ranges_selector.erb +23 -7
- data/lib/karafka/web/ui/views/dashboard/index.erb +19 -8
- data/lib/karafka/web/ui/views/errors/show.erb +2 -23
- data/lib/karafka/web/ui/views/jobs/_breadcrumbs.erb +17 -1
- data/lib/karafka/web/ui/views/jobs/_job.erb +1 -1
- data/lib/karafka/web/ui/views/jobs/_no_jobs.erb +1 -1
- data/lib/karafka/web/ui/views/jobs/_tabs.erb +27 -0
- data/lib/karafka/web/ui/views/jobs/{index.erb → pending.erb} +9 -7
- data/lib/karafka/web/ui/{pro/views/jobs/index.erb → views/jobs/running.erb} +9 -11
- data/lib/karafka/web/ui/views/routing/_consumer_group.erb +14 -12
- data/lib/karafka/web/ui/views/shared/_navigation.erb +1 -1
- data/lib/karafka/web/ui/views/shared/_pagination.erb +1 -1
- data/lib/karafka/web/ui/views/shared/exceptions/not_allowed.erb +37 -0
- data/lib/karafka/web/ui/views/status/show.erb +17 -2
- data/lib/karafka/web/ui/views/status/warnings/_routing_topics_presence.erb +15 -0
- data/lib/karafka/web/version.rb +1 -1
- data/lib/karafka/web.rb +6 -2
- data.tar.gz.sig +0 -0
- metadata +61 -26
- metadata.gz.sig +0 -0
- data/lib/karafka/web/management/base.rb +0 -34
- data/lib/karafka/web/management/clean_boot_file.rb +0 -31
- data/lib/karafka/web/management/create_initial_states.rb +0 -101
- data/lib/karafka/web/management/create_topics.rb +0 -133
- data/lib/karafka/web/management/delete_topics.rb +0 -28
- data/lib/karafka/web/management/enable.rb +0 -102
- data/lib/karafka/web/management/extend_boot_file.rb +0 -37
- data/lib/karafka/web/tracking/ttl_array.rb +0 -59
- data/lib/karafka/web/tracking/ttl_hash.rb +0 -16
- data/lib/karafka/web/ui/pro/views/dashboard/_ranges_selector.erb +0 -39
- data/lib/karafka/web/ui/views/cluster/index.erb +0 -74
@@ -26,11 +26,11 @@ module Karafka
|
|
26
26
|
.topics
|
27
27
|
.sort_by { |topic| topic[:topic_name] }
|
28
28
|
|
29
|
-
unless ::Karafka::Web.config.ui.
|
29
|
+
unless ::Karafka::Web.config.ui.visibility.internal_topics
|
30
30
|
@topics.reject! { |topic| topic[:topic_name].start_with?('__') }
|
31
31
|
end
|
32
32
|
|
33
|
-
|
33
|
+
render
|
34
34
|
end
|
35
35
|
|
36
36
|
# Displays aggregated messages from (potentially) all partitions of a topic
|
@@ -45,7 +45,7 @@ module Karafka
|
|
45
45
|
# @note We cannot use offset references here because each of the partitions may have
|
46
46
|
# completely different values
|
47
47
|
def topic(topic_id)
|
48
|
-
@visibility_filter = ::Karafka::Web.config.ui.
|
48
|
+
@visibility_filter = ::Karafka::Web.config.ui.visibility.filter
|
49
49
|
|
50
50
|
@topic_id = topic_id
|
51
51
|
@partitions_count = Models::ClusterInfo.partitions_count(topic_id)
|
@@ -60,7 +60,7 @@ module Karafka
|
|
60
60
|
|
61
61
|
paginate(@params.current_page, next_page)
|
62
62
|
|
63
|
-
|
63
|
+
render
|
64
64
|
end
|
65
65
|
|
66
66
|
# Shows messages available in a given partition
|
@@ -68,7 +68,7 @@ module Karafka
|
|
68
68
|
# @param topic_id [String]
|
69
69
|
# @param partition_id [Integer]
|
70
70
|
def partition(topic_id, partition_id)
|
71
|
-
@visibility_filter = ::Karafka::Web.config.ui.
|
71
|
+
@visibility_filter = ::Karafka::Web.config.ui.visibility.filter
|
72
72
|
@topic_id = topic_id
|
73
73
|
@partition_id = partition_id
|
74
74
|
@watermark_offsets = Ui::Models::WatermarkOffsets.find(topic_id, partition_id)
|
@@ -84,7 +84,7 @@ module Karafka
|
|
84
84
|
@messages.map { |message| message.is_a?(Array) ? message.last : message.offset }
|
85
85
|
)
|
86
86
|
|
87
|
-
|
87
|
+
render
|
88
88
|
end
|
89
89
|
|
90
90
|
# Displays given message
|
@@ -94,7 +94,7 @@ module Karafka
|
|
94
94
|
# @param offset [Integer] offset of the message we want to display
|
95
95
|
# @param paginate [Boolean] do we want to have pagination
|
96
96
|
def show(topic_id, partition_id, offset, paginate: true)
|
97
|
-
@visibility_filter = ::Karafka::Web.config.ui.
|
97
|
+
@visibility_filter = ::Karafka::Web.config.ui.visibility.filter
|
98
98
|
@topic_id = topic_id
|
99
99
|
@partition_id = partition_id
|
100
100
|
@offset = offset
|
@@ -116,7 +116,7 @@ module Karafka
|
|
116
116
|
paginate(offset, watermark_offsets.low, watermark_offsets.high)
|
117
117
|
end
|
118
118
|
|
119
|
-
|
119
|
+
render
|
120
120
|
end
|
121
121
|
|
122
122
|
# Displays the most recent message on a topic/partition
|
@@ -18,12 +18,36 @@ module Karafka
|
|
18
18
|
module Controllers
|
19
19
|
# Health state controller
|
20
20
|
class Health < Ui::Controllers::Base
|
21
|
+
self.sortable_attributes = %w[
|
22
|
+
id
|
23
|
+
lag_stored
|
24
|
+
lag_stored_d
|
25
|
+
committed_offset
|
26
|
+
committed_offset_fd
|
27
|
+
stored_offset
|
28
|
+
stored_offset_fd
|
29
|
+
hi_offset
|
30
|
+
hi_offset_fd
|
31
|
+
ls_offset
|
32
|
+
ls_offset_fd
|
33
|
+
fetch_state
|
34
|
+
poll_state
|
35
|
+
lso_risk_state
|
36
|
+
name
|
37
|
+
poll_state_ch
|
38
|
+
].freeze
|
39
|
+
|
21
40
|
# Displays the current system state
|
22
41
|
def overview
|
23
42
|
current_state = Models::ConsumersState.current!
|
24
43
|
@stats = Models::Health.current(current_state)
|
25
44
|
|
26
|
-
|
45
|
+
# Refine only on a per topic basis not to resort higher levels
|
46
|
+
@stats.each_value do |cg_details|
|
47
|
+
cg_details.each_value { |topic_details| refine(topic_details) }
|
48
|
+
end
|
49
|
+
|
50
|
+
render
|
27
51
|
end
|
28
52
|
|
29
53
|
# Displays details about offsets and their progression/statuses
|
@@ -31,7 +55,15 @@ module Karafka
|
|
31
55
|
# Same data as overview but presented differently
|
32
56
|
overview
|
33
57
|
|
34
|
-
|
58
|
+
render
|
59
|
+
end
|
60
|
+
|
61
|
+
# Displays information related to time of changes of particular attributes
|
62
|
+
def changes
|
63
|
+
# Same data as overview but presented differently
|
64
|
+
overview
|
65
|
+
|
66
|
+
render
|
35
67
|
end
|
36
68
|
end
|
37
69
|
end
|
@@ -18,6 +18,17 @@ module Karafka
|
|
18
18
|
module Controllers
|
19
19
|
# Displays list of active jobs
|
20
20
|
class Jobs < Ui::Controllers::Jobs
|
21
|
+
self.sortable_attributes = %w[
|
22
|
+
name
|
23
|
+
topic
|
24
|
+
consumer
|
25
|
+
type
|
26
|
+
messages
|
27
|
+
first_offset
|
28
|
+
last_offset
|
29
|
+
committed_offset
|
30
|
+
updated_at
|
31
|
+
].freeze
|
21
32
|
end
|
22
33
|
end
|
23
34
|
end
|
@@ -42,6 +42,43 @@ module Karafka
|
|
42
42
|
)
|
43
43
|
end
|
44
44
|
|
45
|
+
# Dispatches the message raw payload to the browser as a file
|
46
|
+
#
|
47
|
+
# @param topic_id [String]
|
48
|
+
# @param partition_id [Integer]
|
49
|
+
# @param offset [Integer] offset of the message we want to download
|
50
|
+
def download(topic_id, partition_id, offset)
|
51
|
+
message = Ui::Models::Message.find(topic_id, partition_id, offset)
|
52
|
+
|
53
|
+
# Check if downloads are allowed
|
54
|
+
return deny unless visibility_filter.download?(message)
|
55
|
+
|
56
|
+
file(
|
57
|
+
message.raw_payload,
|
58
|
+
"#{topic_id}_#{partition_id}_#{offset}_payload.msg"
|
59
|
+
)
|
60
|
+
end
|
61
|
+
|
62
|
+
# Dispatches the message payload first deserialized and then serialized to JSON
|
63
|
+
# It differs from the raw payload in cases where raw payload is compressed or binary
|
64
|
+
# or contains data that the Web UI user should not see that was altered on the Web UI
|
65
|
+
# with the visibility filter.
|
66
|
+
#
|
67
|
+
# @param topic_id [String]
|
68
|
+
# @param partition_id [Integer]
|
69
|
+
# @param offset [Integer] offset of the message we want to export
|
70
|
+
def export(topic_id, partition_id, offset)
|
71
|
+
message = Ui::Models::Message.find(topic_id, partition_id, offset)
|
72
|
+
|
73
|
+
# Check if exports are allowed
|
74
|
+
return deny unless visibility_filter.export?(message)
|
75
|
+
|
76
|
+
file(
|
77
|
+
message.payload.to_json,
|
78
|
+
"#{topic_id}_#{partition_id}_#{offset}_payload.json"
|
79
|
+
)
|
80
|
+
end
|
81
|
+
|
45
82
|
private
|
46
83
|
|
47
84
|
# @param message [Karafka::Messages::Message]
|
@@ -54,6 +91,11 @@ module Karafka
|
|
54
91
|
and received offset #{delivery.offset}.
|
55
92
|
MSG
|
56
93
|
end
|
94
|
+
|
95
|
+
# @return [Object] visibility filter. Either default or user-based
|
96
|
+
def visibility_filter
|
97
|
+
::Karafka::Web.config.ui.visibility.filter
|
98
|
+
end
|
57
99
|
end
|
58
100
|
end
|
59
101
|
end
|
@@ -18,13 +18,22 @@ module Karafka
|
|
18
18
|
module Controllers
|
19
19
|
# Routing details - same as in OSS
|
20
20
|
class Routing < Ui::Controllers::Routing
|
21
|
+
self.sortable_attributes = %w[
|
22
|
+
name
|
23
|
+
active?
|
24
|
+
].freeze
|
25
|
+
|
21
26
|
# Routing list
|
22
27
|
def index
|
23
28
|
detect_patterns_routes
|
24
29
|
|
25
30
|
@routes = Karafka::App.routes
|
26
31
|
|
27
|
-
|
32
|
+
@routes.each do |consumer_group|
|
33
|
+
refine(consumer_group.topics)
|
34
|
+
end
|
35
|
+
|
36
|
+
render
|
28
37
|
end
|
29
38
|
|
30
39
|
# Given route details
|
@@ -37,7 +46,7 @@ module Karafka
|
|
37
46
|
|
38
47
|
@topic || raise(::Karafka::Web::Errors::Ui::NotFoundError, topic_id)
|
39
48
|
|
40
|
-
|
49
|
+
render
|
41
50
|
end
|
42
51
|
|
43
52
|
private
|
@@ -14,13 +14,19 @@
|
|
14
14
|
<% if current_path.include?('/jobs') %>
|
15
15
|
<li class="breadcrumb-item">
|
16
16
|
<a href="<%= root_path('consumers', @process.id, 'jobs') %>">
|
17
|
-
|
17
|
+
Jobs
|
18
|
+
</a>
|
19
|
+
</li>
|
20
|
+
|
21
|
+
<li class="breadcrumb-item">
|
22
|
+
<a href="<%= root_path('consumers', @process.id, 'jobs') %>">
|
23
|
+
Running
|
18
24
|
</a>
|
19
25
|
</li>
|
20
26
|
<% elsif current_path.include?('/subscriptions') %>
|
21
27
|
<li class="breadcrumb-item">
|
22
28
|
<a href="<%= root_path('consumers', @process.id, 'subscriptions') %>">
|
23
|
-
|
29
|
+
Subscriptions
|
24
30
|
</a>
|
25
31
|
</li>
|
26
32
|
<% else %>
|
@@ -10,18 +10,24 @@
|
|
10
10
|
|
11
11
|
<p class="mt-0 mb-1">
|
12
12
|
<% process.consumer_groups.each do |consumer_group| %>
|
13
|
+
<% sg_topics = Hash.new { |h, k| h[k] = [] } %>
|
14
|
+
|
13
15
|
<% consumer_group.subscription_groups.each do |subscription_group| %>
|
14
16
|
<% subscription_group.topics.each do |topic| %>
|
15
|
-
|
16
|
-
<%= topic.name %>:
|
17
|
-
<% if topic.partitions.size > 10 %>
|
18
|
-
<%= "#{topic.partitions.first(10).map(&:id).join(',')}..." %>
|
19
|
-
<% else %>
|
20
|
-
<%= topic.partitions.map(&:id).join(',') %>
|
21
|
-
<% end %>
|
22
|
-
</span>
|
17
|
+
<% sg_topics[topic.name] << topic.partitions.map(&:id) %>
|
23
18
|
<% end %>
|
24
19
|
<% end %>
|
20
|
+
|
21
|
+
<% sg_topics.each do |topic_name, partitions| %>
|
22
|
+
<span class="badge bg-secondary badge-topic" title="Consumer group: <%= consumer_group.id %>">
|
23
|
+
<%= topic_name %>:
|
24
|
+
<% if partitions.size > 10 %>
|
25
|
+
<%= "#{partitions.sort.first(10).join(',')}..." %>
|
26
|
+
<% else %>
|
27
|
+
<%= partitions.sort.join(',') %>
|
28
|
+
<% end %>
|
29
|
+
</span>
|
30
|
+
<% end %>
|
25
31
|
<% end %>
|
26
32
|
</p>
|
27
33
|
|
@@ -24,18 +24,20 @@
|
|
24
24
|
<div class="desc">Lag stored</div>
|
25
25
|
</li>
|
26
26
|
<li class="col-sm">
|
27
|
-
<a href="<%= root_path('jobs') %>">
|
27
|
+
<a href="<%= root_path('jobs/running') %>">
|
28
28
|
<div class="count mb-1">
|
29
29
|
<%= number_with_delimiter @counters.busy, ' ' %>
|
30
30
|
</div>
|
31
|
-
<div class="desc">
|
31
|
+
<div class="desc">Running</div>
|
32
32
|
</a>
|
33
33
|
</li>
|
34
34
|
<li class="col-sm">
|
35
|
-
<
|
36
|
-
|
37
|
-
|
38
|
-
|
35
|
+
<a href="<%= root_path('jobs/pending') %>">
|
36
|
+
<div class="count mb-1">
|
37
|
+
<%= number_with_delimiter @counters.pending, ' ' %>
|
38
|
+
</div>
|
39
|
+
<div class="desc">Pending</div>
|
40
|
+
</a>
|
39
41
|
</li>
|
40
42
|
<li class="col-sm">
|
41
43
|
<a href="<%= root_path('errors') %>">
|
@@ -16,6 +16,9 @@
|
|
16
16
|
<td>
|
17
17
|
<code>#<%= job.type %></code>
|
18
18
|
</td>
|
19
|
+
<td>
|
20
|
+
<%= job.messages %>
|
21
|
+
</td>
|
19
22
|
<td>
|
20
23
|
<%== offset_with_label job.topic, job.partition, job.first_offset, explore: true %>
|
21
24
|
</td>
|
@@ -26,6 +29,6 @@
|
|
26
29
|
<%== offset_with_label job.topic, job.partition, job.committed_offset, explore: true %>
|
27
30
|
</td>
|
28
31
|
<td>
|
29
|
-
<%== relative_time job.
|
32
|
+
<%== relative_time job.updated_at %>
|
30
33
|
</td>
|
31
34
|
</tr>
|
@@ -26,9 +26,7 @@
|
|
26
26
|
</span>
|
27
27
|
</td>
|
28
28
|
<td>
|
29
|
-
|
30
|
-
<%= partition.poll_state %>
|
31
|
-
</span>
|
29
|
+
<%== poll_state_with_change_time_label(partition.poll_state, partition.poll_state_ch) %>
|
32
30
|
</td>
|
33
31
|
<td>
|
34
32
|
<span class="badge bg-success <%= lso_risk_state_bg(partition) %> bg-opacity-100">
|
@@ -14,6 +14,7 @@
|
|
14
14
|
</span>
|
15
15
|
</div>
|
16
16
|
</div>
|
17
|
+
|
17
18
|
<div class="card">
|
18
19
|
<div class="card-body d-flex flex-column align-items-center justify-content-center p-2">
|
19
20
|
Join state:
|
@@ -22,30 +23,46 @@
|
|
22
23
|
</span>
|
23
24
|
</div>
|
24
25
|
</div>
|
26
|
+
|
25
27
|
<div class="card">
|
26
28
|
<div class="card-body d-flex flex-column align-items-center justify-content-center p-2">
|
27
29
|
State change:
|
28
30
|
<span class="badge bg-secondary mt-1 mb-1">
|
29
31
|
<%==
|
30
32
|
relative_time(
|
31
|
-
Time.at(@process.dispatched_at) - (subscription_group.stateage / 1_000)
|
33
|
+
Time.at(@process.dispatched_at) - (subscription_group.stateage / 1_000.0)
|
32
34
|
)
|
33
35
|
%>
|
34
36
|
</span>
|
35
37
|
</div>
|
36
38
|
</div>
|
39
|
+
|
40
|
+
<div class="card">
|
41
|
+
<div class="card-body d-flex flex-column align-items-center justify-content-center p-2">
|
42
|
+
Last Poll:
|
43
|
+
<span class="badge bg-secondary mt-1 mb-1">
|
44
|
+
<%==
|
45
|
+
relative_time(
|
46
|
+
Time.at(@process.dispatched_at) - (subscription_group.poll_age / 1_000.0)
|
47
|
+
)
|
48
|
+
%>
|
49
|
+
</span>
|
50
|
+
</div>
|
51
|
+
</div>
|
52
|
+
|
37
53
|
<div class="card">
|
38
54
|
<div class="card-body d-flex flex-column align-items-center justify-content-center p-2">
|
39
55
|
Last rebalance:
|
40
56
|
<span class="badge bg-secondary mt-1 mb-1">
|
41
57
|
<%==
|
42
58
|
relative_time(
|
43
|
-
Time.at(@process.dispatched_at) - (subscription_group.rebalance_age / 1_000)
|
59
|
+
Time.at(@process.dispatched_at) - (subscription_group.rebalance_age / 1_000.0)
|
44
60
|
)
|
45
61
|
%>
|
46
62
|
</span>
|
47
63
|
</div>
|
48
64
|
</div>
|
65
|
+
|
49
66
|
<div class="card">
|
50
67
|
<div class="card-body d-flex flex-column align-items-center justify-content-center p-2">
|
51
68
|
Rebalance count:
|
@@ -68,7 +85,7 @@
|
|
68
85
|
<div class="row mb-4">
|
69
86
|
<div class="col-lg-12">
|
70
87
|
<div class="alert alert-info" role="alert">
|
71
|
-
This process does not consume any messages from any topics of this
|
88
|
+
This process does not consume any messages from any topics of this subscription group.
|
72
89
|
</div>
|
73
90
|
</div>
|
74
91
|
</div>
|
@@ -86,14 +103,14 @@
|
|
86
103
|
</th>
|
87
104
|
<tr class="align-middle">
|
88
105
|
</tr>
|
89
|
-
<th
|
90
|
-
<th
|
91
|
-
<th
|
92
|
-
<th
|
93
|
-
<th
|
94
|
-
<th
|
95
|
-
<th
|
96
|
-
<th
|
106
|
+
<th><%== sort_link('Partition', :id) %></th>
|
107
|
+
<th><%== sort_link(:lag_stored) %></th>
|
108
|
+
<th><%== sort_link('Lag stored trend', :lag_stored_d) %></th>
|
109
|
+
<th><%== sort_link(:committed_offset) %></th>
|
110
|
+
<th><%== sort_link(:stored_offset) %></th>
|
111
|
+
<th><%== sort_link(:fetch_state) %></th>
|
112
|
+
<th><%== sort_link(:poll_state) %></th>
|
113
|
+
<th><%== sort_link('LSO state', :lso_risk_state) %></th>
|
97
114
|
</tr>
|
98
115
|
</thead>
|
99
116
|
<tbody>
|
@@ -5,15 +5,22 @@
|
|
5
5
|
<ul class="nav nav-tabs">
|
6
6
|
<li class="nav-item">
|
7
7
|
<a class="nav-link <%= nav_class(include: 'subscriptions') %>" href="<%= root_path('consumers', @process.id, 'subscriptions') %>">
|
8
|
-
|
8
|
+
Subscriptions
|
9
9
|
(<%= @process.subscribed_partitions_count %>)
|
10
10
|
</a>
|
11
11
|
</li>
|
12
12
|
|
13
13
|
<li class="nav-item">
|
14
|
-
<a class="nav-link <%= nav_class(include: 'jobs') %>" href="<%= root_path('consumers', @process.id, 'jobs') %>">
|
14
|
+
<a class="nav-link <%= nav_class(include: 'jobs/running') %>" href="<%= root_path('consumers', @process.id, 'jobs', 'running') %>">
|
15
15
|
Running jobs
|
16
|
-
(<%= @process.jobs.count %>)
|
16
|
+
(<%= @process.jobs.running.count %>)
|
17
|
+
</a>
|
18
|
+
</li>
|
19
|
+
|
20
|
+
<li class="nav-item">
|
21
|
+
<a class="nav-link <%= nav_class(include: 'jobs/pending') %>" href="<%= root_path('consumers', @process.id, 'jobs', 'pending') %>">
|
22
|
+
Pending jobs
|
23
|
+
(<%= @process.jobs.pending.count %>)
|
17
24
|
</a>
|
18
25
|
</li>
|
19
26
|
|
@@ -12,12 +12,12 @@
|
|
12
12
|
<table class="processes bg-white table table-hover table-bordered table-striped mb-0 align-middle">
|
13
13
|
<thead>
|
14
14
|
<tr class="align-middle">
|
15
|
-
<th
|
16
|
-
<th class="col-sm-2"
|
15
|
+
<th><%== sort_link(:name) %></th>
|
16
|
+
<th class="col-sm-2"><%== sort_link('Started', :started_at, rev: true) %></th>
|
17
17
|
<th class="col-sm-1">Memory</th>
|
18
18
|
<th class="col-sm-1">Performance</th>
|
19
19
|
<th class="col-sm-1">Load</th>
|
20
|
-
<th class="col-sm-1"
|
20
|
+
<th class="col-sm-1"><%== sort_link(:lag_stored) %></th>
|
21
21
|
</tr>
|
22
22
|
</thead>
|
23
23
|
<tbody>
|
@@ -0,0 +1,43 @@
|
|
1
|
+
<%== view_title(@process.name) %>
|
2
|
+
|
3
|
+
<% if @process.status == 'stopped' %>
|
4
|
+
<%== partial 'consumers/consumer/stopped' %>
|
5
|
+
<% end %>
|
6
|
+
|
7
|
+
<%== partial 'consumers/consumer/metrics' %>
|
8
|
+
|
9
|
+
<%== partial 'consumers/consumer/tabs' %>
|
10
|
+
|
11
|
+
<% if @pending_jobs.empty? %>
|
12
|
+
<%== partial 'consumers/consumer/no_jobs', locals: { type: 'pending' } %>
|
13
|
+
<% else %>
|
14
|
+
<div class="container">
|
15
|
+
<div class="row mb-5">
|
16
|
+
<div class="col-sm-12">
|
17
|
+
<table class="processes bg-white table table-hover table-bordered table-striped mb-0 align-middle">
|
18
|
+
<thead>
|
19
|
+
<tr class="align-middle">
|
20
|
+
<th><%== sort_link(:topic) %></th>
|
21
|
+
<th><%== sort_link(:consumer) %></th>
|
22
|
+
<th><%== sort_link(:type) %></th>
|
23
|
+
<th><%== sort_link(:messages) %></th>
|
24
|
+
<th><%== sort_link(:first_offset) %></th>
|
25
|
+
<th><%== sort_link(:last_offset) %></th>
|
26
|
+
<th><%== sort_link(:committed_offset) %></th>
|
27
|
+
<th><%== sort_link('Scheduled at', :updated_at, rev: true) %></th>
|
28
|
+
</tr>
|
29
|
+
</thead>
|
30
|
+
<tbody>
|
31
|
+
<%==
|
32
|
+
render_each(
|
33
|
+
@pending_jobs,
|
34
|
+
'consumers/consumer/_job',
|
35
|
+
local: :job
|
36
|
+
)
|
37
|
+
%>
|
38
|
+
</tbody>
|
39
|
+
</table>
|
40
|
+
</div>
|
41
|
+
</div>
|
42
|
+
</div>
|
43
|
+
<% end %>
|
@@ -8,8 +8,8 @@
|
|
8
8
|
|
9
9
|
<%== partial 'consumers/consumer/tabs' %>
|
10
10
|
|
11
|
-
<% if @
|
12
|
-
<%== partial 'consumers/consumer/no_jobs' %>
|
11
|
+
<% if @running_jobs.empty? %>
|
12
|
+
<%== partial 'consumers/consumer/no_jobs', locals: { type: 'running' } %>
|
13
13
|
<% else %>
|
14
14
|
<div class="container">
|
15
15
|
<div class="row mb-5">
|
@@ -17,19 +17,20 @@
|
|
17
17
|
<table class="processes bg-white table table-hover table-bordered table-striped mb-0 align-middle">
|
18
18
|
<thead>
|
19
19
|
<tr class="align-middle">
|
20
|
-
<th
|
21
|
-
<th
|
22
|
-
<th
|
23
|
-
<th
|
24
|
-
<th
|
25
|
-
<th
|
26
|
-
<th
|
20
|
+
<th><%== sort_link(:topic) %></th>
|
21
|
+
<th><%== sort_link(:consumer) %></th>
|
22
|
+
<th><%== sort_link(:type) %></th>
|
23
|
+
<th><%== sort_link(:messages) %></th>
|
24
|
+
<th><%== sort_link(:first_offset) %></th>
|
25
|
+
<th><%== sort_link(:last_offset) %></th>
|
26
|
+
<th><%== sort_link(:committed_offset) %></th>
|
27
|
+
<th><%== sort_link('Started at', :updated_at, rev: true) %></th>
|
27
28
|
</tr>
|
28
29
|
</thead>
|
29
30
|
<tbody>
|
30
31
|
<%==
|
31
32
|
render_each(
|
32
|
-
@
|
33
|
+
@running_jobs,
|
33
34
|
'consumers/consumer/_job',
|
34
35
|
local: :job
|
35
36
|
)
|
@@ -56,6 +56,7 @@
|
|
56
56
|
<%== partial 'shared/tab_nav', locals: { title: 'Utilization', id: 'utilization', active: true } %>
|
57
57
|
<%== partial 'shared/tab_nav', locals: { title: 'RSS', id: 'rss' } %>
|
58
58
|
<%== partial 'shared/tab_nav', locals: { title: 'Concurrency', id: 'concurrency' } %>
|
59
|
+
<%== partial 'shared/tab_nav', locals: { title: 'Data transfers', id: 'data-transfers' } %>
|
59
60
|
</ul>
|
60
61
|
|
61
62
|
<div class="tab-content">
|
@@ -70,9 +71,14 @@
|
|
70
71
|
</div>
|
71
72
|
|
72
73
|
<div class="tab-pane show" id="concurrency" role="tabpanel">
|
73
|
-
<% data = @aggregated_charts.with(:processes, :workers, :
|
74
|
+
<% data = @aggregated_charts.with(:processes, :workers, :active_listeners, :standby_listeners) %>
|
74
75
|
<%== partial 'shared/chart', locals: { data: data, id: 'concurrency' } %>
|
75
76
|
</div>
|
77
|
+
|
78
|
+
<div class="tab-pane show" id="data-transfers" role="tabpanel">
|
79
|
+
<% data = @aggregated_charts.data_transfers %>
|
80
|
+
<%== partial 'shared/chart', locals: { data: data, id: 'data-transfers', label_type_y: 'memory' } %>
|
81
|
+
</div>
|
76
82
|
</div>
|
77
83
|
</div>
|
78
84
|
</div>
|
@@ -0,0 +1,18 @@
|
|
1
|
+
<%
|
2
|
+
republish_path = root_path('messages', @message.topic, @message.partition, @message.offset, 'republish')
|
3
|
+
surrounding_path = explorer_path(@message.topic, @message.partition, @message.offset, 'surrounding')
|
4
|
+
%>
|
5
|
+
|
6
|
+
<div class="row mb-0">
|
7
|
+
<div class="col-sm-12 text-end">
|
8
|
+
<a href="<%= surrounding_path %>" class="btn btn-secondary btn-sm float-end ms-1">
|
9
|
+
⇋
|
10
|
+
Surrounding
|
11
|
+
</a>
|
12
|
+
|
13
|
+
<form action="<%= republish_path %>" method="post" class="confirm-action float-end">
|
14
|
+
<%== csrf_tag(republish_path) %>
|
15
|
+
<input type="submit" value="⟳ Republish" class="btn btn-primary btn-sm"/>
|
16
|
+
</form>
|
17
|
+
</div>
|
18
|
+
</div>
|
@@ -0,0 +1,43 @@
|
|
1
|
+
<div class="row mb-5">
|
2
|
+
<div class="col-sm-12">
|
3
|
+
<table class="processes bg-white table table-hover table-bordered table-striped mb-0 align-middle">
|
4
|
+
<tbody>
|
5
|
+
<% @message.metadata.to_h.except(:received_at, :key, :headers).each do |k, v| %>
|
6
|
+
<%==
|
7
|
+
partial(
|
8
|
+
'explorer/messages/detail',
|
9
|
+
locals: {
|
10
|
+
k: k,
|
11
|
+
v: v
|
12
|
+
}
|
13
|
+
)
|
14
|
+
%>
|
15
|
+
<% end %>
|
16
|
+
|
17
|
+
<%==
|
18
|
+
partial(
|
19
|
+
'explorer/messages/detail',
|
20
|
+
locals: {
|
21
|
+
k: 'bytesize',
|
22
|
+
v: format_memory(((@message.raw_payload&.bytesize || 0) / 1024.to_f).round(4))
|
23
|
+
}
|
24
|
+
)
|
25
|
+
%>
|
26
|
+
|
27
|
+
<%==
|
28
|
+
partial(
|
29
|
+
'explorer/messages/key',
|
30
|
+
locals: { message: @message }
|
31
|
+
)
|
32
|
+
%>
|
33
|
+
|
34
|
+
<%==
|
35
|
+
partial(
|
36
|
+
'explorer/messages/headers',
|
37
|
+
locals: { message: @message }
|
38
|
+
)
|
39
|
+
%>
|
40
|
+
</tbody>
|
41
|
+
</table>
|
42
|
+
</div>
|
43
|
+
</div>
|