karafka-web 0.10.4 → 0.11.0.beta2

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.
Files changed (475) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/CHANGELOG.md +54 -176
  4. data/Gemfile +4 -0
  5. data/Gemfile.lock +87 -43
  6. data/LICENSE +6 -2
  7. data/bin/verify_kafka_warnings +35 -0
  8. data/bin/verify_topics_naming +35 -0
  9. data/config/locales/pro_errors.yml +1 -0
  10. data/docker-compose.yml +1 -1
  11. data/gulpfile.js +0 -2
  12. data/karafka-web.gemspec +2 -2
  13. data/lib/karafka/web/config.rb +80 -9
  14. data/lib/karafka/web/contracts/config.rb +44 -5
  15. data/lib/karafka/web/errors.rb +10 -12
  16. data/lib/karafka/web/management/actions/create_initial_states.rb +6 -6
  17. data/lib/karafka/web/management/actions/create_topics.rb +30 -64
  18. data/lib/karafka/web/management/actions/delete_topics.rb +5 -5
  19. data/lib/karafka/web/management/actions/enable.rb +5 -5
  20. data/lib/karafka/web/pro/commanding/commands/base.rb +37 -13
  21. data/lib/karafka/web/pro/commanding/commands/consumers/quiet.rb +33 -0
  22. data/lib/karafka/web/pro/commanding/commands/consumers/stop.rb +32 -0
  23. data/lib/karafka/web/pro/commanding/commands/consumers/trace.rb +37 -0
  24. data/lib/karafka/web/pro/commanding/commands/partitions/pause.rb +30 -0
  25. data/lib/karafka/web/pro/commanding/commands/partitions/resume.rb +30 -0
  26. data/lib/karafka/web/pro/commanding/commands/partitions/seek.rb +30 -0
  27. data/lib/karafka/web/pro/commanding/config.rb +6 -10
  28. data/lib/karafka/web/pro/commanding/contracts/config.rb +2 -10
  29. data/lib/karafka/web/pro/commanding/dispatcher.rb +45 -24
  30. data/lib/karafka/web/pro/commanding/handlers/partitions/commands/base.rb +67 -0
  31. data/lib/karafka/web/pro/commanding/handlers/partitions/commands/pause.rb +44 -0
  32. data/lib/karafka/web/pro/commanding/handlers/partitions/commands/resume.rb +29 -0
  33. data/lib/karafka/web/pro/commanding/handlers/partitions/commands/seek.rb +86 -0
  34. data/lib/karafka/web/pro/commanding/handlers/partitions/executor.rb +56 -0
  35. data/lib/karafka/web/pro/commanding/handlers/partitions/listener.rb +55 -0
  36. data/lib/karafka/web/pro/commanding/handlers/partitions/tracker.rb +62 -0
  37. data/lib/karafka/web/pro/commanding/listener.rb +4 -12
  38. data/lib/karafka/web/pro/commanding/manager.rb +36 -24
  39. data/lib/karafka/web/pro/commanding/matcher.rb +7 -17
  40. data/lib/karafka/web/pro/commanding/request.rb +39 -0
  41. data/lib/karafka/web/pro/commanding.rb +2 -10
  42. data/lib/karafka/web/pro/loader.rb +13 -10
  43. data/lib/karafka/web/pro/ui/app.rb +31 -390
  44. data/lib/karafka/web/pro/ui/controllers/base_controller.rb +8 -10
  45. data/lib/karafka/web/pro/ui/controllers/cluster_controller.rb +2 -10
  46. data/lib/karafka/web/pro/ui/controllers/consumers/base_controller.rb +21 -0
  47. data/lib/karafka/web/pro/ui/controllers/consumers/commanding_controller.rb +148 -0
  48. data/lib/karafka/web/pro/ui/controllers/consumers/commands_controller.rb +96 -0
  49. data/lib/karafka/web/pro/ui/controllers/consumers/consumers_controller.rb +99 -0
  50. data/lib/karafka/web/pro/ui/controllers/consumers/controls_controller.rb +36 -0
  51. data/lib/karafka/web/pro/ui/controllers/consumers/jobs_controller.rb +57 -0
  52. data/lib/karafka/web/pro/ui/controllers/consumers/partitions/base_controller.rb +86 -0
  53. data/lib/karafka/web/pro/ui/controllers/consumers/partitions/offsets_controller.rb +75 -0
  54. data/lib/karafka/web/pro/ui/controllers/consumers/partitions/pauses_controller.rb +110 -0
  55. data/lib/karafka/web/pro/ui/controllers/dashboard_controller.rb +2 -10
  56. data/lib/karafka/web/pro/ui/controllers/dlq_controller.rb +2 -10
  57. data/lib/karafka/web/pro/ui/controllers/errors_controller.rb +3 -11
  58. data/lib/karafka/web/pro/ui/controllers/explorer/base_controller.rb +21 -0
  59. data/lib/karafka/web/pro/ui/controllers/explorer/explorer_controller.rb +215 -0
  60. data/lib/karafka/web/pro/ui/controllers/explorer/messages_controller.rb +145 -0
  61. data/lib/karafka/web/pro/ui/controllers/explorer/search_controller.rb +68 -0
  62. data/lib/karafka/web/pro/ui/controllers/health_controller.rb +2 -10
  63. data/lib/karafka/web/pro/ui/controllers/jobs_controller.rb +2 -10
  64. data/lib/karafka/web/pro/ui/controllers/recurring_tasks_controller.rb +12 -13
  65. data/lib/karafka/web/pro/ui/controllers/routing_controller.rb +2 -10
  66. data/lib/karafka/web/pro/ui/controllers/scheduled_messages/base_controller.rb +2 -10
  67. data/lib/karafka/web/pro/ui/controllers/scheduled_messages/explorer_controller.rb +8 -16
  68. data/lib/karafka/web/pro/ui/controllers/scheduled_messages/messages_controller.rb +9 -15
  69. data/lib/karafka/web/pro/ui/controllers/scheduled_messages/schedules_controller.rb +2 -10
  70. data/lib/karafka/web/pro/ui/controllers/status_controller.rb +2 -10
  71. data/lib/karafka/web/pro/ui/controllers/support_controller.rb +2 -10
  72. data/lib/karafka/web/pro/ui/controllers/topics/base_controller.rb +21 -0
  73. data/lib/karafka/web/pro/ui/controllers/topics/configs_controller.rb +86 -0
  74. data/lib/karafka/web/pro/ui/controllers/topics/distributions_controller.rb +91 -0
  75. data/lib/karafka/web/pro/ui/controllers/topics/offsets_controller.rb +55 -0
  76. data/lib/karafka/web/pro/ui/controllers/topics/replications_controller.rb +37 -0
  77. data/lib/karafka/web/pro/ui/controllers/topics/topics_controller.rb +101 -0
  78. data/lib/karafka/web/pro/ui/controllers/ux_controller.rb +2 -10
  79. data/lib/karafka/web/pro/ui/lib/branding/config.rb +2 -10
  80. data/lib/karafka/web/pro/ui/lib/branding/contracts/config.rb +2 -10
  81. data/lib/karafka/web/pro/ui/lib/branding.rb +2 -10
  82. data/lib/karafka/web/pro/ui/lib/features.rb +53 -0
  83. data/lib/karafka/web/pro/ui/lib/patterns_detector.rb +2 -10
  84. data/lib/karafka/web/pro/ui/lib/policies/config.rb +2 -10
  85. data/lib/karafka/web/pro/ui/lib/policies/contracts/config.rb +2 -10
  86. data/lib/karafka/web/pro/ui/lib/policies/messages.rb +2 -10
  87. data/lib/karafka/web/pro/ui/lib/policies/requests.rb +2 -10
  88. data/lib/karafka/web/pro/ui/lib/policies.rb +2 -10
  89. data/lib/karafka/web/pro/ui/lib/safe_runner.rb +5 -0
  90. data/lib/karafka/web/pro/ui/lib/search/config.rb +2 -10
  91. data/lib/karafka/web/pro/ui/lib/search/contracts/config.rb +2 -10
  92. data/lib/karafka/web/pro/ui/lib/search/contracts/form.rb +2 -10
  93. data/lib/karafka/web/pro/ui/lib/search/matchers/base.rb +2 -10
  94. data/lib/karafka/web/pro/ui/lib/search/matchers/raw_header_includes.rb +10 -11
  95. data/lib/karafka/web/pro/ui/lib/search/matchers/raw_key_includes.rb +2 -10
  96. data/lib/karafka/web/pro/ui/lib/search/matchers/raw_payload_includes.rb +23 -11
  97. data/lib/karafka/web/pro/ui/lib/search/normalizer.rb +2 -10
  98. data/lib/karafka/web/pro/ui/lib/search/runner.rb +3 -11
  99. data/lib/karafka/web/pro/ui/lib/search.rb +2 -10
  100. data/lib/karafka/web/pro/ui/routes/base.rb +19 -0
  101. data/lib/karafka/web/pro/ui/routes/cluster.rb +37 -0
  102. data/lib/karafka/web/pro/ui/routes/consumers.rb +145 -0
  103. data/lib/karafka/web/pro/ui/routes/dashboard.rb +25 -0
  104. data/lib/karafka/web/pro/ui/routes/dlq.rb +24 -0
  105. data/lib/karafka/web/pro/ui/routes/errors.rb +39 -0
  106. data/lib/karafka/web/pro/ui/routes/explorer.rb +118 -0
  107. data/lib/karafka/web/pro/ui/routes/health.rb +47 -0
  108. data/lib/karafka/web/pro/ui/routes/jobs.rb +33 -0
  109. data/lib/karafka/web/pro/ui/routes/recurring_tasks.rb +59 -0
  110. data/lib/karafka/web/pro/ui/routes/routing.rb +31 -0
  111. data/lib/karafka/web/pro/ui/routes/scheduled_messages.rb +75 -0
  112. data/lib/karafka/web/pro/ui/routes/status.rb +24 -0
  113. data/lib/karafka/web/pro/ui/routes/support.rb +24 -0
  114. data/lib/karafka/web/pro/ui/routes/topics.rb +90 -0
  115. data/lib/karafka/web/pro/ui/routes/ux.rb +24 -0
  116. data/lib/karafka/web/pro/ui/views/cluster/_breadcrumbs.erb +3 -0
  117. data/lib/karafka/web/pro/ui/views/cluster/_broker.erb +3 -0
  118. data/lib/karafka/web/pro/ui/views/cluster/_config.erb +3 -0
  119. data/lib/karafka/web/pro/ui/views/cluster/_tabs.erb +3 -0
  120. data/lib/karafka/web/pro/ui/views/cluster/index.erb +4 -1
  121. data/lib/karafka/web/pro/ui/views/cluster/show.erb +3 -0
  122. data/lib/karafka/web/pro/ui/views/{commands → consumers/commands}/_backtrace.erb +3 -0
  123. data/lib/karafka/web/pro/ui/views/consumers/commands/_breadcrumbs.erb +24 -0
  124. data/lib/karafka/web/pro/ui/views/{commands → consumers/commands}/_command.erb +22 -6
  125. data/lib/karafka/web/pro/ui/views/consumers/commands/_command_details.erb +4 -0
  126. data/lib/karafka/web/pro/ui/views/consumers/commands/_empty.erb +6 -0
  127. data/lib/karafka/web/pro/ui/views/{commands → consumers/commands}/_incompatible_schema.erb +3 -0
  128. data/lib/karafka/web/pro/ui/views/{commands → consumers/commands}/_metadata.erb +4 -1
  129. data/lib/karafka/web/pro/ui/views/{commands → consumers/commands}/_table.erb +5 -2
  130. data/lib/karafka/web/pro/ui/views/{commands → consumers/commands}/index.erb +7 -4
  131. data/lib/karafka/web/pro/ui/views/consumers/commands/show.erb +32 -0
  132. data/lib/karafka/web/pro/ui/views/consumers/consumers/_breadcrumbs.erb +46 -0
  133. data/lib/karafka/web/pro/ui/views/consumers/{_consumer.erb → consumers/_consumer.erb} +4 -1
  134. data/lib/karafka/web/pro/ui/views/consumers/{_consumer_performance.erb → consumers/_consumer_performance.erb} +4 -1
  135. data/lib/karafka/web/pro/ui/views/consumers/consumers/_tabs.erb +38 -0
  136. data/lib/karafka/web/pro/ui/views/consumers/consumers/consumer/_commands.erb +80 -0
  137. data/lib/karafka/web/pro/ui/views/consumers/consumers/consumer/_consumer_group.erb +11 -0
  138. data/lib/karafka/web/pro/ui/views/consumers/{consumer → consumers/consumer}/_metrics.erb +3 -0
  139. data/lib/karafka/web/pro/ui/views/consumers/consumers/consumer/_no_subscriptions.erb +10 -0
  140. data/lib/karafka/web/pro/ui/views/consumers/{consumer → consumers/consumer}/_partition.erb +16 -0
  141. data/lib/karafka/web/pro/ui/views/consumers/consumers/consumer/_partition_edit_options.erb +33 -0
  142. data/lib/karafka/web/pro/ui/views/consumers/{consumer → consumers/consumer}/_stopped.erb +3 -0
  143. data/lib/karafka/web/pro/ui/views/consumers/{consumer → consumers/consumer}/_subscription_group.erb +7 -3
  144. data/lib/karafka/web/pro/ui/views/consumers/{consumer → consumers/consumer}/_tabs.erb +7 -4
  145. data/lib/karafka/web/pro/ui/views/consumers/consumers/details.erb +15 -0
  146. data/lib/karafka/web/pro/ui/views/consumers/{index.erb → consumers/index.erb} +6 -3
  147. data/lib/karafka/web/pro/ui/views/consumers/{performance.erb → consumers/performance.erb} +6 -3
  148. data/lib/karafka/web/pro/ui/views/consumers/consumers/subscriptions.erb +24 -0
  149. data/lib/karafka/web/pro/ui/views/consumers/controls/_breadcrumbs.erb +16 -0
  150. data/lib/karafka/web/pro/ui/views/consumers/{_consumer_controls.erb → controls/_controls.erb} +10 -7
  151. data/lib/karafka/web/pro/ui/views/consumers/{controls.erb → controls/index.erb} +8 -5
  152. data/lib/karafka/web/pro/ui/views/consumers/jobs/_breadcrumbs.erb +36 -0
  153. data/lib/karafka/web/pro/ui/views/consumers/{consumer → jobs}/_job.erb +3 -0
  154. data/lib/karafka/web/pro/ui/views/consumers/{consumer → jobs}/_no_jobs.erb +3 -0
  155. data/lib/karafka/web/pro/ui/views/consumers/{pending_jobs.erb → jobs/pending.erb} +7 -8
  156. data/lib/karafka/web/pro/ui/views/consumers/{running_jobs.erb → jobs/running.erb} +7 -8
  157. data/lib/karafka/web/pro/ui/views/consumers/partitions/offsets/_basics.erb +77 -0
  158. data/lib/karafka/web/pro/ui/views/consumers/partitions/offsets/_breadcrumbs.erb +58 -0
  159. data/lib/karafka/web/pro/ui/views/consumers/partitions/offsets/_form.erb +109 -0
  160. data/lib/karafka/web/pro/ui/views/consumers/partitions/offsets/_not_running_error.erb +16 -0
  161. data/lib/karafka/web/pro/ui/views/consumers/partitions/offsets/_running_warning.erb +15 -0
  162. data/lib/karafka/web/pro/ui/views/consumers/partitions/offsets/edit.erb +12 -0
  163. data/lib/karafka/web/pro/ui/views/consumers/partitions/pauses/_active_not_editable.erb +22 -0
  164. data/lib/karafka/web/pro/ui/views/consumers/partitions/pauses/_adjusting_warning.erb +27 -0
  165. data/lib/karafka/web/pro/ui/views/consumers/partitions/pauses/_breadcrumbs.erb +60 -0
  166. data/lib/karafka/web/pro/ui/views/consumers/partitions/pauses/_edit_form.erb +59 -0
  167. data/lib/karafka/web/pro/ui/views/consumers/partitions/pauses/_lrj_not_manageable.erb +24 -0
  168. data/lib/karafka/web/pro/ui/views/consumers/partitions/pauses/_new_form.erb +78 -0
  169. data/lib/karafka/web/pro/ui/views/consumers/partitions/pauses/_not_running.erb +16 -0
  170. data/lib/karafka/web/pro/ui/views/consumers/partitions/pauses/edit.erb +20 -0
  171. data/lib/karafka/web/pro/ui/views/consumers/partitions/pauses/new.erb +16 -0
  172. data/lib/karafka/web/pro/ui/views/dashboard/index.erb +4 -1
  173. data/lib/karafka/web/pro/ui/views/dlq/_breadcrumbs.erb +3 -0
  174. data/lib/karafka/web/pro/ui/views/dlq/_no_topics.erb +3 -0
  175. data/lib/karafka/web/pro/ui/views/dlq/_topic.erb +4 -1
  176. data/lib/karafka/web/pro/ui/views/dlq/index.erb +3 -0
  177. data/lib/karafka/web/pro/ui/views/errors/_breadcrumbs.erb +3 -0
  178. data/lib/karafka/web/pro/ui/views/errors/_error.erb +3 -0
  179. data/lib/karafka/web/pro/ui/views/errors/_partition_option.erb +3 -0
  180. data/lib/karafka/web/pro/ui/views/errors/_selector.erb +3 -0
  181. data/lib/karafka/web/pro/ui/views/errors/_table.erb +4 -1
  182. data/lib/karafka/web/pro/ui/views/errors/index.erb +6 -3
  183. data/lib/karafka/web/pro/ui/views/errors/partition.erb +5 -2
  184. data/lib/karafka/web/pro/ui/views/errors/show.erb +3 -0
  185. data/lib/karafka/web/pro/ui/views/explorer/{_breadcrumbs.erb → explorer/_breadcrumbs.erb} +7 -4
  186. data/lib/karafka/web/pro/ui/views/explorer/{_failed_deserialization.erb → explorer/_failed_deserialization.erb} +3 -0
  187. data/lib/karafka/web/pro/ui/views/explorer/{_filtered.erb → explorer/_filtered.erb} +3 -0
  188. data/lib/karafka/web/pro/ui/views/explorer/{_message.erb → explorer/_message.erb} +4 -1
  189. data/lib/karafka/web/pro/ui/views/explorer/explorer/_no_topics.erb +4 -0
  190. data/lib/karafka/web/pro/ui/views/explorer/{_partition_option.erb → explorer/_partition_option.erb} +4 -1
  191. data/lib/karafka/web/pro/ui/views/explorer/{_selector.erb → explorer/_selector.erb} +4 -1
  192. data/lib/karafka/web/pro/ui/views/explorer/explorer/_topic.erb +13 -0
  193. data/lib/karafka/web/pro/ui/views/explorer/explorer/index.erb +17 -0
  194. data/lib/karafka/web/pro/ui/views/explorer/{message → explorer/message}/_metadata.erb +10 -7
  195. data/lib/karafka/web/pro/ui/views/explorer/{message → explorer/message}/_payload.erb +6 -3
  196. data/lib/karafka/web/pro/ui/views/explorer/{message → explorer/message}/_resources_utilization.erb +7 -4
  197. data/lib/karafka/web/pro/ui/views/explorer/{message → explorer/message}/_too_big_to_be_displayed.erb +3 -0
  198. data/lib/karafka/web/pro/ui/views/explorer/{messages → explorer/messages}/_detail.erb +3 -0
  199. data/lib/karafka/web/pro/ui/views/explorer/explorer/messages/_headers.erb +51 -0
  200. data/lib/karafka/web/pro/ui/views/explorer/{messages → explorer/messages}/_key.erb +3 -0
  201. data/lib/karafka/web/pro/ui/views/explorer/explorer/partition/_cleaned.erb +6 -0
  202. data/lib/karafka/web/pro/ui/views/explorer/explorer/partition/_empty.erb +6 -0
  203. data/lib/karafka/web/pro/ui/views/explorer/{partition → explorer/partition}/_messages.erb +4 -1
  204. data/lib/karafka/web/pro/ui/views/explorer/explorer/partition/_time_selector.erb +16 -0
  205. data/lib/karafka/web/pro/ui/views/explorer/explorer/partition/_timestamp_selector.erb +33 -0
  206. data/lib/karafka/web/pro/ui/views/explorer/{partition.erb → explorer/partition.erb} +24 -17
  207. data/lib/karafka/web/pro/ui/views/explorer/{show.erb → explorer/show.erb} +17 -19
  208. data/lib/karafka/web/pro/ui/views/explorer/{topic → explorer/topic}/_actions.erb +5 -2
  209. data/lib/karafka/web/pro/ui/views/explorer/explorer/topic/_empty.erb +6 -0
  210. data/lib/karafka/web/pro/ui/views/explorer/{topic → explorer/topic}/_limited.erb +3 -0
  211. data/lib/karafka/web/pro/ui/views/explorer/{topic.erb → explorer/topic.erb} +7 -4
  212. data/lib/karafka/web/pro/ui/views/explorer/messages/_breadcrumbs.erb +32 -0
  213. data/lib/karafka/web/pro/ui/views/explorer/messages/forward.erb +143 -0
  214. data/lib/karafka/web/pro/ui/views/explorer/search/_breadcrumbs.erb +4 -0
  215. data/lib/karafka/web/pro/ui/views/explorer/search/_fix_errors.erb +6 -0
  216. data/lib/karafka/web/pro/ui/views/{search → explorer/search}/_metadata.erb +3 -0
  217. data/lib/karafka/web/pro/ui/views/explorer/search/_no_results.erb +6 -0
  218. data/lib/karafka/web/pro/ui/views/{search → explorer/search}/_no_search_criteria.erb +3 -0
  219. data/lib/karafka/web/pro/ui/views/{search → explorer/search}/_search_criteria.erb +3 -0
  220. data/lib/karafka/web/pro/ui/views/{search → explorer/search}/_search_modal.erb +5 -2
  221. data/lib/karafka/web/pro/ui/views/explorer/search/_timeout.erb +6 -0
  222. data/lib/karafka/web/pro/ui/views/explorer/search/index.erb +32 -0
  223. data/lib/karafka/web/pro/ui/views/health/_breadcrumbs.erb +3 -0
  224. data/lib/karafka/web/pro/ui/views/health/_no_data.erb +3 -0
  225. data/lib/karafka/web/pro/ui/views/health/_partition.erb +16 -1
  226. data/lib/karafka/web/pro/ui/views/health/_partition_lags.erb +3 -0
  227. data/lib/karafka/web/pro/ui/views/health/_partition_offset.erb +3 -0
  228. data/lib/karafka/web/pro/ui/views/health/_partition_times.erb +3 -0
  229. data/lib/karafka/web/pro/ui/views/health/_table_metadata.erb +4 -1
  230. data/lib/karafka/web/pro/ui/views/health/_tabs.erb +3 -0
  231. data/lib/karafka/web/pro/ui/views/health/changes.erb +4 -1
  232. data/lib/karafka/web/pro/ui/views/health/cluster_lags.erb +3 -0
  233. data/lib/karafka/web/pro/ui/views/health/lags.erb +5 -2
  234. data/lib/karafka/web/pro/ui/views/health/offsets.erb +4 -1
  235. data/lib/karafka/web/pro/ui/views/health/overview.erb +8 -3
  236. data/lib/karafka/web/pro/ui/views/jobs/_job.erb +4 -1
  237. data/lib/karafka/web/pro/ui/views/jobs/_no_jobs.erb +3 -0
  238. data/lib/karafka/web/pro/ui/views/jobs/pending.erb +4 -1
  239. data/lib/karafka/web/pro/ui/views/jobs/running.erb +4 -1
  240. data/lib/karafka/web/pro/ui/views/recurring_tasks/_actions.erb +3 -0
  241. data/lib/karafka/web/pro/ui/views/recurring_tasks/_batch_actions.erb +3 -0
  242. data/lib/karafka/web/pro/ui/views/recurring_tasks/_breadcrumbs.erb +3 -0
  243. data/lib/karafka/web/pro/ui/views/recurring_tasks/_log.erb +3 -0
  244. data/lib/karafka/web/pro/ui/views/recurring_tasks/_not_active.erb +3 -0
  245. data/lib/karafka/web/pro/ui/views/recurring_tasks/_tabs.erb +3 -0
  246. data/lib/karafka/web/pro/ui/views/recurring_tasks/_task.erb +3 -0
  247. data/lib/karafka/web/pro/ui/views/recurring_tasks/logs.erb +3 -0
  248. data/lib/karafka/web/pro/ui/views/recurring_tasks/schedule.erb +3 -0
  249. data/lib/karafka/web/pro/ui/views/routing/_consumer_group.erb +3 -0
  250. data/lib/karafka/web/pro/ui/views/routing/_detail.erb +3 -0
  251. data/lib/karafka/web/pro/ui/views/routing/_topic.erb +3 -0
  252. data/lib/karafka/web/pro/ui/views/routing/index.erb +3 -0
  253. data/lib/karafka/web/pro/ui/views/routing/show.erb +3 -0
  254. data/lib/karafka/web/pro/ui/views/scheduled_messages/explorer/_breadcrumbs.erb +6 -3
  255. data/lib/karafka/web/pro/ui/views/scheduled_messages/explorer/_key.erb +3 -0
  256. data/lib/karafka/web/pro/ui/views/scheduled_messages/explorer/_message.erb +4 -1
  257. data/lib/karafka/web/pro/ui/views/scheduled_messages/explorer/_messages.erb +3 -0
  258. data/lib/karafka/web/pro/ui/views/scheduled_messages/explorer/partition.erb +23 -16
  259. data/lib/karafka/web/pro/ui/views/scheduled_messages/explorer/topic.erb +6 -3
  260. data/lib/karafka/web/pro/ui/views/scheduled_messages/schedules/_breadcrumbs.erb +3 -0
  261. data/lib/karafka/web/pro/ui/views/scheduled_messages/schedules/_no_groups.erb +3 -0
  262. data/lib/karafka/web/pro/ui/views/scheduled_messages/schedules/index.erb +4 -1
  263. data/lib/karafka/web/pro/ui/views/scheduled_messages/schedules/show.erb +3 -0
  264. data/lib/karafka/web/pro/ui/views/shared/_navigation.erb +25 -17
  265. data/lib/karafka/web/pro/ui/views/shared/_rdkafka_form_error_alert_box.erb +16 -0
  266. data/lib/karafka/web/pro/ui/views/shared/branding/_label.erb +3 -0
  267. data/lib/karafka/web/pro/ui/views/shared/branding/_notice.erb +3 -0
  268. data/lib/karafka/web/pro/ui/views/topics/configs/_breadcrumbs.erb +34 -0
  269. data/lib/karafka/web/pro/ui/views/topics/configs/_config.erb +26 -0
  270. data/lib/karafka/web/pro/ui/views/topics/configs/_delete_button.erb +13 -0
  271. data/lib/karafka/web/pro/ui/views/topics/configs/_edit_form.erb +50 -0
  272. data/lib/karafka/web/pro/ui/views/topics/configs/_edit_plan.erb +16 -0
  273. data/lib/karafka/web/pro/ui/views/topics/configs/_edit_warning.erb +12 -0
  274. data/lib/karafka/web/pro/ui/views/topics/configs/edit.erb +16 -0
  275. data/lib/karafka/web/pro/ui/views/topics/{config.erb → configs/index.erb} +9 -3
  276. data/lib/karafka/web/pro/ui/views/topics/distributions/_add_partitions_button.erb +13 -0
  277. data/lib/karafka/web/pro/ui/views/topics/{distribution → distributions}/_badges.erb +3 -0
  278. data/lib/karafka/web/pro/ui/views/topics/distributions/_breadcrumbs.erb +28 -0
  279. data/lib/karafka/web/pro/ui/views/topics/{distribution → distributions}/_chart.erb +3 -0
  280. data/lib/karafka/web/pro/ui/views/topics/distributions/_edit_form.erb +47 -0
  281. data/lib/karafka/web/pro/ui/views/topics/distributions/_edit_hints.erb +15 -0
  282. data/lib/karafka/web/pro/ui/views/topics/distributions/_edit_warnings.erb +14 -0
  283. data/lib/karafka/web/pro/ui/views/topics/distributions/_empty_partitions.erb +4 -0
  284. data/lib/karafka/web/pro/ui/views/topics/{distribution → distributions}/_limited.erb +3 -0
  285. data/lib/karafka/web/pro/ui/views/topics/distributions/_partition.erb +13 -0
  286. data/lib/karafka/web/pro/ui/views/topics/distributions/edit.erb +16 -0
  287. data/lib/karafka/web/pro/ui/views/topics/{distribution.erb → distributions/show.erb} +11 -7
  288. data/lib/karafka/web/pro/ui/views/topics/offsets/_breadcrumbs.erb +20 -0
  289. data/lib/karafka/web/pro/ui/views/topics/offsets/_partition.erb +13 -0
  290. data/lib/karafka/web/pro/ui/views/topics/{offsets.erb → offsets/show.erb} +6 -3
  291. data/lib/karafka/web/pro/ui/views/topics/replications/_breadcrumbs.erb +20 -0
  292. data/lib/karafka/web/pro/ui/views/topics/{_partition.erb → replications/_partition.erb} +4 -1
  293. data/lib/karafka/web/pro/ui/views/topics/{replication.erb → replications/show.erb} +6 -3
  294. data/lib/karafka/web/pro/ui/views/topics/topics/_breadcrumbs.erb +32 -0
  295. data/lib/karafka/web/pro/ui/views/topics/topics/_create_button.erb +13 -0
  296. data/lib/karafka/web/pro/ui/views/topics/topics/_create_hints.erb +15 -0
  297. data/lib/karafka/web/pro/ui/views/topics/topics/_delete_form.erb +36 -0
  298. data/lib/karafka/web/pro/ui/views/topics/topics/_delete_hints.erb +15 -0
  299. data/lib/karafka/web/pro/ui/views/topics/topics/_delete_warning.erb +13 -0
  300. data/lib/karafka/web/pro/ui/views/topics/topics/_new_form.erb +80 -0
  301. data/lib/karafka/web/pro/ui/views/topics/{_tabs.erb → topics/_tabs.erb} +7 -4
  302. data/lib/karafka/web/pro/ui/views/topics/topics/_topic.erb +12 -0
  303. data/lib/karafka/web/pro/ui/views/topics/topics/edit.erb +10 -0
  304. data/lib/karafka/web/pro/ui/views/topics/topics/index.erb +19 -0
  305. data/lib/karafka/web/pro/ui/views/topics/topics/new.erb +12 -0
  306. data/lib/karafka/web/processing/consumer.rb +7 -7
  307. data/lib/karafka/web/processing/consumers/aggregators/state.rb +14 -14
  308. data/lib/karafka/web/processing/consumers/metrics.rb +1 -1
  309. data/lib/karafka/web/processing/consumers/state.rb +1 -1
  310. data/lib/karafka/web/processing/publisher.rb +4 -4
  311. data/lib/karafka/web/tracking/consumers/contracts/partition.rb +1 -0
  312. data/lib/karafka/web/tracking/consumers/listeners/pausing.rb +2 -2
  313. data/lib/karafka/web/tracking/consumers/listeners/transactions.rb +44 -0
  314. data/lib/karafka/web/tracking/consumers/reporter.rb +2 -2
  315. data/lib/karafka/web/tracking/consumers/sampler.rb +81 -14
  316. data/lib/karafka/web/tracking/helpers/sysconf.rb +33 -0
  317. data/lib/karafka/web/tracking/producers/reporter.rb +1 -1
  318. data/lib/karafka/web/ui/app.rb +19 -112
  319. data/lib/karafka/web/ui/base.rb +58 -3
  320. data/lib/karafka/web/ui/controllers/base_controller.rb +43 -1
  321. data/lib/karafka/web/ui/controllers/cluster_controller.rb +5 -2
  322. data/lib/karafka/web/ui/controllers/errors_controller.rb +1 -1
  323. data/lib/karafka/web/ui/controllers/requests/execution_wrapper.rb +52 -0
  324. data/lib/karafka/web/ui/controllers/requests/hookable.rb +99 -0
  325. data/lib/karafka/web/ui/controllers/requests/params.rb +39 -1
  326. data/lib/karafka/web/ui/controllers/responses/redirect.rb +0 -5
  327. data/lib/karafka/web/ui/controllers/status_controller.rb +3 -0
  328. data/lib/karafka/web/ui/helpers/application_helper.rb +10 -1
  329. data/lib/karafka/web/ui/helpers/paths_helper.rb +54 -10
  330. data/lib/karafka/web/ui/lib/admin.rb +1 -1
  331. data/lib/karafka/web/ui/lib/cache.rb +135 -0
  332. data/lib/karafka/web/ui/models/broker.rb +1 -2
  333. data/lib/karafka/web/ui/models/cluster_info.rb +15 -21
  334. data/lib/karafka/web/ui/models/consumers_metrics.rb +1 -1
  335. data/lib/karafka/web/ui/models/consumers_state.rb +1 -1
  336. data/lib/karafka/web/ui/models/counters.rb +1 -1
  337. data/lib/karafka/web/ui/models/health.rb +9 -7
  338. data/lib/karafka/web/ui/models/process.rb +16 -0
  339. data/lib/karafka/web/ui/models/processes.rb +29 -8
  340. data/lib/karafka/web/ui/models/recurring_tasks/schedule.rb +1 -1
  341. data/lib/karafka/web/ui/models/status.rb +28 -9
  342. data/lib/karafka/web/ui/models/topic.rb +1 -2
  343. data/lib/karafka/web/ui/public/javascripts/application.js +8 -98
  344. data/lib/karafka/web/ui/public/javascripts/application.min.js +12 -4
  345. data/lib/karafka/web/ui/public/javascripts/application.min.js.br +0 -0
  346. data/lib/karafka/web/ui/public/javascripts/application.min.js.gz +0 -0
  347. data/lib/karafka/web/ui/public/javascripts/components/action_confirmation_manager.js +30 -0
  348. data/lib/karafka/web/ui/public/javascripts/components/alerts.js +39 -0
  349. data/lib/karafka/web/ui/public/javascripts/components/button_lock_manager.js +50 -0
  350. data/lib/karafka/web/ui/public/javascripts/components/live_poll.js +71 -19
  351. data/lib/karafka/web/ui/public/javascripts/components/message_republish_manager.js +50 -0
  352. data/lib/karafka/web/ui/public/javascripts/components/page_title_tracker.js +21 -0
  353. data/lib/karafka/web/ui/public/javascripts/components/partition_redirect_manager.js +21 -0
  354. data/lib/karafka/web/ui/public/javascripts/components/time_ago_manager.js +25 -0
  355. data/lib/karafka/web/ui/public/javascripts/components/timestamp_selector.js +30 -0
  356. data/lib/karafka/web/ui/public/javascripts/libs/datepicker.js +2 -2
  357. data/lib/karafka/web/ui/public/stylesheets/application.css +30 -0
  358. data/lib/karafka/web/ui/public/stylesheets/application.min.css +5122 -13
  359. data/lib/karafka/web/ui/public/stylesheets/application.min.css.br +0 -0
  360. data/lib/karafka/web/ui/public/stylesheets/application.min.css.gz +0 -0
  361. data/lib/karafka/web/ui/public/stylesheets/libs/highlight_dark.min.css.gz +0 -0
  362. data/lib/karafka/web/ui/public/stylesheets/libs/highlight_light.min.css.gz +0 -0
  363. data/lib/karafka/web/ui/public/stylesheets/libs/tailwind.css +507 -214
  364. data/lib/karafka/web/ui/routes/assets.rb +53 -0
  365. data/lib/karafka/web/ui/routes/base.rb +36 -0
  366. data/lib/karafka/web/ui/routes/cluster.rb +28 -0
  367. data/lib/karafka/web/ui/routes/consumers.rb +35 -0
  368. data/lib/karafka/web/ui/routes/dashboard.rb +20 -0
  369. data/lib/karafka/web/ui/routes/errors.rb +26 -0
  370. data/lib/karafka/web/ui/routes/jobs.rb +28 -0
  371. data/lib/karafka/web/ui/routes/pro_only.rb +27 -0
  372. data/lib/karafka/web/ui/routes/routing.rb +26 -0
  373. data/lib/karafka/web/ui/routes/status.rb +19 -0
  374. data/lib/karafka/web/ui/routes/support.rb +19 -0
  375. data/lib/karafka/web/ui/routes/ux.rb +19 -0
  376. data/lib/karafka/web/ui/views/cluster/_partition.erb +2 -2
  377. data/lib/karafka/web/ui/views/cluster/brokers.erb +1 -1
  378. data/lib/karafka/web/ui/views/consumers/_breadcrumbs.erb +7 -1
  379. data/lib/karafka/web/ui/views/consumers/_consumer.erb +1 -1
  380. data/lib/karafka/web/ui/views/consumers/_no_consumers.erb +2 -2
  381. data/lib/karafka/web/ui/views/consumers/_tabs.erb +4 -4
  382. data/lib/karafka/web/ui/views/consumers/index.erb +1 -1
  383. data/lib/karafka/web/ui/views/dashboard/_feature_pro.erb +1 -1
  384. data/lib/karafka/web/ui/views/dashboard/_not_enough_data.erb +2 -2
  385. data/lib/karafka/web/ui/views/dashboard/_ranges_selector.erb +1 -1
  386. data/lib/karafka/web/ui/views/dashboard/index.erb +6 -49
  387. data/lib/karafka/web/ui/views/errors/_detail.erb +3 -3
  388. data/lib/karafka/web/ui/views/errors/index.erb +1 -1
  389. data/lib/karafka/web/ui/views/jobs/_job.erb +1 -1
  390. data/lib/karafka/web/ui/views/jobs/pending.erb +1 -1
  391. data/lib/karafka/web/ui/views/jobs/running.erb +1 -1
  392. data/lib/karafka/web/ui/views/layout.erb +7 -5
  393. data/lib/karafka/web/ui/views/shared/_become_pro.erb +1 -1
  394. data/lib/karafka/web/ui/views/shared/_brand.erb +1 -1
  395. data/lib/karafka/web/ui/views/shared/_breadcrumbs.erb +1 -1
  396. data/lib/karafka/web/ui/views/shared/_content.erb +1 -1
  397. data/lib/karafka/web/ui/views/shared/_controls.erb +10 -3
  398. data/lib/karafka/web/ui/views/shared/_custom_nav.erb +9 -0
  399. data/lib/karafka/web/ui/views/shared/_flashes.erb +3 -5
  400. data/lib/karafka/web/ui/views/shared/_header.erb +25 -2
  401. data/lib/karafka/web/ui/views/shared/_navigation.erb +17 -15
  402. data/lib/karafka/web/ui/views/shared/alerts/_error.erb +8 -0
  403. data/lib/karafka/web/ui/views/shared/alerts/_info.erb +8 -0
  404. data/lib/karafka/web/ui/views/shared/alerts/_primary.erb +8 -0
  405. data/lib/karafka/web/ui/views/shared/alerts/_secondary.erb +8 -0
  406. data/lib/karafka/web/ui/views/shared/alerts/_success.erb +8 -0
  407. data/lib/karafka/web/ui/views/shared/alerts/_warning.erb +8 -0
  408. data/lib/karafka/web/ui/views/shared/exceptions/not_allowed.erb +4 -0
  409. data/lib/karafka/web/ui/views/shared/exceptions/not_found.erb +5 -1
  410. data/lib/karafka/web/ui/views/shared/exceptions/pro_only.erb +4 -0
  411. data/lib/karafka/web/ui/views/shared/icons/_arrow_left.erb +3 -0
  412. data/lib/karafka/web/ui/views/shared/icons/_arrow_up_tray.erb +3 -0
  413. data/lib/karafka/web/ui/views/shared/icons/_clock.erb +3 -0
  414. data/lib/karafka/web/ui/views/shared/icons/_pencil.erb +3 -0
  415. data/lib/karafka/web/ui/views/shared/icons/_pencil_square.erb +3 -0
  416. data/lib/karafka/web/ui/views/shared/icons/_play_pause.erb +3 -0
  417. data/lib/karafka/web/ui/views/shared/icons/_plus.erb +3 -0
  418. data/lib/karafka/web/ui/views/shared/icons/_trash.erb +3 -0
  419. data/lib/karafka/web/ui/views/status/failures/_live_reporting.erb +1 -1
  420. data/lib/karafka/web/ui/views/status/failures/_partitions.erb +3 -3
  421. data/lib/karafka/web/ui/views/status/failures/_state_calculation.erb +2 -2
  422. data/lib/karafka/web/ui/views/status/info/_components.erb +6 -6
  423. data/lib/karafka/web/ui/views/status/show.erb +15 -0
  424. data/lib/karafka/web/ui/views/status/warnings/_consumers_schemas.erb +31 -0
  425. data/lib/karafka/web/ui/views/ux/_icons.erb +1 -1
  426. data/lib/karafka/web/version.rb +1 -1
  427. data/lib/karafka/web.rb +3 -0
  428. data/package-lock.json +776 -1208
  429. data/package.json +3 -4
  430. data/postcss.config.js +1 -2
  431. data/renovate.json +13 -1
  432. data/tailwind.config.js +0 -4
  433. data.tar.gz.sig +0 -0
  434. metadata +225 -106
  435. metadata.gz.sig +0 -0
  436. data/lib/karafka/web/pro/commanding/commands/quiet.rb +0 -34
  437. data/lib/karafka/web/pro/commanding/commands/stop.rb +0 -34
  438. data/lib/karafka/web/pro/commanding/commands/trace.rb +0 -41
  439. data/lib/karafka/web/pro/ui/controllers/commanding_controller.rb +0 -118
  440. data/lib/karafka/web/pro/ui/controllers/commands_controller.rb +0 -96
  441. data/lib/karafka/web/pro/ui/controllers/consumers_controller.rb +0 -138
  442. data/lib/karafka/web/pro/ui/controllers/explorer_controller.rb +0 -220
  443. data/lib/karafka/web/pro/ui/controllers/messages_controller.rb +0 -107
  444. data/lib/karafka/web/pro/ui/controllers/search_controller.rb +0 -73
  445. data/lib/karafka/web/pro/ui/controllers/topics_controller.rb +0 -130
  446. data/lib/karafka/web/pro/ui/views/commands/_breadcrumbs.erb +0 -21
  447. data/lib/karafka/web/pro/ui/views/commands/_command_details.erb +0 -1
  448. data/lib/karafka/web/pro/ui/views/commands/_empty.erb +0 -3
  449. data/lib/karafka/web/pro/ui/views/commands/show.erb +0 -33
  450. data/lib/karafka/web/pro/ui/views/consumers/_breadcrumbs.erb +0 -55
  451. data/lib/karafka/web/pro/ui/views/consumers/_tabs.erb +0 -33
  452. data/lib/karafka/web/pro/ui/views/consumers/consumer/_commands.erb +0 -72
  453. data/lib/karafka/web/pro/ui/views/consumers/consumer/_consumer_group.erb +0 -8
  454. data/lib/karafka/web/pro/ui/views/consumers/consumer/_no_subscriptions.erb +0 -7
  455. data/lib/karafka/web/pro/ui/views/consumers/details.erb +0 -13
  456. data/lib/karafka/web/pro/ui/views/consumers/subscriptions.erb +0 -25
  457. data/lib/karafka/web/pro/ui/views/explorer/_no_topics.erb +0 -1
  458. data/lib/karafka/web/pro/ui/views/explorer/_topic.erb +0 -10
  459. data/lib/karafka/web/pro/ui/views/explorer/index.erb +0 -14
  460. data/lib/karafka/web/pro/ui/views/explorer/messages/_headers.erb +0 -33
  461. data/lib/karafka/web/pro/ui/views/explorer/partition/_cleaned.erb +0 -3
  462. data/lib/karafka/web/pro/ui/views/explorer/partition/_empty.erb +0 -3
  463. data/lib/karafka/web/pro/ui/views/explorer/topic/_empty.erb +0 -3
  464. data/lib/karafka/web/pro/ui/views/search/_breadcrumbs.erb +0 -1
  465. data/lib/karafka/web/pro/ui/views/search/_fix_errors.erb +0 -3
  466. data/lib/karafka/web/pro/ui/views/search/_no_results.erb +0 -3
  467. data/lib/karafka/web/pro/ui/views/search/_timeout.erb +0 -3
  468. data/lib/karafka/web/pro/ui/views/search/index.erb +0 -29
  469. data/lib/karafka/web/pro/ui/views/topics/_breadcrumbs.erb +0 -45
  470. data/lib/karafka/web/pro/ui/views/topics/_partition_offsets.erb +0 -10
  471. data/lib/karafka/web/pro/ui/views/topics/_topic.erb +0 -9
  472. data/lib/karafka/web/pro/ui/views/topics/distribution/_empty_partitions.erb +0 -1
  473. data/lib/karafka/web/pro/ui/views/topics/distribution/_partition.erb +0 -10
  474. data/lib/karafka/web/pro/ui/views/topics/index.erb +0 -14
  475. data/lib/karafka/web/ui/lib/ttl_cache.rb +0 -82
