karafka-web 0.6.2 → 0.6.3
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 +2 -8
- data/Gemfile.lock +1 -1
- data/lib/karafka/web/config.rb +0 -2
- data/lib/karafka/web/ui/base.rb +2 -6
- data/lib/karafka/web/ui/controllers/base.rb +0 -17
- data/lib/karafka/web/ui/controllers/cluster.rb +2 -5
- data/lib/karafka/web/ui/controllers/consumers.rb +1 -3
- data/lib/karafka/web/ui/controllers/errors.rb +6 -19
- data/lib/karafka/web/ui/controllers/jobs.rb +1 -3
- data/lib/karafka/web/ui/controllers/requests/params.rb +0 -10
- data/lib/karafka/web/ui/lib/paginate_array.rb +38 -0
- data/lib/karafka/web/ui/models/message.rb +38 -114
- data/lib/karafka/web/ui/models/status.rb +1 -3
- data/lib/karafka/web/ui/pro/app.rb +3 -11
- data/lib/karafka/web/ui/pro/controllers/consumers.rb +1 -3
- data/lib/karafka/web/ui/pro/controllers/dlq.rb +2 -1
- data/lib/karafka/web/ui/pro/controllers/errors.rb +10 -43
- data/lib/karafka/web/ui/pro/controllers/explorer.rb +7 -52
- data/lib/karafka/web/ui/pro/views/errors/_breadcrumbs.erb +6 -8
- data/lib/karafka/web/ui/pro/views/errors/_error.erb +1 -1
- data/lib/karafka/web/ui/pro/views/errors/_partition_option.erb +1 -1
- data/lib/karafka/web/ui/pro/views/errors/index.erb +56 -9
- data/lib/karafka/web/ui/pro/views/explorer/_breadcrumbs.erb +1 -1
- data/lib/karafka/web/ui/pro/views/explorer/_message.erb +2 -8
- data/lib/karafka/web/ui/pro/views/explorer/_partition_option.erb +1 -1
- data/lib/karafka/web/ui/pro/views/explorer/_topic.erb +1 -1
- data/lib/karafka/web/ui/pro/views/explorer/partition/_messages.erb +0 -1
- data/lib/karafka/web/ui/pro/views/explorer/partition.erb +1 -1
- data/lib/karafka/web/ui/pro/views/shared/_navigation.erb +1 -1
- data/lib/karafka/web/ui/views/cluster/_partition.erb +1 -1
- data/lib/karafka/web/ui/views/errors/_error.erb +1 -1
- data/lib/karafka/web/ui/views/shared/_pagination.erb +12 -16
- data/lib/karafka/web/version.rb +1 -1
- data.tar.gz.sig +0 -0
- metadata +2 -17
- metadata.gz.sig +0 -0
- data/lib/karafka/web/ui/lib/paginations/base.rb +0 -61
- data/lib/karafka/web/ui/lib/paginations/offset_based.rb +0 -96
- data/lib/karafka/web/ui/lib/paginations/page_based.rb +0 -70
- data/lib/karafka/web/ui/lib/paginations/paginators/arrays.rb +0 -33
- data/lib/karafka/web/ui/lib/paginations/paginators/base.rb +0 -23
- data/lib/karafka/web/ui/lib/paginations/paginators/partitions.rb +0 -52
- data/lib/karafka/web/ui/lib/paginations/paginators/sets.rb +0 -85
- data/lib/karafka/web/ui/lib/ttl_cache.rb +0 -74
- data/lib/karafka/web/ui/models/cluster_info.rb +0 -59
- data/lib/karafka/web/ui/pro/views/errors/_table.erb +0 -21
- data/lib/karafka/web/ui/pro/views/errors/_title_with_select.erb +0 -31
- data/lib/karafka/web/ui/pro/views/errors/partition.erb +0 -17
- data/lib/karafka/web/ui/pro/views/explorer/topic/_empty.erb +0 -3
- data/lib/karafka/web/ui/pro/views/explorer/topic/_limited.erb +0 -4
- data/lib/karafka/web/ui/pro/views/explorer/topic/_partitions.erb +0 -11
- data/lib/karafka/web/ui/pro/views/explorer/topic.erb +0 -49
@@ -1,33 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Karafka
|
4
|
-
module Web
|
5
|
-
module Ui
|
6
|
-
module Lib
|
7
|
-
module Paginations
|
8
|
-
# Namespace for commands that build paginated resources based on the provided page
|
9
|
-
module Paginators
|
10
|
-
# A simple wrapper for paginating array related data structures
|
11
|
-
# We call this with plural (same with `Sets`) to avoid confusion with Ruby classes
|
12
|
-
class Arrays < Base
|
13
|
-
class << self
|
14
|
-
# @param array [Array] array we want to paginate
|
15
|
-
# @param current_page [Integer] page we want to be on
|
16
|
-
# @return [Array<Array, Boolean>] Array with two elements: first is the array with
|
17
|
-
# data of the given page and second is a boolean flag with info if the elements we got
|
18
|
-
# are from the last page
|
19
|
-
def call(array, current_page)
|
20
|
-
slices = array.each_slice(per_page).to_a
|
21
|
-
current_data = slices[current_page - 1] || []
|
22
|
-
last_page = !(slices.count >= current_page - 1 && current_data.size >= per_page)
|
23
|
-
|
24
|
-
[current_data, last_page]
|
25
|
-
end
|
26
|
-
end
|
27
|
-
end
|
28
|
-
end
|
29
|
-
end
|
30
|
-
end
|
31
|
-
end
|
32
|
-
end
|
33
|
-
end
|
@@ -1,23 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Karafka
|
4
|
-
module Web
|
5
|
-
module Ui
|
6
|
-
module Lib
|
7
|
-
module Paginations
|
8
|
-
module Paginators
|
9
|
-
# Base paginator
|
10
|
-
class Base
|
11
|
-
class << self
|
12
|
-
# @return [Integer] number of elements per page
|
13
|
-
def per_page
|
14
|
-
::Karafka::Web.config.ui.per_page
|
15
|
-
end
|
16
|
-
end
|
17
|
-
end
|
18
|
-
end
|
19
|
-
end
|
20
|
-
end
|
21
|
-
end
|
22
|
-
end
|
23
|
-
end
|
@@ -1,52 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Karafka
|
4
|
-
module Web
|
5
|
-
module Ui
|
6
|
-
module Lib
|
7
|
-
module Paginations
|
8
|
-
module Paginators
|
9
|
-
# Paginator for selecting proper range of partitions for each page
|
10
|
-
# For topics with a lot of partitions we cannot get all the data efficiently, that
|
11
|
-
# is why we limit number of partitions per page and reduce the operations
|
12
|
-
# that way. This allows us to effectively display more while not having to fetch
|
13
|
-
# more partitions then the number of messages per page.
|
14
|
-
# In cases like this we distribute partitions evenly part of partitions on each of
|
15
|
-
# the pages. This may become unreliable for partitions that are not evenly
|
16
|
-
# distributed but this allows us to display data for as many partitions as we want
|
17
|
-
# without overloading the system
|
18
|
-
class Partitions < Base
|
19
|
-
class << self
|
20
|
-
# Computers the partitions slice, materialized page and the limitations status
|
21
|
-
# for a given page
|
22
|
-
# @param partitions_count [Integer] number of partitions for a given topic
|
23
|
-
# @param current_page [Integer] current page
|
24
|
-
# @return [Array<Array<Integer>, Integer, Boolean>] list of partitions that should
|
25
|
-
# be active on a given page, materialized page for them and info if we had to
|
26
|
-
# limit the partitions number on a given page
|
27
|
-
def call(partitions_count, current_page)
|
28
|
-
# How many "chunks" of partitions we will have
|
29
|
-
slices_count = (partitions_count / per_page.to_f).ceil
|
30
|
-
# How many partitions in a single slice should we have
|
31
|
-
in_slice = (partitions_count / slices_count.to_f).ceil
|
32
|
-
# Which "chunked" page do we want to get
|
33
|
-
materialized_page = (current_page / slices_count.to_f).ceil
|
34
|
-
# Which slice is the one we are operating on
|
35
|
-
active_slice_index = (current_page - 1) % slices_count
|
36
|
-
# All available slices so we can pick one that is active
|
37
|
-
partitions_slices = (0...partitions_count).each_slice(in_slice).to_a
|
38
|
-
# Select active partitions only
|
39
|
-
active_partitions = partitions_slices[active_slice_index]
|
40
|
-
# Are we limiting ourselves because of partition count
|
41
|
-
limited = slices_count > 1
|
42
|
-
|
43
|
-
[active_partitions, materialized_page, limited]
|
44
|
-
end
|
45
|
-
end
|
46
|
-
end
|
47
|
-
end
|
48
|
-
end
|
49
|
-
end
|
50
|
-
end
|
51
|
-
end
|
52
|
-
end
|
@@ -1,85 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Karafka
|
4
|
-
module Web
|
5
|
-
module Ui
|
6
|
-
module Lib
|
7
|
-
module Paginations
|
8
|
-
module Paginators
|
9
|
-
# Paginator that allows us to take several lists/sets and iterate over them in a
|
10
|
-
# round-robin fashion.
|
11
|
-
#
|
12
|
-
# It does not have to iterate over all the elements from each set for higher pages
|
13
|
-
# making it much more effective than the naive implementation.
|
14
|
-
class Sets < Base
|
15
|
-
class << self
|
16
|
-
# @param counts [Array<Integer>] sets elements counts
|
17
|
-
# @param current_page [Integer] page number
|
18
|
-
# @return [Hash<Integer, Range>] hash with integer keys indicating the count
|
19
|
-
# location and the range needed to be taken of elements (counting backwards) for
|
20
|
-
# each partition
|
21
|
-
def call(counts, current_page)
|
22
|
-
return {} if current_page < 1
|
23
|
-
|
24
|
-
lists = counts.dup.map.with_index { |el, i| [i, el] }
|
25
|
-
|
26
|
-
curr_item_index = 0
|
27
|
-
curr_list_index = 0
|
28
|
-
items_to_skip_count = per_page * (current_page - 1)
|
29
|
-
|
30
|
-
loop do
|
31
|
-
lists_count = lists.length
|
32
|
-
return {} if lists_count.zero?
|
33
|
-
|
34
|
-
shortest_list_count = lists.map(&:last).min
|
35
|
-
mover = (shortest_list_count - curr_item_index)
|
36
|
-
items_we_are_considering_count = lists_count * mover
|
37
|
-
|
38
|
-
if items_we_are_considering_count >= items_to_skip_count
|
39
|
-
curr_item_index += items_to_skip_count / lists_count
|
40
|
-
curr_list_index = items_to_skip_count % lists_count
|
41
|
-
break
|
42
|
-
else
|
43
|
-
curr_item_index = shortest_list_count
|
44
|
-
lists.delete_if { |x| x.last == shortest_list_count }
|
45
|
-
items_to_skip_count -= items_we_are_considering_count
|
46
|
-
end
|
47
|
-
end
|
48
|
-
|
49
|
-
page_items = []
|
50
|
-
largest_list_count = lists.map(&:last).max
|
51
|
-
|
52
|
-
while page_items.length < per_page && curr_item_index < largest_list_count
|
53
|
-
curr_list = lists[curr_list_index]
|
54
|
-
|
55
|
-
if curr_item_index < curr_list.last
|
56
|
-
page_items << [curr_list.first, curr_item_index]
|
57
|
-
end
|
58
|
-
|
59
|
-
curr_list_index += 1
|
60
|
-
if curr_list_index == lists.length
|
61
|
-
curr_list_index = 0
|
62
|
-
curr_item_index += 1
|
63
|
-
end
|
64
|
-
end
|
65
|
-
|
66
|
-
hashed = Hash.new { |h, k| h[k] = [] }
|
67
|
-
|
68
|
-
page_items.each do |el|
|
69
|
-
hashed[el.first] << el.last
|
70
|
-
end
|
71
|
-
|
72
|
-
hashed.each do |key, value|
|
73
|
-
hashed[key] = (value.first..value.last)
|
74
|
-
end
|
75
|
-
|
76
|
-
hashed
|
77
|
-
end
|
78
|
-
end
|
79
|
-
end
|
80
|
-
end
|
81
|
-
end
|
82
|
-
end
|
83
|
-
end
|
84
|
-
end
|
85
|
-
end
|
@@ -1,74 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Karafka
|
4
|
-
module Web
|
5
|
-
module Ui
|
6
|
-
# Non info related extra components used in the UI
|
7
|
-
module Lib
|
8
|
-
# Ttl Cache for caching things in-memory
|
9
|
-
# @note It **is** thread-safe
|
10
|
-
class TtlCache
|
11
|
-
include ::Karafka::Core::Helpers::Time
|
12
|
-
|
13
|
-
# @param ttl [Integer] time in ms how long should this cache keep data
|
14
|
-
def initialize(ttl)
|
15
|
-
@ttl = ttl
|
16
|
-
@times = {}
|
17
|
-
@values = {}
|
18
|
-
@mutex = Mutex.new
|
19
|
-
end
|
20
|
-
|
21
|
-
# Reads data from the cache
|
22
|
-
#
|
23
|
-
# @param key [String, Symbol] key for the cache read
|
24
|
-
# @return [Object] anything that was cached
|
25
|
-
def read(key)
|
26
|
-
@mutex.synchronize do
|
27
|
-
evict
|
28
|
-
@values[key]
|
29
|
-
end
|
30
|
-
end
|
31
|
-
|
32
|
-
# Writes to the cache
|
33
|
-
#
|
34
|
-
# @param key [String, Symbol] key for the cache
|
35
|
-
# @param value [Object] value we want to cache
|
36
|
-
# @return [Object] value we have written
|
37
|
-
def write(key, value)
|
38
|
-
@mutex.synchronize do
|
39
|
-
@times[key] = monotonic_now + @ttl
|
40
|
-
@values[key] = value
|
41
|
-
end
|
42
|
-
end
|
43
|
-
|
44
|
-
# Reads from the cache and if value not present, will run the block and store its result
|
45
|
-
# in the cache
|
46
|
-
#
|
47
|
-
# @param key [String, Symbol] key for the cache read
|
48
|
-
# @return [Object] anything that was cached or yielded
|
49
|
-
def fetch(key)
|
50
|
-
@mutex.synchronize do
|
51
|
-
evict
|
52
|
-
|
53
|
-
return @values[key] if @values.key?(key)
|
54
|
-
|
55
|
-
@values[key] = yield
|
56
|
-
end
|
57
|
-
end
|
58
|
-
|
59
|
-
private
|
60
|
-
|
61
|
-
# Removes expired elements from the cache
|
62
|
-
def evict
|
63
|
-
@times.each do |key, time|
|
64
|
-
next if time >= monotonic_now
|
65
|
-
|
66
|
-
@times.delete(key)
|
67
|
-
@values.delete(key)
|
68
|
-
end
|
69
|
-
end
|
70
|
-
end
|
71
|
-
end
|
72
|
-
end
|
73
|
-
end
|
74
|
-
end
|
@@ -1,59 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Karafka
|
4
|
-
module Web
|
5
|
-
module Ui
|
6
|
-
module Models
|
7
|
-
# Wraps around the `Karafka::Admin#cluster_info` with caching and some additional aliases
|
8
|
-
# so we can reference relevant information easily
|
9
|
-
class ClusterInfo
|
10
|
-
class << self
|
11
|
-
# Gets us all the cluster metadata info
|
12
|
-
#
|
13
|
-
# @param cached [Boolean] should we use cached data (true by default)
|
14
|
-
# @return [Rdkafka::Metadata] cluster metadata info
|
15
|
-
def fetch(cached: true)
|
16
|
-
cache = ::Karafka::Web.config.ui.cache
|
17
|
-
|
18
|
-
cluster_info = cache.read(:cluster_info)
|
19
|
-
|
20
|
-
if cluster_info.nil? || !cached
|
21
|
-
cluster_info = cache.write(:cluster_info, Karafka::Admin.cluster_info)
|
22
|
-
end
|
23
|
-
|
24
|
-
cluster_info
|
25
|
-
end
|
26
|
-
|
27
|
-
# Returns us all the info about available topics from the cluster
|
28
|
-
#
|
29
|
-
# @param cached [Boolean] should we use cached data (true by default)
|
30
|
-
# @return [Array<Ui::Models::Topic>] topics details
|
31
|
-
def topics(cached: true)
|
32
|
-
fetch(cached: cached)
|
33
|
-
.topics
|
34
|
-
.map { |topic| Topic.new(topic) }
|
35
|
-
end
|
36
|
-
|
37
|
-
# Fetches us details about particular topic
|
38
|
-
#
|
39
|
-
# @param topic_name [String] name of the topic we are looking for
|
40
|
-
# @param cached [Boolean] should we use cached data (true by default)
|
41
|
-
# @return [Ui::Models::Topic] topic details
|
42
|
-
def topic(topic_name, cached: true)
|
43
|
-
topics(cached: cached)
|
44
|
-
.find { |topic_data| topic_data.topic_name == topic_name }
|
45
|
-
.tap { |topic| topic || raise(Web::Errors::Ui::NotFoundError, topic_name) }
|
46
|
-
end
|
47
|
-
|
48
|
-
# @param topic_name [String] name of the topic we are looking for
|
49
|
-
# @param cached [Boolean] should we use cached data (true by default)
|
50
|
-
# @return [Integer] number of partitions in a given topic
|
51
|
-
def partitions_count(topic_name, cached: true)
|
52
|
-
topic(topic_name, cached: cached).partition_count
|
53
|
-
end
|
54
|
-
end
|
55
|
-
end
|
56
|
-
end
|
57
|
-
end
|
58
|
-
end
|
59
|
-
end
|
@@ -1,21 +0,0 @@
|
|
1
|
-
<table class="processes bg-white table table-hover table-bordered table-striped mb-0 align-middle">
|
2
|
-
<thead>
|
3
|
-
<tr class="align-middle">
|
4
|
-
<th>Origin</th>
|
5
|
-
<th>Process name</th>
|
6
|
-
<th>Error</th>
|
7
|
-
<th>Occurred at</th>
|
8
|
-
<th></th>
|
9
|
-
</tr>
|
10
|
-
</thead>
|
11
|
-
|
12
|
-
<tbody>
|
13
|
-
<%==
|
14
|
-
each_partial(
|
15
|
-
@error_messages,
|
16
|
-
'errors/error',
|
17
|
-
local: :error_msg
|
18
|
-
)
|
19
|
-
%>
|
20
|
-
</tbody>
|
21
|
-
</table>
|
@@ -1,31 +0,0 @@
|
|
1
|
-
<div class="container mb-4">
|
2
|
-
<div class="row">
|
3
|
-
<div class="col">
|
4
|
-
<h3>
|
5
|
-
Errors
|
6
|
-
</h3>
|
7
|
-
</div>
|
8
|
-
|
9
|
-
<div class="col">
|
10
|
-
<div class="col-auto text-end">
|
11
|
-
<label class="col-form-label">Partition</label>
|
12
|
-
</div>
|
13
|
-
</div>
|
14
|
-
|
15
|
-
<div class="col pt-1 mb-0 pb-0">
|
16
|
-
<div class="col-auto">
|
17
|
-
<select class="form-select form-select-sm mb-0 form-control" id="current-partition">
|
18
|
-
<%==
|
19
|
-
each_partial(
|
20
|
-
[nil] + @partitions_count.times.to_a,
|
21
|
-
'errors/partition_option',
|
22
|
-
local: :partition
|
23
|
-
)
|
24
|
-
%>
|
25
|
-
</select>
|
26
|
-
</div>
|
27
|
-
</div>
|
28
|
-
</div>
|
29
|
-
|
30
|
-
<hr>
|
31
|
-
</div>
|
@@ -1,17 +0,0 @@
|
|
1
|
-
<%== partial('errors/title_with_select') %>
|
2
|
-
|
3
|
-
<div class="container">
|
4
|
-
<div class="row mb-5">
|
5
|
-
<div class="col-sm-12">
|
6
|
-
<%== partial('explorer/partition/watermark_offsets') %>
|
7
|
-
|
8
|
-
<% if @watermark_offsets.empty? %>
|
9
|
-
<%== partial 'errors/no_errors' %>
|
10
|
-
<% elsif @watermark_offsets.cleaned? %>
|
11
|
-
<%== partial 'errors/cleaned' %>
|
12
|
-
<% else %>
|
13
|
-
<%== partial 'errors/table' %>
|
14
|
-
<% end %>
|
15
|
-
</div>
|
16
|
-
</div>
|
17
|
-
</div>
|
@@ -1,11 +0,0 @@
|
|
1
|
-
<p class="text-end mb-4">
|
2
|
-
Partitions:
|
3
|
-
<span class="badge bg-secondary mt-1 mb-1">
|
4
|
-
total: <%= @partitions_count %>
|
5
|
-
</span>
|
6
|
-
|
7
|
-
<span class="badge bg-secondary mt-1 mb-1">
|
8
|
-
visible:
|
9
|
-
<%= [@active_partitions.first, @active_partitions.last].uniq.join(' to ') %>
|
10
|
-
</span>
|
11
|
-
</p>
|
@@ -1,49 +0,0 @@
|
|
1
|
-
<div class="container mb-4">
|
2
|
-
<div class="row">
|
3
|
-
<div class="col">
|
4
|
-
<h3>
|
5
|
-
<%= @topic_id %>
|
6
|
-
</h3>
|
7
|
-
</div>
|
8
|
-
|
9
|
-
<div class="col">
|
10
|
-
<div class="col-auto text-end">
|
11
|
-
<label class="col-form-label">Partition</label>
|
12
|
-
</div>
|
13
|
-
</div>
|
14
|
-
|
15
|
-
<div class="col pt-1 mb-0 pb-0">
|
16
|
-
<div class="col-auto">
|
17
|
-
<select class="form-select form-select-sm mb-0 form-control" id="current-partition">
|
18
|
-
<%==
|
19
|
-
each_partial(
|
20
|
-
[nil] + @partitions_count.times.to_a,
|
21
|
-
'explorer/partition_option',
|
22
|
-
local: :partition
|
23
|
-
)
|
24
|
-
%>
|
25
|
-
</select>
|
26
|
-
</div>
|
27
|
-
</div>
|
28
|
-
</div>
|
29
|
-
|
30
|
-
<hr>
|
31
|
-
</div>
|
32
|
-
|
33
|
-
<div class="container">
|
34
|
-
<div class="row mb-5">
|
35
|
-
<div class="col-sm-12">
|
36
|
-
<%== partial('explorer/topic/partitions') %>
|
37
|
-
|
38
|
-
<% if @limited %>
|
39
|
-
<%== partial('explorer/topic/limited') %>
|
40
|
-
<% end %>
|
41
|
-
|
42
|
-
<% if @messages.empty? && params.current_page == 1 %>
|
43
|
-
<%== partial 'explorer/topic/empty' %>
|
44
|
-
<% else %>
|
45
|
-
<%== partial('explorer/partition/messages') %>
|
46
|
-
<% end %>
|
47
|
-
</div>
|
48
|
-
</div>
|
49
|
-
</div>
|