karafka-web 0.10.3 → 0.11.0.beta1

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 (481) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/CHANGELOG.md +64 -176
  4. data/Gemfile +4 -0
  5. data/Gemfile.lock +88 -44
  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 +3 -5
  13. data/lib/karafka/web/config.rb +86 -10
  14. data/lib/karafka/web/contracts/config.rb +45 -5
  15. data/lib/karafka/web/errors.rb +10 -0
  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 +32 -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 +101 -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/consumers/_consumer.erb +59 -0
  134. data/lib/karafka/web/pro/ui/views/consumers/consumers/_consumer_performance.erb +71 -0
  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/controls/_controls.erb +107 -0
  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 +50 -38
  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/consumers/metrics.rb +1 -1
  307. data/lib/karafka/web/processing/consumers/state.rb +1 -1
  308. data/lib/karafka/web/processing/publisher.rb +4 -4
  309. data/lib/karafka/web/tracking/consumers/contracts/partition.rb +1 -0
  310. data/lib/karafka/web/tracking/consumers/listeners/errors.rb +38 -9
  311. data/lib/karafka/web/tracking/consumers/listeners/pausing.rb +2 -2
  312. data/lib/karafka/web/tracking/consumers/listeners/transactions.rb +44 -0
  313. data/lib/karafka/web/tracking/consumers/reporter.rb +2 -2
  314. data/lib/karafka/web/tracking/consumers/sampler.rb +81 -14
  315. data/lib/karafka/web/tracking/helpers/sysconf.rb +33 -0
  316. data/lib/karafka/web/tracking/producers/reporter.rb +2 -9
  317. data/lib/karafka/web/ui/app.rb +19 -112
  318. data/lib/karafka/web/ui/base.rb +63 -4
  319. data/lib/karafka/web/ui/controllers/base_controller.rb +43 -1
  320. data/lib/karafka/web/ui/controllers/cluster_controller.rb +5 -2
  321. data/lib/karafka/web/ui/controllers/errors_controller.rb +1 -1
  322. data/lib/karafka/web/ui/controllers/requests/execution_wrapper.rb +52 -0
  323. data/lib/karafka/web/ui/controllers/requests/hookable.rb +99 -0
  324. data/lib/karafka/web/ui/controllers/requests/params.rb +39 -1
  325. data/lib/karafka/web/ui/controllers/responses/redirect.rb +0 -5
  326. data/lib/karafka/web/ui/controllers/status_controller.rb +3 -0
  327. data/lib/karafka/web/ui/helpers/application_helper.rb +10 -1
  328. data/lib/karafka/web/ui/helpers/paths_helper.rb +54 -10
  329. data/lib/karafka/web/ui/lib/admin.rb +1 -1
  330. data/lib/karafka/web/ui/lib/cache.rb +135 -0
  331. data/lib/karafka/web/ui/models/broker.rb +1 -2
  332. data/lib/karafka/web/ui/models/cluster_info.rb +15 -21
  333. data/lib/karafka/web/ui/models/consumers_metrics.rb +1 -1
  334. data/lib/karafka/web/ui/models/consumers_state.rb +1 -1
  335. data/lib/karafka/web/ui/models/counters.rb +1 -1
  336. data/lib/karafka/web/ui/models/health.rb +9 -7
  337. data/lib/karafka/web/ui/models/process.rb +14 -0
  338. data/lib/karafka/web/ui/models/processes.rb +2 -2
  339. data/lib/karafka/web/ui/models/recurring_tasks/schedule.rb +1 -1
  340. data/lib/karafka/web/ui/models/status.rb +27 -8
  341. data/lib/karafka/web/ui/models/topic.rb +1 -2
  342. data/lib/karafka/web/ui/public/javascripts/application.js +8 -98
  343. data/lib/karafka/web/ui/public/javascripts/application.min.js +15 -6
  344. data/lib/karafka/web/ui/public/javascripts/application.min.js.br +0 -0
  345. data/lib/karafka/web/ui/public/javascripts/application.min.js.gz +0 -0
  346. data/lib/karafka/web/ui/public/javascripts/components/action_confirmation_manager.js +30 -0
  347. data/lib/karafka/web/ui/public/javascripts/components/alerts.js +39 -0
  348. data/lib/karafka/web/ui/public/javascripts/components/btn_toggle_manager.js +17 -7
  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 +5123 -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 +512 -213
  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 +39 -30
  380. data/lib/karafka/web/ui/views/consumers/_incompatible.erb +13 -0
  381. data/lib/karafka/web/ui/views/consumers/_no_consumers.erb +2 -2
  382. data/lib/karafka/web/ui/views/consumers/_tabs.erb +4 -4
  383. data/lib/karafka/web/ui/views/consumers/index.erb +1 -1
  384. data/lib/karafka/web/ui/views/dashboard/_feature_pro.erb +1 -1
  385. data/lib/karafka/web/ui/views/dashboard/_not_enough_data.erb +2 -2
  386. data/lib/karafka/web/ui/views/dashboard/_ranges_selector.erb +1 -1
  387. data/lib/karafka/web/ui/views/dashboard/index.erb +6 -49
  388. data/lib/karafka/web/ui/views/errors/_detail.erb +3 -3
  389. data/lib/karafka/web/ui/views/errors/index.erb +1 -1
  390. data/lib/karafka/web/ui/views/jobs/_job.erb +38 -29
  391. data/lib/karafka/web/ui/views/jobs/pending.erb +1 -1
  392. data/lib/karafka/web/ui/views/jobs/running.erb +1 -1
  393. data/lib/karafka/web/ui/views/layout.erb +7 -5
  394. data/lib/karafka/web/ui/views/shared/_become_pro.erb +1 -1
  395. data/lib/karafka/web/ui/views/shared/_brand.erb +1 -1
  396. data/lib/karafka/web/ui/views/shared/_breadcrumbs.erb +1 -1
  397. data/lib/karafka/web/ui/views/shared/_content.erb +1 -1
  398. data/lib/karafka/web/ui/views/shared/_controls.erb +10 -3
  399. data/lib/karafka/web/ui/views/shared/_custom_nav.erb +9 -0
  400. data/lib/karafka/web/ui/views/shared/_flashes.erb +3 -5
  401. data/lib/karafka/web/ui/views/shared/_header.erb +25 -2
  402. data/lib/karafka/web/ui/views/shared/_navigation.erb +17 -15
  403. data/lib/karafka/web/ui/views/shared/alerts/_error.erb +8 -0
  404. data/lib/karafka/web/ui/views/shared/alerts/_info.erb +8 -0
  405. data/lib/karafka/web/ui/views/shared/alerts/_primary.erb +8 -0
  406. data/lib/karafka/web/ui/views/shared/alerts/_secondary.erb +8 -0
  407. data/lib/karafka/web/ui/views/shared/alerts/_success.erb +8 -0
  408. data/lib/karafka/web/ui/views/shared/alerts/_warning.erb +8 -0
  409. data/lib/karafka/web/ui/views/shared/exceptions/incompatible_schema.erb +34 -0
  410. data/lib/karafka/web/ui/views/shared/exceptions/not_allowed.erb +4 -0
  411. data/lib/karafka/web/ui/views/shared/exceptions/not_found.erb +5 -1
  412. data/lib/karafka/web/ui/views/shared/exceptions/pro_only.erb +4 -0
  413. data/lib/karafka/web/ui/views/shared/icons/_arrow_left.erb +3 -0
  414. data/lib/karafka/web/ui/views/shared/icons/_arrow_up_tray.erb +3 -0
  415. data/lib/karafka/web/ui/views/shared/icons/_clock.erb +3 -0
  416. data/lib/karafka/web/ui/views/shared/icons/_pencil.erb +3 -0
  417. data/lib/karafka/web/ui/views/shared/icons/_pencil_square.erb +3 -0
  418. data/lib/karafka/web/ui/views/shared/icons/_play_pause.erb +3 -0
  419. data/lib/karafka/web/ui/views/shared/icons/_plus.erb +3 -0
  420. data/lib/karafka/web/ui/views/shared/icons/_trash.erb +3 -0
  421. data/lib/karafka/web/ui/views/status/failures/_live_reporting.erb +1 -1
  422. data/lib/karafka/web/ui/views/status/failures/_partitions.erb +3 -3
  423. data/lib/karafka/web/ui/views/status/failures/_state_calculation.erb +2 -2
  424. data/lib/karafka/web/ui/views/status/info/_components.erb +6 -6
  425. data/lib/karafka/web/ui/views/status/show.erb +15 -0
  426. data/lib/karafka/web/ui/views/status/warnings/_consumers_schemas.erb +31 -0
  427. data/lib/karafka/web/ui/views/ux/_icons.erb +1 -1
  428. data/lib/karafka/web/ui/views/ux/_status_rows.erb +6 -0
  429. data/lib/karafka/web/version.rb +1 -1
  430. data/lib/karafka/web.rb +9 -1
  431. data/package-lock.json +799 -1226
  432. data/package.json +3 -4
  433. data/postcss.config.js +1 -2
  434. data/renovate.json +13 -1
  435. data/tailwind.config.js +0 -4
  436. data.tar.gz.sig +0 -0
  437. metadata +233 -109
  438. metadata.gz.sig +0 -0
  439. data/lib/karafka/web/pro/commanding/commands/quiet.rb +0 -34
  440. data/lib/karafka/web/pro/commanding/commands/stop.rb +0 -34
  441. data/lib/karafka/web/pro/commanding/commands/trace.rb +0 -41
  442. data/lib/karafka/web/pro/ui/controllers/commanding_controller.rb +0 -118
  443. data/lib/karafka/web/pro/ui/controllers/commands_controller.rb +0 -96
  444. data/lib/karafka/web/pro/ui/controllers/consumers_controller.rb +0 -138
  445. data/lib/karafka/web/pro/ui/controllers/explorer_controller.rb +0 -220
  446. data/lib/karafka/web/pro/ui/controllers/messages_controller.rb +0 -107
  447. data/lib/karafka/web/pro/ui/controllers/search_controller.rb +0 -73
  448. data/lib/karafka/web/pro/ui/controllers/topics_controller.rb +0 -130
  449. data/lib/karafka/web/pro/ui/views/commands/_breadcrumbs.erb +0 -21
  450. data/lib/karafka/web/pro/ui/views/commands/_command_details.erb +0 -1
  451. data/lib/karafka/web/pro/ui/views/commands/_empty.erb +0 -3
  452. data/lib/karafka/web/pro/ui/views/commands/show.erb +0 -33
  453. data/lib/karafka/web/pro/ui/views/consumers/_breadcrumbs.erb +0 -55
  454. data/lib/karafka/web/pro/ui/views/consumers/_consumer.erb +0 -47
  455. data/lib/karafka/web/pro/ui/views/consumers/_consumer_controls.erb +0 -95
  456. data/lib/karafka/web/pro/ui/views/consumers/_consumer_performance.erb +0 -59
  457. data/lib/karafka/web/pro/ui/views/consumers/_tabs.erb +0 -33
  458. data/lib/karafka/web/pro/ui/views/consumers/consumer/_commands.erb +0 -72
  459. data/lib/karafka/web/pro/ui/views/consumers/consumer/_consumer_group.erb +0 -8
  460. data/lib/karafka/web/pro/ui/views/consumers/consumer/_no_subscriptions.erb +0 -7
  461. data/lib/karafka/web/pro/ui/views/consumers/details.erb +0 -13
  462. data/lib/karafka/web/pro/ui/views/consumers/subscriptions.erb +0 -25
  463. data/lib/karafka/web/pro/ui/views/explorer/_no_topics.erb +0 -1
  464. data/lib/karafka/web/pro/ui/views/explorer/_topic.erb +0 -10
  465. data/lib/karafka/web/pro/ui/views/explorer/index.erb +0 -14
  466. data/lib/karafka/web/pro/ui/views/explorer/messages/_headers.erb +0 -33
  467. data/lib/karafka/web/pro/ui/views/explorer/partition/_cleaned.erb +0 -3
  468. data/lib/karafka/web/pro/ui/views/explorer/partition/_empty.erb +0 -3
  469. data/lib/karafka/web/pro/ui/views/explorer/topic/_empty.erb +0 -3
  470. data/lib/karafka/web/pro/ui/views/search/_breadcrumbs.erb +0 -1
  471. data/lib/karafka/web/pro/ui/views/search/_fix_errors.erb +0 -3
  472. data/lib/karafka/web/pro/ui/views/search/_no_results.erb +0 -3
  473. data/lib/karafka/web/pro/ui/views/search/_timeout.erb +0 -3
  474. data/lib/karafka/web/pro/ui/views/search/index.erb +0 -29
  475. data/lib/karafka/web/pro/ui/views/topics/_breadcrumbs.erb +0 -45
  476. data/lib/karafka/web/pro/ui/views/topics/_partition_offsets.erb +0 -10
  477. data/lib/karafka/web/pro/ui/views/topics/_topic.erb +0 -9
  478. data/lib/karafka/web/pro/ui/views/topics/distribution/_empty_partitions.erb +0 -1
  479. data/lib/karafka/web/pro/ui/views/topics/distribution/_partition.erb +0 -10
  480. data/lib/karafka/web/pro/ui/views/topics/index.erb +0 -14
  481. data/lib/karafka/web/ui/lib/ttl_cache.rb +0 -82