@@ -36,7 +36,7 @@ module Karafka
36
36
  def add(report, offset)
37
37
  super(report)
38
38
  increment_total_counters(report)
39
- update_process_state(report, offset)
39
+ add_state(report, offset)
40
40
  # We always evict after counters updates because we want to use expired (stopped)
41
41
  # data for counters as it was valid previously. This can happen only when web consumer
42
42
  # had a lag and is catching up.
@@ -46,6 +46,19 @@ module Karafka
46
46
  refresh_current_stats
47
47
  end
48
48
 
49
+ # Registers or updates the given process state based on the report
50
+ #
51
+ # @param report [Hash]
52
+ # @param offset [Integer]
53
+ def add_state(report, offset)
54
+ process_id = report[:process][:id]
55
+
56
+ state[:processes][process_id] = {
57
+ dispatched_at: report[:dispatched_at],
58
+ offset: offset
59
+ }
60
+ end
61
+
49
62
  # @return [Array<Hash, Float>] aggregated current stats value and time from which this
50
63
  # aggregation comes from
51
64
  #
@@ -84,19 +97,6 @@ module Karafka
84
97
  end
85
98
  end
86
99
 
87
- # Registers or updates the given process state based on the report
88
- #
89
- # @param report [Hash]
90
- # @param offset [Integer]
91
- def update_process_state(report, offset)
92
- process_id = report[:process][:id]
93
-
94
- state[:processes][process_id] = {
95
- dispatched_at: report[:dispatched_at],
96
- offset: offset
97
- }
98
- end
99
-
100
100
  # Evicts expired processes from the current state
