karafka-web 0.6.0 → 0.6.2
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 +22 -1
- data/Gemfile.lock +1 -1
- data/lib/karafka/web/config.rb +2 -0
- data/lib/karafka/web/tracking/consumers/contracts/report.rb +7 -3
- data/lib/karafka/web/tracking/consumers/reporter.rb +5 -3
- data/lib/karafka/web/tracking/consumers/sampler.rb +2 -1
- data/lib/karafka/web/tracking/sampler.rb +5 -0
- data/lib/karafka/web/ui/base.rb +6 -2
- data/lib/karafka/web/ui/controllers/base.rb +17 -0
- data/lib/karafka/web/ui/controllers/cluster.rb +5 -2
- data/lib/karafka/web/ui/controllers/consumers.rb +3 -1
- data/lib/karafka/web/ui/controllers/errors.rb +19 -6
- data/lib/karafka/web/ui/controllers/jobs.rb +3 -1
- data/lib/karafka/web/ui/controllers/requests/params.rb +10 -0
- 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/ttl_cache.rb +74 -0
- data/lib/karafka/web/ui/models/cluster_info.rb +59 -0
- data/lib/karafka/web/ui/models/message.rb +114 -38
- data/lib/karafka/web/ui/models/status.rb +34 -8
- data/lib/karafka/web/ui/pro/app.rb +11 -3
- data/lib/karafka/web/ui/pro/controllers/consumers.rb +3 -1
- data/lib/karafka/web/ui/pro/controllers/dlq.rb +1 -2
- data/lib/karafka/web/ui/pro/controllers/errors.rb +43 -10
- data/lib/karafka/web/ui/pro/controllers/explorer.rb +52 -7
- data/lib/karafka/web/ui/pro/views/consumers/consumer/_metrics.erb +6 -1
- data/lib/karafka/web/ui/pro/views/errors/_breadcrumbs.erb +8 -6
- 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/_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/explorer/_breadcrumbs.erb +1 -1
- data/lib/karafka/web/ui/pro/views/explorer/_message.erb +8 -2
- 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 +1 -0
- data/lib/karafka/web/ui/pro/views/explorer/partition.erb +1 -1
- 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/_partitions.erb +11 -0
- data/lib/karafka/web/ui/pro/views/explorer/topic.erb +49 -0
- 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 +16 -12
- data/lib/karafka/web/ui/views/status/failures/_initial_state.erb +1 -10
- data/lib/karafka/web/ui/views/status/info/_components.erb +6 -1
- data/lib/karafka/web/ui/views/status/show.erb +6 -1
- data/lib/karafka/web/ui/views/status/successes/_connection.erb +1 -0
- data/lib/karafka/web/ui/views/status/warnings/_connection.erb +11 -0
- data/lib/karafka/web/version.rb +1 -1
- data.tar.gz.sig +0 -0
- metadata +20 -3
- metadata.gz.sig +0 -0
- data/lib/karafka/web/ui/lib/paginate_array.rb +0 -38
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d52e64643f448374a2f4efcb5e27089d5f36c8b8cde0eac5e2434c59b07d371c
|
4
|
+
data.tar.gz: '068a2ee3c3d82eeccdde1e001e6788c71980769c58013dd5290ad47002eac47e'
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0ef49085501fafc176d09c6813d3bafc7cb5d56231e2b655462ed12da02effe623a46c1934a8f17e9913dccf8da6405877ddede9a0f519b350613d0fbf4a66a1
|
7
|
+
data.tar.gz: 8251fe9ac27bab1b0992cb8f9baae68fe15100b48814e245589c6a38cc608ee5fa2d65d62faff0fdc8712dcff7685d82a0e87677d56bcc73fd35b07766f02af8
|
checksums.yaml.gz.sig
CHANGED
Binary file
|
data/CHANGELOG.md
CHANGED
@@ -1,6 +1,27 @@
|
|
1
1
|
# Karafka Web changelog
|
2
2
|
|
3
|
-
## 0.
|
3
|
+
## 0.7.0 (Unreleased)
|
4
|
+
- **[Feature]** Introduce per-topic data exploration in the Explorer.
|
5
|
+
- [Improvement] Introduce in-memory cluster state cached to improve performance.
|
6
|
+
- [Improvement] Switch to offset based pagination instead of per-page pagination.
|
7
|
+
- [Improvement] Avoid double-reading of watermark offsets for explorer and errors display.
|
8
|
+
- [Improvement] When no params needed for a page, do not include empty params.
|
9
|
+
- [Improvement] Do not include page when page is 1 in the url.
|
10
|
+
- [Refactor] Reorganize pagination engine to support offset based pagination.
|
11
|
+
|
12
|
+
## 0.6.2 (2023-07-22)
|
13
|
+
- [Fix] Fix extensive CPU usage when using HPET clock instead of TSC due to interrupt frequency.
|
14
|
+
|
15
|
+
## 0.6.1 (2023-06-25)
|
16
|
+
- [Improvement] Include the karafka-web version in the status page tags.
|
17
|
+
- [Improvement] Report `karafka-web` version that is running in particular processes.
|
18
|
+
- [Improvement] Display `karafka-web` version in the per-process view.
|
19
|
+
- [Improvement] Report in the web-ui a scenario, where getting cluster info takes more than 500ms as a warning to make people realize, that operating with Kafka with extensive latencies is not recommended.
|
20
|
+
- [Improvement] Continue the status assessment flow on warnings.
|
21
|
+
- [Fix] Do not recommend running a server as a way to bootstrap the initial state.
|
22
|
+
- [Fix] Ensure in the report contract, that `karafka-core`, `karafka-web`, `rdkafka` and `librdkafka` are validated.
|
23
|
+
|
24
|
+
## 0.6.0 (2023-06-13)
|
4
25
|
- **[Feature]** Introduce producers errors tracking.
|
5
26
|
- [Improvement] Display the error origin as a badge to align with consumers view topic assignments.
|
6
27
|
- [Improvement] Collect more job metrics for future usage.
|
data/Gemfile.lock
CHANGED
data/lib/karafka/web/config.rb
CHANGED
@@ -12,7 +12,7 @@ module Karafka
|
|
12
12
|
class Report < Tracking::Contracts::Base
|
13
13
|
configure
|
14
14
|
|
15
|
-
required(:schema_version) { |val| val.is_a?(String) }
|
15
|
+
required(:schema_version) { |val| val.is_a?(String) && !val.empty? }
|
16
16
|
required(:dispatched_at) { |val| val.is_a?(Numeric) && val.positive? }
|
17
17
|
# We have consumers and producer reports and need to ensure that each is handled
|
18
18
|
# in an expected fashion
|
@@ -24,7 +24,7 @@ module Karafka
|
|
24
24
|
required(:memory_usage) { |val| val.is_a?(Integer) && val >= 0 }
|
25
25
|
required(:memory_total_usage) { |val| val.is_a?(Integer) && val >= 0 }
|
26
26
|
required(:memory_size) { |val| val.is_a?(Integer) && val >= 0 }
|
27
|
-
required(:status) { |val| ::Karafka::Status::STATES.key?(val.to_sym) }
|
27
|
+
required(:status) { |val| ::Karafka::Status::STATES.key?(val.to_s.to_sym) }
|
28
28
|
required(:listeners) { |val| val.is_a?(Integer) && val >= 0 }
|
29
29
|
required(:concurrency) { |val| val.is_a?(Integer) && val.positive? }
|
30
30
|
required(:tags) { |val| val.is_a?(Karafka::Core::Taggable::Tags) }
|
@@ -38,9 +38,13 @@ module Karafka
|
|
38
38
|
end
|
39
39
|
|
40
40
|
nested(:versions) do
|
41
|
+
required(:ruby) { |val| val.is_a?(String) && !val.empty? }
|
41
42
|
required(:karafka) { |val| val.is_a?(String) && !val.empty? }
|
43
|
+
required(:karafka_core) { |val| val.is_a?(String) && !val.empty? }
|
44
|
+
required(:karafka_web) { |val| val.is_a?(String) && !val.empty? }
|
42
45
|
required(:waterdrop) { |val| val.is_a?(String) && !val.empty? }
|
43
|
-
required(:
|
46
|
+
required(:rdkafka) { |val| val.is_a?(String) && !val.empty? }
|
47
|
+
required(:librdkafka) { |val| val.is_a?(String) && !val.empty? }
|
44
48
|
end
|
45
49
|
|
46
50
|
nested(:stats) do
|
@@ -93,12 +93,14 @@ module Karafka
|
|
93
93
|
def call
|
94
94
|
@running = true
|
95
95
|
|
96
|
+
# We won't track more often anyhow but want to try frequently not to miss a window
|
97
|
+
# We need to convert the sleep interval into seconds for sleep
|
98
|
+
sleep_time = ::Karafka::Web.config.tracking.interval.to_f / 1_000 / 10
|
99
|
+
|
96
100
|
loop do
|
97
101
|
report
|
98
102
|
|
99
|
-
|
100
|
-
# We need to convert the sleep interval into seconds for sleep
|
101
|
-
sleep(::Karafka::Web.config.tracking.interval / 1_000 / 10)
|
103
|
+
sleep(sleep_time)
|
102
104
|
end
|
103
105
|
end
|
104
106
|
|
@@ -75,8 +75,9 @@ module Karafka
|
|
75
75
|
versions: {
|
76
76
|
ruby: ruby_version,
|
77
77
|
karafka: karafka_version,
|
78
|
-
waterdrop: waterdrop_version,
|
79
78
|
karafka_core: karafka_core_version,
|
79
|
+
karafka_web: karafka_web_version,
|
80
|
+
waterdrop: waterdrop_version,
|
80
81
|
rdkafka: rdkafka_version,
|
81
82
|
librdkafka: librdkafka_version
|
82
83
|
},
|
@@ -32,6 +32,11 @@ module Karafka
|
|
32
32
|
::Karafka::VERSION
|
33
33
|
end
|
34
34
|
|
35
|
+
# @return [String] Karafka Web UI version
|
36
|
+
def karafka_web_version
|
37
|
+
::Karafka::Web::VERSION
|
38
|
+
end
|
39
|
+
|
35
40
|
# @return [String] Karafka::Core version
|
36
41
|
def karafka_core_version
|
37
42
|
::Karafka::Core::VERSION
|
data/lib/karafka/web/ui/base.rb
CHANGED
@@ -68,8 +68,12 @@ module Karafka
|
|
68
68
|
# Allows us to build current path with additional params
|
69
69
|
# @param query_data [Hash] query params we want to add to the current path
|
70
70
|
path :current do |query_data = {}|
|
71
|
-
q = query_data
|
72
|
-
|
71
|
+
q = query_data
|
72
|
+
.select { |_, v| v }
|
73
|
+
.map { |k, v| "#{k}=#{CGI.escape(v.to_s)}" }
|
74
|
+
.join('&')
|
75
|
+
|
76
|
+
[request.path, q].compact.delete_if(&:empty?).join('?')
|
73
77
|
end
|
74
78
|
|
75
79
|
# Sets appropriate template variables based on the response object and renders the
|
@@ -12,6 +12,8 @@ module Karafka
|
|
12
12
|
@params = params
|
13
13
|
end
|
14
14
|
|
15
|
+
private
|
16
|
+
|
15
17
|
# Builds the respond data object with assigned attributes based on instance variables.
|
16
18
|
#
|
17
19
|
# @return [Responses::Data] data that should be used to render appropriate view
|
@@ -33,6 +35,21 @@ module Karafka
|
|
33
35
|
attributes
|
34
36
|
)
|
35
37
|
end
|
38
|
+
|
39
|
+
# Initializes the expected pagination engine and assigns expected arguments
|
40
|
+
# @param args Any arguments accepted by the selected pagination engine
|
41
|
+
def paginate(*args)
|
42
|
+
engine = case args.count
|
43
|
+
when 2
|
44
|
+
Ui::Lib::Paginations::PageBased
|
45
|
+
when 4
|
46
|
+
Ui::Lib::Paginations::OffsetBased
|
47
|
+
else
|
48
|
+
raise ::Karafka::Errors::UnsupportedCaseError, args.count
|
49
|
+
end
|
50
|
+
|
51
|
+
@pagination = engine.new(*args)
|
52
|
+
end
|
36
53
|
end
|
37
54
|
end
|
38
55
|
end
|
@@ -8,7 +8,8 @@ module Karafka
|
|
8
8
|
class Cluster < Base
|
9
9
|
# List cluster info data
|
10
10
|
def index
|
11
|
-
|
11
|
+
# Make sure, that for the cluster view we always get the most recent cluster state
|
12
|
+
@cluster_info = Models::ClusterInfo.fetch(cached: false)
|
12
13
|
|
13
14
|
partitions_total = []
|
14
15
|
|
@@ -18,11 +19,13 @@ module Karafka
|
|
18
19
|
end
|
19
20
|
end
|
20
21
|
|
21
|
-
@partitions,
|
22
|
+
@partitions, last_page = Ui::Lib::Paginations::Paginators::Arrays.call(
|
22
23
|
partitions_total,
|
23
24
|
@params.current_page
|
24
25
|
)
|
25
26
|
|
27
|
+
paginate(@params.current_page, !last_page)
|
28
|
+
|
26
29
|
respond
|
27
30
|
end
|
28
31
|
|
@@ -11,11 +11,13 @@ module Karafka
|
|
11
11
|
def index
|
12
12
|
@current_state = Models::State.current!
|
13
13
|
@counters = Models::Counters.new(@current_state)
|
14
|
-
@processes,
|
14
|
+
@processes, last_page = Ui::Lib::Paginations::Paginators::Arrays.call(
|
15
15
|
Models::Processes.active(@current_state),
|
16
16
|
@params.current_page
|
17
17
|
)
|
18
18
|
|
19
|
+
paginate(@params.current_page, !last_page)
|
20
|
+
|
19
21
|
respond
|
20
22
|
end
|
21
23
|
end
|
@@ -10,13 +10,15 @@ module Karafka
|
|
10
10
|
class Errors < Base
|
11
11
|
# Lists first page of the errors
|
12
12
|
def index
|
13
|
-
@previous_page, @error_messages, @next_page, = Models::Message.page(
|
14
|
-
errors_topic,
|
15
|
-
0,
|
16
|
-
@params.current_page
|
17
|
-
)
|
18
|
-
|
19
13
|
@watermark_offsets = Ui::Models::WatermarkOffsets.find(errors_topic, 0)
|
14
|
+
previous_offset, @error_messages, next_offset, = current_page_data
|
15
|
+
|
16
|
+
paginate(
|
17
|
+
previous_offset,
|
18
|
+
@params.current_offset,
|
19
|
+
next_offset,
|
20
|
+
@error_messages.map(&:offset)
|
21
|
+
)
|
20
22
|
|
21
23
|
respond
|
22
24
|
end
|
@@ -34,6 +36,17 @@ module Karafka
|
|
34
36
|
|
35
37
|
private
|
36
38
|
|
39
|
+
# @return [Array] Array with requested messages as well as pagination details and other
|
40
|
+
# obtained metadata
|
41
|
+
def current_page_data
|
42
|
+
Models::Message.offset_page(
|
43
|
+
errors_topic,
|
44
|
+
0,
|
45
|
+
@params.current_offset,
|
46
|
+
@watermark_offsets
|
47
|
+
)
|
48
|
+
end
|
49
|
+
|
37
50
|
# @return [String] errors topic
|
38
51
|
def errors_topic
|
39
52
|
::Karafka::Web.config.topics.errors
|
@@ -19,11 +19,13 @@ module Karafka
|
|
19
19
|
end
|
20
20
|
end
|
21
21
|
|
22
|
-
@jobs,
|
22
|
+
@jobs, last_page = Ui::Lib::Paginations::Paginators::Arrays.call(
|
23
23
|
jobs_total,
|
24
24
|
@params.current_page
|
25
25
|
)
|
26
26
|
|
27
|
+
paginate(@params.current_page, !last_page)
|
28
|
+
|
27
29
|
respond
|
28
30
|
end
|
29
31
|
end
|
@@ -22,6 +22,16 @@ module Karafka
|
|
22
22
|
page.positive? ? page : 1
|
23
23
|
end
|
24
24
|
end
|
25
|
+
|
26
|
+
# @return [Integer] offset from which we want to start. `-1` indicates, that we want
|
27
|
+
# to show the first page discovered based on the high watermark offset. If no offset
|
28
|
+
# is provided, we go with the high offset first page approach
|
29
|
+
def current_offset
|
30
|
+
@current_offset ||= begin
|
31
|
+
offset = @request_params.fetch('offset', -1).to_i
|
32
|
+
offset < -1 ? -1 : offset
|
33
|
+
end
|
34
|
+
end
|
25
35
|
end
|
26
36
|
end
|
27
37
|
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Karafka
|
4
|
+
module Web
|
5
|
+
module Ui
|
6
|
+
module Lib
|
7
|
+
# Namespace for all the types of pagination engines we want to support
|
8
|
+
module Paginations
|
9
|
+
# Abstraction on top of pagination, so we can alter pagination key and other things
|
10
|
+
# for non-standard pagination views (non page based, etc)
|
11
|
+
#
|
12
|
+
# @note We do not use `_page` explicitly to indicate, that the page scope may not operate
|
13
|
+
# on numerable pages (1,2,3,4) but can operate on offsets or times, etc. `_offset` is
|
14
|
+
# more general and may refer to many types of pagination.
|
15
|
+
class Base
|
16
|
+
attr_reader :previous_offset, :current_offset, :next_offset
|
17
|
+
|
18
|
+
# @return [Boolean] Should we show pagination at all
|
19
|
+
def paginate?
|
20
|
+
raise NotImplementedError, 'Implement in a subclass'
|
21
|
+
end
|
22
|
+
|
23
|
+
# @return [Boolean] Should first offset link be active. If false, the first offset link
|
24
|
+
# will be disabled
|
25
|
+
def first_offset?
|
26
|
+
raise NotImplementedError, 'Implement in a subclass'
|
27
|
+
end
|
28
|
+
|
29
|
+
# @return [String] first offset url value
|
30
|
+
def first_offset
|
31
|
+
raise NotImplementedError, 'Implement in a subclass'
|
32
|
+
end
|
33
|
+
|
34
|
+
# @return [Boolean] Should previous offset link be active. If false, the previous
|
35
|
+
# offset link will be disabled
|
36
|
+
def previous_offset?
|
37
|
+
raise NotImplementedError, 'Implement in a subclass'
|
38
|
+
end
|
39
|
+
|
40
|
+
# @return [Boolean] Should we show current offset. If false, the current offset link
|
41
|
+
# will not be visible at all. Useful for non-linear pagination.
|
42
|
+
def current_offset?
|
43
|
+
raise NotImplementedError, 'Implement in a subclass'
|
44
|
+
end
|
45
|
+
|
46
|
+
# @return [Boolean] Should we show next offset pagination. If false, next offset link
|
47
|
+
# will be disabled.
|
48
|
+
def next_offset?
|
49
|
+
raise NotImplementedError, 'Implement in a subclass'
|
50
|
+
end
|
51
|
+
|
52
|
+
# @return [String] the url offset key
|
53
|
+
def offset_key
|
54
|
+
raise NotImplementedError, 'Implement in a subclass'
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,96 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Karafka
|
4
|
+
module Web
|
5
|
+
module Ui
|
6
|
+
module Lib
|
7
|
+
module Paginations
|
8
|
+
# Kafka offset based pagination backend
|
9
|
+
#
|
10
|
+
# Allows us to support paginating over offsets
|
11
|
+
class OffsetBased < Base
|
12
|
+
# @param previous_offset [Integer, false] previous offset or false if should not be
|
13
|
+
# presented
|
14
|
+
# @param current_offset [Integer] current offset
|
15
|
+
# @param next_offset [Integer, Boolean] should we show next offset page button. If
|
16
|
+
# false it will not be presented.
|
17
|
+
# @param visible_offsets [Array<Integer>] offsets that are visible in the paginated
|
18
|
+
# view. It is needed for the current page label
|
19
|
+
def initialize(
|
20
|
+
previous_offset,
|
21
|
+
current_offset,
|
22
|
+
next_offset,
|
23
|
+
visible_offsets
|
24
|
+
)
|
25
|
+
@previous_offset = previous_offset
|
26
|
+
@current_offset = current_offset
|
27
|
+
@next_offset = next_offset
|
28
|
+
@visible_offsets = visible_offsets
|
29
|
+
super()
|
30
|
+
end
|
31
|
+
|
32
|
+
# Show pagination only when there is more than one page of results to be presented
|
33
|
+
#
|
34
|
+
# @return [Boolean]
|
35
|
+
def paginate?
|
36
|
+
@current_offset && (!!@previous_offset || !!@next_offset)
|
37
|
+
end
|
38
|
+
|
39
|
+
# @return [Boolean] active only when we are not on the first page. First page is always
|
40
|
+
# indicated by the current offset being -1. If there is someone that sets up the
|
41
|
+
# current offset to a value equal to the last message in the topic partition, we do
|
42
|
+
# not consider it as a first page and we allow to "reset" to -1 via the first page
|
43
|
+
# button
|
44
|
+
def first_offset?
|
45
|
+
@current_offset != -1
|
46
|
+
end
|
47
|
+
|
48
|
+
# @return [Boolean] first page offset is always nothing because we use the default -1
|
49
|
+
# for the offset.
|
50
|
+
def first_offset
|
51
|
+
false
|
52
|
+
end
|
53
|
+
|
54
|
+
# @return [Boolean] Active previous page link when it is not the first page
|
55
|
+
def previous_offset?
|
56
|
+
!!@previous_offset
|
57
|
+
end
|
58
|
+
|
59
|
+
# @return [Boolean] We show current label with offsets that are present on the given
|
60
|
+
# page
|
61
|
+
def current_offset?
|
62
|
+
true
|
63
|
+
end
|
64
|
+
|
65
|
+
# @return [Boolean] move to the next page if not false. False indicates, that there is
|
66
|
+
# no next page to move to
|
67
|
+
def next_offset?
|
68
|
+
!!@next_offset
|
69
|
+
end
|
70
|
+
|
71
|
+
# If there is no next offset, we point to 0 as there should be no smaller offset than
|
72
|
+
# that in Kafka ever
|
73
|
+
# @return [Integer]
|
74
|
+
def next_offset
|
75
|
+
next_offset? ? @next_offset : 0
|
76
|
+
end
|
77
|
+
|
78
|
+
# @return [String] label of the current page. It is combined out of the first and
|
79
|
+
# the last offsets to show the range where we are. It will be empty if no offsets
|
80
|
+
# but this is not a problem as then we should not display pagination at all
|
81
|
+
def current_label
|
82
|
+
first = @visible_offsets.first
|
83
|
+
last = @visible_offsets.last
|
84
|
+
[first, last].compact.uniq.join(' - ').to_s
|
85
|
+
end
|
86
|
+
|
87
|
+
# @return [String] for offset based pagination we use the offset param name
|
88
|
+
def offset_key
|
89
|
+
'offset'
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Karafka
|
4
|
+
module Web
|
5
|
+
module Ui
|
6
|
+
module Lib
|
7
|
+
module Paginations
|
8
|
+
# Regular page-based pagination engine
|
9
|
+
class PageBased < Base
|
10
|
+
# @param current_offset [Integer] current page
|
11
|
+
# @param show_next_offset [Boolean] should we show next page
|
12
|
+
# (value is computed automatically)
|
13
|
+
def initialize(
|
14
|
+
current_offset,
|
15
|
+
show_next_offset
|
16
|
+
)
|
17
|
+
@previous_offset = current_offset - 1
|
18
|
+
@current_offset = current_offset
|
19
|
+
@next_offset = show_next_offset ? current_offset + 1 : false
|
20
|
+
super()
|
21
|
+
end
|
22
|
+
|
23
|
+
# Show pagination only when there is more than one page
|
24
|
+
# @return [Boolean]
|
25
|
+
def paginate?
|
26
|
+
@current_offset && (@current_offset > 1 || !!@next_offset)
|
27
|
+
end
|
28
|
+
|
29
|
+
# @return [Boolean] active the first page link when we are not on the first page
|
30
|
+
def first_offset?
|
31
|
+
@current_offset > 1
|
32
|
+
end
|
33
|
+
|
34
|
+
# @return [Boolean] first page for page based pagination is always empty as it moves us
|
35
|
+
# to the initial page so we do not include any page info
|
36
|
+
def first_offset
|
37
|
+
false
|
38
|
+
end
|
39
|
+
|
40
|
+
# @return [Boolean] Active previous page link when it is not the first page
|
41
|
+
def previous_offset?
|
42
|
+
@current_offset > 1
|
43
|
+
end
|
44
|
+
|
45
|
+
# @return [Boolean] always show current offset pagination value
|
46
|
+
def current_offset?
|
47
|
+
true
|
48
|
+
end
|
49
|
+
|
50
|
+
# @return [String] label of the current page
|
51
|
+
def current_label
|
52
|
+
@current_offset.to_s
|
53
|
+
end
|
54
|
+
|
55
|
+
# @return [Boolean] move to the next page if not false. False indicates, that there is
|
56
|
+
# no next page to move to
|
57
|
+
def next_offset?
|
58
|
+
@next_offset
|
59
|
+
end
|
60
|
+
|
61
|
+
# @return [String] for page pages pagination, always use page as the url value
|
62
|
+
def offset_key
|
63
|
+
'page'
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,33 @@
|
|
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
|
@@ -0,0 +1,23 @@
|
|
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
|
@@ -0,0 +1,52 @@
|
|
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
|