@@ -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
@@ -10,13 +10,6 @@ module Karafka
10
10
  # because there is no expectation on immediate status updates for producers and their
11
11
  # dispatch flow is always periodic based.
12
12
  class Reporter < Tracking::Reporter
13
- # Minimum number of messages to produce to produce them in sync mode
14
- # This acts as a small back-off not to overload the system in case we would have
15
- # extremely big number of errors happening
16
- PRODUCE_SYNC_THRESHOLD = 25
17
-
18
- private_constant :PRODUCE_SYNC_THRESHOLD
19
-
20
13
  # This mutex is shared between tracker and samplers so there is no case where metrics
21
14
  # would be collected same time tracker reports
22
15
  MUTEX = Mutex.new
@@ -40,7 +33,7 @@ module Karafka
40
33
  @error_contract.validate!(error)
41
34
 
42
35
  {
43
- topic: Karafka::Web.config.topics.errors,
36
+ topic: Karafka::Web.config.topics.errors.name,
44
37
  payload: error.to_json,
45
38
  # Always dispatch errors from the same process to the same partition
46
39
  key: error[:process][:id]
@@ -82,7 +75,7 @@ module Karafka
82
75
  # normal operations we should not have that many messages to dispatch and it should not
83
76
  # slowdown any processing.
84
77
  def produce(messages)
85
- if messages.count >= PRODUCE_SYNC_THRESHOLD
78
+ if messages.count >= ::Karafka::Web.config.tracking.producers.sync_threshold
86
79
  ::Karafka::Web.producer.produce_many_sync(messages)
87
80
  else
88
81
  ::Karafka::Web.producer.produce_many_async(messages)
@@ -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|
@@ -84,7 +94,8 @@ module Karafka
84
94
  ::Rdkafka::RdkafkaError,
85
95
  Errors::Ui::NotFoundError,
86
96
  Errors::Ui::ProOnlyError,
87
- Errors::Ui::ForbiddenError
97
+ Errors::Ui::ForbiddenError,
98
+ Errors::Ui::IncompatibleSchemaError
88
99
  ] do |e|