101
101
  # We consider processes dead if they do not report often enough
102
102
  # @note We do not evict based on states (stopped), because we want to report the
@@ -12,7 +12,7 @@ module Karafka
12
12
  # @return [Hash] latest (current) aggregated metrics state
13
13
  def current!
14
14
  metrics_message = ::Karafka::Admin.read_topic(
15
- Karafka::Web.config.topics.consumers.metrics,
15
+ Karafka::Web.config.topics.consumers.metrics.name,
16
16
  0,
17
17
  # We need to take more in case there would be transactions running.
18
18
  # In theory we could take two but this compensates for any involuntary
@@ -12,7 +12,7 @@ module Karafka
12
12
  # @return [Hash] last (current) aggregated processes state
13
13
  def current!
14
14
  state_message = ::Karafka::Admin.read_topic(
15
- Karafka::Web.config.topics.consumers.states,
15
+ Karafka::Web.config.topics.consumers.states.name,
16
16
  0,
17
17
  # We need to take more in case there would be transactions running.
18
18
  # In theory we could take two but this compensates for any involuntary
@@ -36,17 +36,17 @@ module Karafka
36
36
  def prepare_data(consumers_state, consumers_metrics)
37
37
  [
38
38
  {
39
- topic: Karafka::Web.config.topics.consumers.states,
39
+ topic: Karafka::Web.config.topics.consumers.states.name,
40
40
  payload: Zlib::Deflate.deflate(consumers_state.to_json),
41
41
  # This will ensure that the consumer states are compacted
42
- key: Karafka::Web.config.topics.consumers.states,
42
+ key: Karafka::Web.config.topics.consumers.states.name,
43
43
  partition: 0,
44
44
  headers: { 'zlib' => 'true' }
45
45
  },
46
46
  {
47
- topic: Karafka::Web.config.topics.consumers.metrics,
47
+ topic: Karafka::Web.config.topics.consumers.metrics.name,
48
48
  payload: Zlib::Deflate.deflate(consumers_metrics.to_json),
49
- key: Karafka::Web.config.topics.consumers.metrics,
49
+ key: Karafka::Web.config.topics.consumers.metrics.name,
50
50
  partition: 0,
51
51
  headers: { 'zlib' => 'true' }
52
52
  }
@@ -28,6 +28,7 @@ module Karafka
28
28
  required(:ls_offset) { |val| val.is_a?(Integer) }
29
29
  required(:ls_offset_d) { |val| val.is_a?(Integer) }
30
30
  required(:ls_offset_fd) { |val| val.is_a?(Integer) && val >= 0 }
31
+ required(:transactional) { |val| [true, false].include?(val) }
31
32
  end
32
33
  end
33
34
  end
@@ -8,10 +8,10 @@ module Karafka
8
8
  # Tracks pausing and un-pausing of topics partitions for both user requested and
9
9
  # automatic events.
10
10
  class Pausing < Base
11
- # Indicate pause
11
+ # Tracks the pause start
12
12
  #
13
13
  # @param event [Karafka::Core::Monitoring::Event]
14
- def on_consumer_consuming_pause(event)
14
+ def on_client_pause(event)
15
15
  track do |sampler|
16
16
  sampler.pauses[pause_id(event)] = {
17
17
  timeout: event[:timeout],
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Karafka
4
+ module Web
5
+ module Tracking
6
+ module Consumers
7
+ module Listeners
8
+ # Tracks data related to transactions
9
+ # seek offsets are needed because when consumer offsets are committed in transactions,
10
+ # librdkafka does not publish the lags in a regular way (they are set to -1) and we need
11
+ # to compute them via enrichment of information.
12
+ class Transactions < Base
13
+ # Tracking of things needed to support transactional consumers post successful
14
+ # transaction.
15
+ #
16
+ # @param event [Karafka::Core::Monitoring::Event]
17
+ def on_consumer_consuming_transaction(event)
18
+ consumer = event[:caller]
19
+ sg_id = consumer.topic.subscription_group.id
20
+ topic_name = consumer.topic.name
21
+ # We store it as a string because librdkafka also does that and its easier to align
22
+ # without casting it later
23
+ partition_id = consumer.partition
24
+
25
+ track do |sampler|
26
+ break unless sampler.subscription_groups.key?(sg_id)
27
+
28
+ seek_offset = consumer.coordinator.seek_offset
29
+
30
+ break if seek_offset.nil?
31
+
32
+ topics_scope = sampler.subscription_groups[sg_id][:topics]
33
+ p_scope = topics_scope[topic_name][partition_id]
34
+
35
+ p_scope[:transactional] = true
36
+ p_scope[:seek_offset] = seek_offset
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
@@ -63,7 +63,7 @@ module Karafka
63
63
  # Report consumers statuses
64
64
  messages = [
65
65
  {
66
- topic: ::Karafka::Web.config.topics.consumers.reports,
66
+ topic: ::Karafka::Web.config.topics.consumers.reports.name,
67
67
  payload: Zlib::Deflate.deflate(report.to_json),
68
68
  key: process_id,
69
69
  partition: 0,
@@ -76,7 +76,7 @@ module Karafka
76
76
  @error_contract.validate!(error)
77
77
 
78
78
  {
79
- topic: Karafka::Web.config.topics.errors,
79
+ topic: Karafka::Web.config.topics.errors.name,
80
80
  payload: Zlib::Deflate.deflate(error.to_json),
81
81
  # Always dispatch errors from the same process to the same partition
82
82
  key: process_id,
@@ -15,7 +15,7 @@ module Karafka
15
15
  # Current schema version
16
16
  # This is used for detecting incompatible changes and not using outdated data during
17
17
  # upgrades
18
- SCHEMA_VERSION = '1.4.0'
18
+ SCHEMA_VERSION = '1.4.1'
19
19
 
20
20
  # Counters that count events occurrences during the given window
21
21
  COUNTERS_BASE = {
@@ -51,7 +51,17 @@ module Karafka
51
51
  @subscription_groups = Hash.new do |h, sg_id|
52
52
  h[sg_id] = {
53
53
  id: sg_id,
54
- polled_at: monotonic_now
54
+ polled_at: monotonic_now,
55
+ topics: Hash.new do |h1, topic|
56
+ h1[topic] = Hash.new do |h2, partition|
57
+ # We track those details in case we need to fill statistical gaps for
58
+ # transactional consumers
59
+ h2[partition] = {
60
+ seek_offset: -1,
61
+ transactional: false
62
+ }
63
+ end
64
+ end
55
65
  }
56
66
  end
57
67
 
@@ -223,11 +233,9 @@ module Karafka
223
233
  def memory_size
224
234
  @memory_size ||= case RUBY_PLATFORM
225
235
  when /linux/
226
- @shell
227
- .call('grep MemTotal /proc/meminfo')
228
- .match(/(\d+)/)
229
- .to_s
230
- .to_i
236
+ mem_info = File.read('/proc/meminfo')
237
+ mem_total_line = mem_info.match(/MemTotal:\s*(?<total>\d+)/)
238
+ mem_total_line['total'].to_i
231
239
  when /darwin|bsd/
232
240
  @shell
233
241
  .call('sysctl -a')
@@ -245,7 +253,13 @@ module Karafka
245
253
  # @return [Array<Float>] load averages for last 1, 5 and 15 minutes
246
254
  def cpu_usage
247
255
  case RUBY_PLATFORM
248
- when /darwin|bsd|linux/
256
+ when /linux/
257
+ File
258
+ .read('/proc/loadavg')
259
+ .split(' ')
260
+ .first(3)
261
+ .map(&:to_f)
262
+ when /darwin|bsd/
249
263
  @shell
250
264
  .call('w | head -1')
251
265
  .strip
@@ -280,10 +294,21 @@ module Karafka
280
294
  def memory_threads_ps
281
295
  @memory_threads_ps = case RUBY_PLATFORM
282
296
  when /linux/
283
- @shell
284
- .call('ps -A -o rss=,thcount=,pid=')
285
- .split("\n")
286
- .map { |row| row.strip.split(' ').map(&:to_i) }
297
+ page_size = Karafka::Web::Tracking::Helpers::Sysconf.page_size
298
+ status_file = "/proc/#{::Process.pid}/status"
299
+
300
+ pid = status_file.match(%r{/proc/(\d+)/status})[1]
301
+
302
+ # Extract thread count from /proc/<pid>/status
303
+ thcount = File.read(status_file)[/^Threads:\s+(\d+)/, 1].to_i
304
+
305
+ # Extract RSS from /proc/<pid>/statm (second field)
306
+ statm_file = "/proc/#{pid}/statm"
307
+ rss_pages = File.read(statm_file).split[1].to_i rescue 0
308
+ # page size is retrieved from Sysconf
309
+ rss_kb = (rss_pages * page_size) / 1024
310
+
311
+ [[rss_kb, thcount, pid.to_i]]
287
312
  # thcount is not available on macos ps
288
313
  # because of that we inject 0 as threads count similar to how
289
314
  # we do on windows
@@ -308,9 +333,51 @@ module Karafka
308
333
  # This should be always available, since the subscription group polled at time
309
334
  # is first initialized before we start polling, there should be no case where
310
335
  # we have statistics about a given subscription group but we do not have the
311
- # last polling time
312
- polled_at = subscription_groups.fetch(sg_id).fetch(:polled_at)
336
+ # sg reference
337
+ sg_tracking = subscription_groups.fetch(sg_id)
338
+
339
+ polled_at = sg_tracking.fetch(:polled_at)
313
340
  sg_details[:state][:poll_age] = (monotonic_now - polled_at).round(2)
341
+
342
+ sg_details[:topics].each do |topic_name, topic_details|
343
+ topic_details[:partitions].each do |partition_id, partition_details|
344
+ # Always assume non-transactional as default. Will be overwritten by the
345
+ # consumer level details if collected
346
+ partition_details[:transactional] ||= false
347
+
348
+ # If we have stored offset or stored lag, it means it's not a transactional
349
+ # consumer at all so we can skip enrichment
350
+ next if partition_details[:lag_stored].positive?
351
+ next if partition_details[:stored_offset].positive?
352
+ next unless sg_tracking[:topics].key?(topic_name)
353
+ next unless sg_tracking[:topics][topic_name].key?(partition_id)
354
+
355
+ k_partition_details = sg_tracking[:topics][topic_name][partition_id]
356
+
357
+ # If seek offset was not yey set, nothing to enrich
358
+ next unless k_partition_details[:seek_offset].positive?
359
+
360
+ partition_details[:transactional] = k_partition_details[:transactional]
361
+
362
+ # Seek offset is always +1 from the last stored in Karafka
363
+ seek_offset = k_partition_details[:seek_offset]
364
+ stored_offset = seek_offset - 1
365
+
366
+ # In case of transactions we have to compute the lag ourselves
367
+ # -1 because ls offset (or high watermark) is last + 1
368
+ lag = partition_details[:ls_offset] - seek_offset
369
+ # This can happen if ls_offset is refreshed slower than our stored offset
370
+ # fetching from Karafka transactional layer
371
+ lag = 0 if lag.negative?
372
+
373
+ partition_details[:lag] = lag
374
+ partition_details[:lag_d] = 0
375
+ partition_details[:lag_stored] = lag
376
+ partition_details[:lag_stored_d] = 0
377
+ partition_details[:stored_offset] = stored_offset
378
+ partition_details[:committed_offset] = stored_offset
379
+ end
380
+ end
314
381
  end
315
382
  end
316
383
  end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Karafka
4
+ module Web
5
+ module Tracking
6
+ # Namespace for tracking related helpers
7
+ module Helpers
8
+ # Namespace for unix-based helper methods used to fetch OS details
9
+ module Sysconf
10
+ extend FFI::Library
11
+
12
+ case RUBY_PLATFORM
13
+ when /linux/
14
+ ffi_lib 'libc.so.6' # Standard C library on Linux
15
+ SC_PAGESIZE = 30 # _SC_PAGESIZE constant
16
+ when /darwin/
17
+ ffi_lib 'libSystem.B.dylib' # Standard C library on macOS
18
+ SC_PAGESIZE = 29 # _SC_PAGESIZE constant
19
+ end
20
+
21
+ attach_function :sysconf, [:int], :long
22
+
23
+ class << self
24
+ # @return [Integer]
25
+ def page_size
26
+ sysconf(SC_PAGESIZE)
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
@@ -33,7 +33,7 @@ module Karafka
33
33
  @error_contract.validate!(error)
34
34
 
35
35
  {
36
- topic: Karafka::Web.config.topics.errors,
36
+ topic: Karafka::Web.config.topics.errors.name,
37
37
  payload: error.to_json,
38
38
  # Always dispatch errors from the same process to the same partition
39
39
  key: error[:process][:id]
@@ -11,122 +11,29 @@ module Karafka
11
11
 
12
12
  instance_exec(&CONTEXT_DETAILS)
13
13
 
14
+ # Sub-routes for given pieces of the Web UI
15
+ SUB_ROUTES = [
16
+ Routes::Assets,
17
+ Routes::Dashboard,
18
+ Routes::Consumers,
19
+ Routes::ProOnly,
20
+ Routes::Jobs,
21
+ Routes::Routing,
22
+ Routes::Cluster,
23
+ Routes::Errors,
24
+ Routes::Status,
25
+ Routes::Support,
26
+ Routes::Ux
27
+ ].freeze
28
+
29
+ private_constant :SUB_ROUTES
30
+
14
31
  route do |r|
15
32
  r.root { r.redirect root_path('dashboard') }
16
33
 
17
- # Serve current version specific assets to prevent users from fetching old assets
18
- # after upgrade
19
- r.on 'assets', Karafka::Web::VERSION do
20
- r.public
21
- end
22
-
23
- r.get 'dashboard' do
24
- @breadcrumbs = false
25
- controller = Controllers::DashboardController.new(params)
26
- controller.index
27
- end
28
-
29
- r.on 'consumers' do
30
- %w[
31
- performance
32
- controls
33
- commands
34
- ].each do |path|
35
- r.get path do |_process_id|
36
- raise Errors::Ui::ProOnlyError
37
- end
38
- end
39
-
40
- r.get String, 'subscriptions' do |_process_id|
41
- raise Errors::Ui::ProOnlyError
42
- end
43
-
44
- r.get do
45
- controller = Controllers::ConsumersController.new(params)
46
- controller.index
47
- end
48
- end
49
-
50
- %w[
51
- health
52
- explorer
53
- dlq
54
- ].each do |route|
55
- r.get route, [String, true], [String, true] do
56
- raise Errors::Ui::ProOnlyError
57
- end
58
- end
59
-
60
- r.on 'jobs' do
61
- controller = Controllers::JobsController.new(params)
62
-
63
- r.get 'running' do
64
- controller.running
65
- end
66
-
67
- r.get 'pending' do
68
- controller.pending
69
- end
70
-
71
- r.redirect root_path('jobs/running')
72
- end
73
-
74
- r.on 'routing' do
75
- controller = Controllers::RoutingController.new(params)
76
-
77
- r.get String do |topic_id|
78
- controller.show(topic_id)
79
- end
80
-
81
- r.get do
82
- controller.index
83
- end
84
- end
85
-
86
- r.on 'cluster' do
87
- controller = Controllers::ClusterController.new(params)
88
-
89
- r.get 'brokers' do
90
- controller.brokers
91
- end
92
-
93
- r.get 'replication' do
94
- controller.replication
95
- end
96
-
97
- r.redirect root_path('cluster/brokers')
98
- end
99
-
100
- r.on 'topics' do
101
- raise Errors::Ui::ProOnlyError
102
- end
103
-
104
- r.on 'errors' do
105
- controller = Controllers::ErrorsController.new(params)
106
-
107
- r.get Integer do |offset|
108
- controller.show(offset)
109
- end
110
-
111
- r.get do
112
- controller.index
113
- end
114
- end
115
-
116
- r.get 'status' do
117
- controller = Controllers::StatusController.new(params)
118
- controller.show
119
- end
120
-
121
- r.get 'ux' do
122
- controller = Controllers::UxController.new(params)
123
- controller.show
124
- end
34
+ SUB_ROUTES.each { |sub_route| sub_route.bind(self, r) }
125
35
 
126
- r.get 'support' do
127
- controller = Controllers::SupportController.new(params)
128
- controller.show
129
- end
36
+ nil
130
37
  end
131
38
  end
132
39
  end
@@ -40,6 +40,7 @@ module Karafka
40
40
  plugin :capture_erb
41
41
  plugin :content_for
42
42
  plugin :inject_erb
43
+ plugin :all_verbs
43
44
 
44
45
  # Based on
45
46
  # https://github.com/sidekiq/sidekiq/blob/ae6ca119/lib/sidekiq/web/application.rb#L8
@@ -70,7 +71,16 @@ module Karafka
70
71
  # Map redirect flashes (if any) to Roda flash messages
71
72
  result.flashes.each { |key, value| flash[key] = value }
72
73
 
73
- response.redirect result.back? ? request.referer : root_path(result.path)
74
+ path = case result.path
75
+ when :back
76
+ session[:current_path]
77
+ when :previous
78
+ session[:previous_path]
79
+ else
80
+ root_path(result.path)
81
+ end
82
+
83
+ response.redirect path || root_path
74
84
  end
75
85
 
76
86
  handle_block_result Controllers::Responses::File do |result|
@@ -109,9 +119,11 @@ module Karafka
109
119
 
110
120
  before do
111
121
  check_csrf!
122
+ store_paths_history(request, session)
112
123
  end
113
124
 
114
125
  plugin :class_matchers
126
+ plugin :symbol_matchers
115
127
 
116
128
  # Time matcher with optional hours, minutes and seconds
117
129
  TIME_MATCHER = %r{(\d{4}-\d{2}-\d{2}/?(\d{2})?(:\d{2})?(:\d{2})?)}
@@ -122,12 +134,22 @@ module Karafka
122
134
  # @note In case the date-time is invalid, raise and render 404
123
135
  # @note The time component is optional as `Time#parse` will fallback to lowest time
124
136
  # available, so we can build only date based lookups
125
- class_matcher(Time, TIME_MATCHER) do |datetime|
126
- [Time.parse(datetime)]
137
+ class_matcher(Time, TIME_MATCHER) do |*datetime|
138
+ [Time.parse(datetime[0])]
127
139
  rescue ArgumentError
128
140
  raise Errors::Ui::NotFoundError
129
141
  end
130
142
 
143
+ # Partitions ids cannot be bigger than 32 bit C int. We use this matcher to ensure we
144
+ # only support that big partition numbers. Otherwise librdkafka would crash.
145
+ symbol_matcher :partition_id, /(\d{1,14})/ do |value|
146
+ int_value = value.to_i
147
+
148
+ raise Errors::Ui::NotFoundError unless int_value.between?(0, 2_147_483_647)
149
+
150
+ [int_value]
151
+ end
152
+
131
153
  # Allows us to build current path with additional params + it merges existing params into
132
154
  # the query data. Query data takes priority over request params.
133
155
  # @param query_data [Hash] query params we want to add to the current path
@@ -145,6 +167,14 @@ module Karafka
145
167
  [request.path, query_string].compact.join('?')
146
168
  end
147
169
 
170
+ # Builds a consumer instance with all needed details
171
+ # @param consumer_class [Class]
172
+ def build(consumer_class)
173
+ Controllers::Requests::ExecutionWrapper.new(
174
+ consumer_class.new(params, session)
175
+ )
176
+ end
177
+
148
178
  # Sets appropriate template variables based on the response object and renders the
149
179
  # expected view
150
180
  # @param response [Karafka::Web::Ui::Controllers::Responses::Data] response data object
@@ -162,6 +192,31 @@ module Karafka
162
192
  def params
163
193
  Controllers::Requests::Params.new(request.params)
164
194
  end
195
+
196
+ private
197
+
198
+ # Stores history about visited paths. Useful for redirecting users back when needed.
199
+ # @param request [Karafka::Web::Ui::App::RodaRequest]
200
+ # @param session [Object] session object (Rails or Rack)
201
+ def store_paths_history(request, session)
202
+ # Code below tracks previous paths so we can use it to redirect users back
203
+ return unless request.get?
204
+ return unless request.env['HTTP_ACCEPT']&.include?('text/html')
205
+
206
+ requested_path = request.path
207
+
208
+ if session[:current_path].nil?
209
+ session[:current_path] = requested_path
210
+
211
+ return
212
+ end
213
+
214
+ return if request.path == session[:current_path]
215
+
216
+ # When navigating to a different page
217
+ session[:previous_path] = session[:current_path]
218
+ session[:current_path] = requested_path
219
+ end
165
220
  end
166
221
  end
167
222
  end
@@ -8,6 +8,9 @@ module Karafka
8
8
  # Base controller from which all the controllers should inherit.
9
9
  class BaseController
10
10
  include Web::Ui::Lib::Paginations
11
+ include Requests::Hookable
12
+
13
+ attr_reader :params, :session
11
14
 
12
15
  # Alias for easier referencing
13
16
  Models = Web::Ui::Models
@@ -19,9 +22,31 @@ module Karafka
19
22
 
20
23
  self.sortable_attributes = []
21
24
 
25
+ # Detect that the state of the cache has changed
26
+ before do
27
+ cache.clear_if_needed(
28
+ session[:cache_hash],
29
+ session[:cache_timestamp].to_i
30
+ )
31
+ end
32
+
33
+ after do
34
+ next unless cache.exist?
35
+
36
+ session[:cache_hash] = cache.hash
37
+ session[:cache_timestamp] = cache.timestamp.to_i
38
+ end
39
+
22
40
  # @param params [Karafka::Web::Ui::Controllers::Requests::Params] request parameters
23
- def initialize(params)
41
+ # @param session [Request::Session] request session (Rails or other framework)
42
+ def initialize(params, session)
24
43
  @params = params
44
+ @session = session
45
+ end
46
+
47
+ # @return [Karafka::Web::Ui::Lib::Cache] per-process cache instance
48
+ def cache
49
+ Karafka::Web.config.ui.cache
25
50
  end
26
51
 
27
52
  private
@@ -50,6 +75,9 @@ module Karafka
50
75
 
51
76
  attributes[:breadcrums_scope] = scope
52
77
 
78
+ @current_action_name = action.to_sym
79
+ @current_controller_name = base.join('-')
80
+
53
81
  instance_variables.each do |iv|
54
82
  next if iv.to_s.start_with?('@_')
55
83
  next if iv.to_s.start_with?('@params')
@@ -72,6 +100,20 @@ module Karafka
72
100
  Responses::Redirect.new(path, flashes)
73
101
  end
74
102
 
103
+ # Wraps the provided arguments inside a message with a `<strong>` tag to simplify flash
104
+ # messages building.
105
+ #
106
+ # @param message [String] message with `?` to be replaced.
107
+ # @param args [Array<Object>] arguments to use to replace `?` with strong
108
+ # @return [String] formatted string
109
+ def format_flash(message, *args)
110
+ args.each do |arg|
111
+ message = message.sub('?', "<strong>#{arg}</strong>")
112
+ end
113
+
114
+ message
115
+ end
116
+
75
117
  # Builds a file response object that will be used as a base to dispatch the file
76
118
  #
77
119
  # @param content [String] Payload we want to dispatch as a file