89
100
  @error = true
90
101
 
@@ -95,6 +106,9 @@ module Karafka
95
106
  when Errors::Ui::ForbiddenError
96
107
  response.status = 403
97
108
  view 'shared/exceptions/not_allowed'
109
+ when Errors::Ui::IncompatibleSchemaError
110
+ response.status = 422
111
+ view 'shared/exceptions/incompatible_schema'
98
112
  else
99
113
  response.status = 404
100
114
  view 'shared/exceptions/not_found'
@@ -109,9 +123,11 @@ module Karafka
109
123
 
110
124
  before do
111
125
  check_csrf!
126
+ store_paths_history(request, session)
112
127
  end
113
128
 
114
129
  plugin :class_matchers
130
+ plugin :symbol_matchers
115
131
 
116
132
  # Time matcher with optional hours, minutes and seconds
117
133
  TIME_MATCHER = %r{(\d{4}-\d{2}-\d{2}/?(\d{2})?(:\d{2})?(:\d{2})?)}
@@ -122,12 +138,22 @@ module Karafka
122
138
  # @note In case the date-time is invalid, raise and render 404
123
139
  # @note The time component is optional as `Time#parse` will fallback to lowest time
124
140
  # available, so we can build only date based lookups
125
- class_matcher(Time, TIME_MATCHER) do |datetime|
126
- [Time.parse(datetime)]
141
+ class_matcher(Time, TIME_MATCHER) do |*datetime|
142
+ [Time.parse(datetime[0])]
127
143
  rescue ArgumentError
128
144
  raise Errors::Ui::NotFoundError
129
145
  end
130
146
 
147
+ # Partitions ids cannot be bigger than 32 bit C int. We use this matcher to ensure we
148
+ # only support that big partition numbers. Otherwise librdkafka would crash.
149
+ symbol_matcher :partition_id, /(\d{1,14})/ do |value|
150
+ int_value = value.to_i
151
+
152
+ raise Errors::Ui::NotFoundError unless int_value.between?(0, 2_147_483_647)
153
+
154
+ [int_value]
155
+ end
156
+
131
157
  # Allows us to build current path with additional params + it merges existing params into
132
158
  # the query data. Query data takes priority over request params.
133
159
  # @param query_data [Hash] query params we want to add to the current path
@@ -145,6 +171,14 @@ module Karafka
145
171
  [request.path, query_string].compact.join('?')
146
172
  end
147
173
 
174
+ # Builds a consumer instance with all needed details
175
+ # @param consumer_class [Class]
176
+ def build(consumer_class)
177
+ Controllers::Requests::ExecutionWrapper.new(
178
+ consumer_class.new(params, session)
179
+ )
180
+ end
181
+
148
182
  # Sets appropriate template variables based on the response object and renders the
149
183
  # expected view
150
184
  # @param response [Karafka::Web::Ui::Controllers::Responses::Data] response data object
@@ -162,6 +196,31 @@ module Karafka
162
196
  def params
163
197
  Controllers::Requests::Params.new(request.params)
164
198
  end
199
+
200
+ private
201
+
202
+ # Stores history about visited paths. Useful for redirecting users back when needed.
203
+ # @param request [Karafka::Web::Ui::App::RodaRequest]
204
+ # @param session [Object] session object (Rails or Rack)
205
+ def store_paths_history(request, session)
206
+ # Code below tracks previous paths so we can use it to redirect users back
207
+ return unless request.get?
208
+ return unless request.env['HTTP_ACCEPT']&.include?('text/html')
209
+
210
+ requested_path = request.path
211
+
212
+ if session[:current_path].nil?
213
+ session[:current_path] = requested_path
214
+
215
+ return
216
+ end
217
+
218
+ return if request.path == session[:current_path]
219
+
220
+ # When navigating to a different page
221
+ session[:previous_path] = session[:current_path]
222
+ session[:current_path] = requested_path
223
+ end
165
224
  end
166
225
  end
167
226
  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
@@ -17,6 +17,9 @@ module Karafka
17
17
  in_sync_replica_brokers
18
18
  ].freeze
19
19
 
20
+ # Cluster state should always be fresh and not from cache
21
+ before { cache.clear }
22
+
20
23
  # Lists available brokers in the cluster
21
24
  def brokers
22
25
  @brokers = refine(cluster_info.brokers)
@@ -50,9 +53,9 @@ module Karafka
50
53
 
51
54
  private
52
55
 
53
- # Make sure, that for the cluster view we always get the most recent cluster state
56
+ # @return [Array] whole cluster info
54
57
  def cluster_info
55
- @cluster_info ||= Models::ClusterInfo.fetch(cached: false)
58
+ @cluster_info ||= Models::ClusterInfo.fetch
56
59
  end
57
60
 
58
61
  # @param cluster_info [Rdkafka::Metadata] cluster metadata
@@ -49,7 +49,7 @@ module Karafka
49
49
 
50
50
  # @return [String] errors topic
51
51
  def errors_topic
52
- ::Karafka::Web.config.topics.errors
52
+ ::Karafka::Web.config.topics.errors.name
53
53
  end
54
54
  end
55
55
  end
@@ -0,0 +1,52 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Karafka
4
+ module Web
5
+ module Ui
6
+ module Controllers
7
+ module Requests
8
+ # @note This class is used internally by the Web UI to wrap controllers
9
+ # and inject execution hooks around any method call (before/after filters).
10
+ #
11
+ # ExecutionWrapper delegates all method calls to the provided controller instance.
12
+ # Before and after each invocation, it runs the controller's registered hooks.
13
+ #
14
+ # This allows for cleaner separation of concerns and reusable hook logic.
15
+ #
16
+ # @example
17
+ # controller = SomeController.new
18
+ # wrapper = ExecutionWrapper.new(controller)
19
+ # wrapper.call # will run before(:call), call, then after(:call)
20
+ class ExecutionWrapper
21
+ # @param controller [Object] a controller instance responding to method calls
22
+ def initialize(controller)
23
+ @controller = controller
24
+ end
25
+
26
+ # Delegates any method call to the controller and wraps it with before/after hooks
27
+ #
28
+ # @param method_name [Symbol] the name of the method being called
29
+ # @param args [Array] arguments passed to the method
30
+ # @param block [Proc] optional block passed to the method
31
+ # @return [Object] the result of the delegated controller method for Roda to operate on
32
+ def method_missing(method_name, *args, &block)
33
+ @controller.run_before_hooks(method_name)
34
+ result = @controller.public_send(method_name, *args, &block)
35
+ @controller.run_after_hooks(method_name)
36
+ result
37
+ end
38
+
39
+ # Properly supports respond_to? checks by delegating to the controller
40
+ #
41
+ # @param method_name [Symbol] the method name to check
42
+ # @param include_private [Boolean] whether to include private methods
43
+ # @return [Boolean] true if controller responds to the method
44
+ def respond_to_missing?(method_name, include_private = false)
45
+ @controller.respond_to?(method_name, include_private)
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end