karafka 1.4.12 → 2.2.10

Sign up to get free protection for your applications and to get access to all the features.
Files changed (359) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/.github/FUNDING.yml +1 -0
  4. data/.github/ISSUE_TEMPLATE/bug_report.md +10 -9
  5. data/.github/workflows/ci.yml +169 -31
  6. data/.rspec +4 -0
  7. data/.ruby-version +1 -1
  8. data/CHANGELOG.md +716 -607
  9. data/CONTRIBUTING.md +10 -19
  10. data/Gemfile +7 -0
  11. data/Gemfile.lock +69 -92
  12. data/LICENSE +17 -0
  13. data/LICENSE-COMM +89 -0
  14. data/LICENSE-LGPL +165 -0
  15. data/README.md +48 -47
  16. data/bin/benchmarks +99 -0
  17. data/bin/create_token +22 -0
  18. data/bin/integrations +310 -0
  19. data/bin/karafka +5 -14
  20. data/bin/record_rss +50 -0
  21. data/bin/rspecs +6 -0
  22. data/bin/scenario +29 -0
  23. data/bin/stress_many +13 -0
  24. data/bin/stress_one +13 -0
  25. data/bin/verify_license_integrity +37 -0
  26. data/bin/wait_for_kafka +24 -0
  27. data/certs/cert_chain.pem +26 -0
  28. data/certs/karafka-pro.pem +11 -0
  29. data/config/locales/errors.yml +97 -0
  30. data/config/locales/pro_errors.yml +59 -0
  31. data/docker-compose.yml +19 -11
  32. data/karafka.gemspec +26 -22
  33. data/lib/active_job/karafka.rb +17 -0
  34. data/lib/active_job/queue_adapters/karafka_adapter.rb +32 -0
  35. data/lib/karafka/active_job/consumer.rb +49 -0
  36. data/lib/karafka/active_job/current_attributes/loading.rb +36 -0
  37. data/lib/karafka/active_job/current_attributes/persistence.rb +28 -0
  38. data/lib/karafka/active_job/current_attributes.rb +42 -0
  39. data/lib/karafka/active_job/dispatcher.rb +69 -0
  40. data/lib/karafka/active_job/job_extensions.rb +34 -0
  41. data/lib/karafka/active_job/job_options_contract.rb +32 -0
  42. data/lib/karafka/admin.rb +313 -0
  43. data/lib/karafka/app.rb +47 -23
  44. data/lib/karafka/base_consumer.rb +260 -29
  45. data/lib/karafka/cli/base.rb +67 -36
  46. data/lib/karafka/cli/console.rb +18 -12
  47. data/lib/karafka/cli/help.rb +24 -0
  48. data/lib/karafka/cli/info.rb +47 -12
  49. data/lib/karafka/cli/install.rb +23 -14
  50. data/lib/karafka/cli/server.rb +101 -44
  51. data/lib/karafka/cli/topics.rb +146 -0
  52. data/lib/karafka/cli.rb +24 -27
  53. data/lib/karafka/connection/client.rb +553 -90
  54. data/lib/karafka/connection/consumer_group_coordinator.rb +48 -0
  55. data/lib/karafka/connection/listener.rb +294 -38
  56. data/lib/karafka/connection/listeners_batch.rb +40 -0
  57. data/lib/karafka/connection/messages_buffer.rb +84 -0
  58. data/lib/karafka/connection/pauses_manager.rb +46 -0
  59. data/lib/karafka/connection/proxy.rb +98 -0
  60. data/lib/karafka/connection/raw_messages_buffer.rb +101 -0
  61. data/lib/karafka/connection/rebalance_manager.rb +105 -0
  62. data/lib/karafka/contracts/base.rb +17 -0
  63. data/lib/karafka/contracts/config.rb +130 -11
  64. data/lib/karafka/contracts/consumer_group.rb +32 -187
  65. data/lib/karafka/contracts/server_cli_options.rb +80 -19
  66. data/lib/karafka/contracts/topic.rb +65 -0
  67. data/lib/karafka/contracts.rb +1 -1
  68. data/lib/karafka/embedded.rb +36 -0
  69. data/lib/karafka/env.rb +46 -0
  70. data/lib/karafka/errors.rb +37 -21
  71. data/lib/karafka/helpers/async.rb +33 -0
  72. data/lib/karafka/helpers/colorize.rb +26 -0
  73. data/lib/karafka/helpers/multi_delegator.rb +2 -2
  74. data/lib/karafka/instrumentation/callbacks/error.rb +39 -0
  75. data/lib/karafka/instrumentation/callbacks/rebalance.rb +64 -0
  76. data/lib/karafka/instrumentation/callbacks/statistics.rb +51 -0
  77. data/lib/karafka/instrumentation/logger_listener.rb +303 -0
  78. data/lib/karafka/instrumentation/monitor.rb +13 -61
  79. data/lib/karafka/instrumentation/notifications.rb +79 -0
  80. data/lib/karafka/instrumentation/proctitle_listener.rb +7 -16
  81. data/lib/karafka/instrumentation/vendors/appsignal/base.rb +30 -0
  82. data/lib/karafka/instrumentation/vendors/appsignal/client.rb +122 -0
  83. data/lib/karafka/instrumentation/vendors/appsignal/dashboard.json +222 -0
  84. data/lib/karafka/instrumentation/vendors/appsignal/errors_listener.rb +30 -0
  85. data/lib/karafka/instrumentation/vendors/appsignal/metrics_listener.rb +331 -0
  86. data/lib/karafka/instrumentation/vendors/datadog/dashboard.json +1 -0
  87. data/lib/karafka/instrumentation/vendors/datadog/logger_listener.rb +155 -0
  88. data/lib/karafka/instrumentation/vendors/datadog/metrics_listener.rb +264 -0
  89. data/lib/karafka/instrumentation/vendors/kubernetes/liveness_listener.rb +176 -0
  90. data/lib/karafka/licenser.rb +78 -0
  91. data/lib/karafka/messages/batch_metadata.rb +52 -0
  92. data/lib/karafka/messages/builders/batch_metadata.rb +60 -0
  93. data/lib/karafka/messages/builders/message.rb +40 -0
  94. data/lib/karafka/messages/builders/messages.rb +36 -0
  95. data/lib/karafka/{params/params.rb → messages/message.rb} +20 -13
  96. data/lib/karafka/messages/messages.rb +71 -0
  97. data/lib/karafka/{params → messages}/metadata.rb +4 -6
  98. data/lib/karafka/messages/parser.rb +14 -0
  99. data/lib/karafka/messages/seek.rb +12 -0
  100. data/lib/karafka/patches/rdkafka/bindings.rb +122 -0
  101. data/lib/karafka/patches/rdkafka/opaque.rb +36 -0
  102. data/lib/karafka/pro/active_job/consumer.rb +47 -0
  103. data/lib/karafka/pro/active_job/dispatcher.rb +86 -0
  104. data/lib/karafka/pro/active_job/job_options_contract.rb +45 -0
  105. data/lib/karafka/pro/cleaner/errors.rb +27 -0
  106. data/lib/karafka/pro/cleaner/messages/message.rb +46 -0
  107. data/lib/karafka/pro/cleaner/messages/messages.rb +42 -0
  108. data/lib/karafka/pro/cleaner.rb +41 -0
  109. data/lib/karafka/pro/contracts/base.rb +23 -0
  110. data/lib/karafka/pro/contracts/server_cli_options.rb +111 -0
  111. data/lib/karafka/pro/encryption/cipher.rb +58 -0
  112. data/lib/karafka/pro/encryption/contracts/config.rb +79 -0
  113. data/lib/karafka/pro/encryption/errors.rb +27 -0
  114. data/lib/karafka/pro/encryption/messages/middleware.rb +46 -0
  115. data/lib/karafka/pro/encryption/messages/parser.rb +56 -0
  116. data/lib/karafka/pro/encryption/setup/config.rb +48 -0
  117. data/lib/karafka/pro/encryption.rb +47 -0
  118. data/lib/karafka/pro/iterator/expander.rb +95 -0
  119. data/lib/karafka/pro/iterator/tpl_builder.rb +155 -0
  120. data/lib/karafka/pro/iterator.rb +170 -0
  121. data/lib/karafka/pro/loader.rb +106 -0
  122. data/lib/karafka/pro/performance_tracker.rb +84 -0
  123. data/lib/karafka/pro/processing/collapser.rb +62 -0
  124. data/lib/karafka/pro/processing/coordinator.rb +147 -0
  125. data/lib/karafka/pro/processing/filters/base.rb +61 -0
  126. data/lib/karafka/pro/processing/filters/delayer.rb +70 -0
  127. data/lib/karafka/pro/processing/filters/expirer.rb +51 -0
  128. data/lib/karafka/pro/processing/filters/inline_insights_delayer.rb +78 -0
  129. data/lib/karafka/pro/processing/filters/throttler.rb +84 -0
  130. data/lib/karafka/pro/processing/filters/virtual_limiter.rb +52 -0
  131. data/lib/karafka/pro/processing/filters_applier.rb +105 -0
  132. data/lib/karafka/pro/processing/jobs/consume_non_blocking.rb +39 -0
  133. data/lib/karafka/pro/processing/jobs/revoked_non_blocking.rb +37 -0
  134. data/lib/karafka/pro/processing/jobs_builder.rb +50 -0
  135. data/lib/karafka/pro/processing/partitioner.rb +69 -0
  136. data/lib/karafka/pro/processing/scheduler.rb +75 -0
  137. data/lib/karafka/pro/processing/strategies/aj/dlq_ftr_lrj_mom.rb +70 -0
  138. data/lib/karafka/pro/processing/strategies/aj/dlq_ftr_lrj_mom_vp.rb +76 -0
  139. data/lib/karafka/pro/processing/strategies/aj/dlq_ftr_mom.rb +72 -0
  140. data/lib/karafka/pro/processing/strategies/aj/dlq_ftr_mom_vp.rb +76 -0
  141. data/lib/karafka/pro/processing/strategies/aj/dlq_lrj_mom.rb +66 -0
  142. data/lib/karafka/pro/processing/strategies/aj/dlq_lrj_mom_vp.rb +70 -0
  143. data/lib/karafka/pro/processing/strategies/aj/dlq_mom.rb +64 -0
  144. data/lib/karafka/pro/processing/strategies/aj/dlq_mom_vp.rb +69 -0
  145. data/lib/karafka/pro/processing/strategies/aj/ftr_lrj_mom.rb +38 -0
  146. data/lib/karafka/pro/processing/strategies/aj/ftr_lrj_mom_vp.rb +66 -0
  147. data/lib/karafka/pro/processing/strategies/aj/ftr_mom.rb +38 -0
  148. data/lib/karafka/pro/processing/strategies/aj/ftr_mom_vp.rb +58 -0
  149. data/lib/karafka/pro/processing/strategies/aj/lrj_mom.rb +37 -0
  150. data/lib/karafka/pro/processing/strategies/aj/lrj_mom_vp.rb +82 -0
  151. data/lib/karafka/pro/processing/strategies/aj/mom.rb +36 -0
  152. data/lib/karafka/pro/processing/strategies/aj/mom_vp.rb +52 -0
  153. data/lib/karafka/pro/processing/strategies/base.rb +26 -0
  154. data/lib/karafka/pro/processing/strategies/default.rb +105 -0
  155. data/lib/karafka/pro/processing/strategies/dlq/default.rb +137 -0
  156. data/lib/karafka/pro/processing/strategies/dlq/ftr.rb +61 -0
  157. data/lib/karafka/pro/processing/strategies/dlq/ftr_lrj.rb +75 -0
  158. data/lib/karafka/pro/processing/strategies/dlq/ftr_lrj_mom.rb +71 -0
  159. data/lib/karafka/pro/processing/strategies/dlq/ftr_lrj_mom_vp.rb +43 -0
  160. data/lib/karafka/pro/processing/strategies/dlq/ftr_lrj_vp.rb +41 -0
  161. data/lib/karafka/pro/processing/strategies/dlq/ftr_mom.rb +69 -0
  162. data/lib/karafka/pro/processing/strategies/dlq/ftr_mom_vp.rb +41 -0
  163. data/lib/karafka/pro/processing/strategies/dlq/ftr_vp.rb +40 -0
  164. data/lib/karafka/pro/processing/strategies/dlq/lrj.rb +64 -0
  165. data/lib/karafka/pro/processing/strategies/dlq/lrj_mom.rb +65 -0
  166. data/lib/karafka/pro/processing/strategies/dlq/lrj_mom_vp.rb +36 -0
  167. data/lib/karafka/pro/processing/strategies/dlq/lrj_vp.rb +39 -0
  168. data/lib/karafka/pro/processing/strategies/dlq/mom.rb +68 -0
  169. data/lib/karafka/pro/processing/strategies/dlq/mom_vp.rb +37 -0
  170. data/lib/karafka/pro/processing/strategies/dlq/vp.rb +40 -0
  171. data/lib/karafka/pro/processing/strategies/ftr/default.rb +111 -0
  172. data/lib/karafka/pro/processing/strategies/ftr/vp.rb +40 -0
  173. data/lib/karafka/pro/processing/strategies/lrj/default.rb +85 -0
  174. data/lib/karafka/pro/processing/strategies/lrj/ftr.rb +69 -0
  175. data/lib/karafka/pro/processing/strategies/lrj/ftr_mom.rb +67 -0
  176. data/lib/karafka/pro/processing/strategies/lrj/ftr_mom_vp.rb +40 -0
  177. data/lib/karafka/pro/processing/strategies/lrj/ftr_vp.rb +39 -0
  178. data/lib/karafka/pro/processing/strategies/lrj/mom.rb +77 -0
  179. data/lib/karafka/pro/processing/strategies/lrj/mom_vp.rb +38 -0
  180. data/lib/karafka/pro/processing/strategies/lrj/vp.rb +36 -0
  181. data/lib/karafka/pro/processing/strategies/mom/default.rb +46 -0
  182. data/lib/karafka/pro/processing/strategies/mom/ftr.rb +53 -0
  183. data/lib/karafka/pro/processing/strategies/mom/ftr_vp.rb +37 -0
  184. data/lib/karafka/pro/processing/strategies/mom/vp.rb +35 -0
  185. data/lib/karafka/pro/processing/strategies/vp/default.rb +124 -0
  186. data/lib/karafka/pro/processing/strategies.rb +22 -0
  187. data/lib/karafka/pro/processing/strategy_selector.rb +84 -0
  188. data/lib/karafka/pro/processing/virtual_offset_manager.rb +147 -0
  189. data/lib/karafka/pro/routing/features/active_job/builder.rb +45 -0
  190. data/lib/karafka/pro/routing/features/active_job.rb +26 -0
  191. data/lib/karafka/pro/routing/features/base.rb +24 -0
  192. data/lib/karafka/pro/routing/features/dead_letter_queue/contracts/topic.rb +53 -0
  193. data/lib/karafka/pro/routing/features/dead_letter_queue.rb +27 -0
  194. data/lib/karafka/pro/routing/features/delaying/config.rb +27 -0
  195. data/lib/karafka/pro/routing/features/delaying/contracts/topic.rb +41 -0
  196. data/lib/karafka/pro/routing/features/delaying/topic.rb +59 -0
  197. data/lib/karafka/pro/routing/features/delaying.rb +29 -0
  198. data/lib/karafka/pro/routing/features/expiring/config.rb +27 -0
  199. data/lib/karafka/pro/routing/features/expiring/contracts/topic.rb +41 -0
  200. data/lib/karafka/pro/routing/features/expiring/topic.rb +59 -0
  201. data/lib/karafka/pro/routing/features/expiring.rb +27 -0
  202. data/lib/karafka/pro/routing/features/filtering/config.rb +40 -0
  203. data/lib/karafka/pro/routing/features/filtering/contracts/topic.rb +44 -0
  204. data/lib/karafka/pro/routing/features/filtering/topic.rb +51 -0
  205. data/lib/karafka/pro/routing/features/filtering.rb +27 -0
  206. data/lib/karafka/pro/routing/features/inline_insights/config.rb +32 -0
  207. data/lib/karafka/pro/routing/features/inline_insights/contracts/topic.rb +41 -0
  208. data/lib/karafka/pro/routing/features/inline_insights/topic.rb +52 -0
  209. data/lib/karafka/pro/routing/features/inline_insights.rb +26 -0
  210. data/lib/karafka/pro/routing/features/long_running_job/config.rb +28 -0
  211. data/lib/karafka/pro/routing/features/long_running_job/contracts/topic.rb +40 -0
  212. data/lib/karafka/pro/routing/features/long_running_job/topic.rb +42 -0
  213. data/lib/karafka/pro/routing/features/long_running_job.rb +28 -0
  214. data/lib/karafka/pro/routing/features/patterns/builder.rb +38 -0
  215. data/lib/karafka/pro/routing/features/patterns/config.rb +54 -0
  216. data/lib/karafka/pro/routing/features/patterns/consumer_group.rb +72 -0
  217. data/lib/karafka/pro/routing/features/patterns/contracts/consumer_group.rb +62 -0
  218. data/lib/karafka/pro/routing/features/patterns/contracts/pattern.rb +46 -0
  219. data/lib/karafka/pro/routing/features/patterns/contracts/topic.rb +41 -0
  220. data/lib/karafka/pro/routing/features/patterns/detector.rb +71 -0
  221. data/lib/karafka/pro/routing/features/patterns/pattern.rb +95 -0
  222. data/lib/karafka/pro/routing/features/patterns/patterns.rb +35 -0
  223. data/lib/karafka/pro/routing/features/patterns/topic.rb +50 -0
  224. data/lib/karafka/pro/routing/features/patterns/topics.rb +53 -0
  225. data/lib/karafka/pro/routing/features/patterns.rb +33 -0
  226. data/lib/karafka/pro/routing/features/pausing/contracts/topic.rb +51 -0
  227. data/lib/karafka/pro/routing/features/pausing/topic.rb +44 -0
  228. data/lib/karafka/pro/routing/features/pausing.rb +25 -0
  229. data/lib/karafka/pro/routing/features/throttling/config.rb +32 -0
  230. data/lib/karafka/pro/routing/features/throttling/contracts/topic.rb +44 -0
  231. data/lib/karafka/pro/routing/features/throttling/topic.rb +69 -0
  232. data/lib/karafka/pro/routing/features/throttling.rb +30 -0
  233. data/lib/karafka/pro/routing/features/virtual_partitions/config.rb +30 -0
  234. data/lib/karafka/pro/routing/features/virtual_partitions/contracts/topic.rb +55 -0
  235. data/lib/karafka/pro/routing/features/virtual_partitions/topic.rb +56 -0
  236. data/lib/karafka/pro/routing/features/virtual_partitions.rb +27 -0
  237. data/lib/karafka/pro.rb +13 -0
  238. data/lib/karafka/process.rb +24 -8
  239. data/lib/karafka/processing/coordinator.rb +181 -0
  240. data/lib/karafka/processing/coordinators_buffer.rb +62 -0
  241. data/lib/karafka/processing/executor.rb +155 -0
  242. data/lib/karafka/processing/executors_buffer.rb +72 -0
  243. data/lib/karafka/processing/expansions_selector.rb +22 -0
  244. data/lib/karafka/processing/inline_insights/consumer.rb +41 -0
  245. data/lib/karafka/processing/inline_insights/listener.rb +19 -0
  246. data/lib/karafka/processing/inline_insights/tracker.rb +128 -0
  247. data/lib/karafka/processing/jobs/base.rb +55 -0
  248. data/lib/karafka/processing/jobs/consume.rb +45 -0
  249. data/lib/karafka/processing/jobs/idle.rb +24 -0
  250. data/lib/karafka/processing/jobs/revoked.rb +22 -0
  251. data/lib/karafka/processing/jobs/shutdown.rb +23 -0
  252. data/lib/karafka/processing/jobs_builder.rb +28 -0
  253. data/lib/karafka/processing/jobs_queue.rb +150 -0
  254. data/lib/karafka/processing/partitioner.rb +24 -0
  255. data/lib/karafka/processing/result.rb +42 -0
  256. data/lib/karafka/processing/scheduler.rb +22 -0
  257. data/lib/karafka/processing/strategies/aj_dlq_mom.rb +44 -0
  258. data/lib/karafka/processing/strategies/aj_mom.rb +21 -0
  259. data/lib/karafka/processing/strategies/base.rb +52 -0
  260. data/lib/karafka/processing/strategies/default.rb +158 -0
  261. data/lib/karafka/processing/strategies/dlq.rb +88 -0
  262. data/lib/karafka/processing/strategies/dlq_mom.rb +49 -0
  263. data/lib/karafka/processing/strategies/mom.rb +29 -0
  264. data/lib/karafka/processing/strategy_selector.rb +47 -0
  265. data/lib/karafka/processing/worker.rb +93 -0
  266. data/lib/karafka/processing/workers_batch.rb +27 -0
  267. data/lib/karafka/railtie.rb +141 -0
  268. data/lib/karafka/routing/activity_manager.rb +84 -0
  269. data/lib/karafka/routing/builder.rb +45 -19
  270. data/lib/karafka/routing/consumer_group.rb +56 -20
  271. data/lib/karafka/routing/consumer_mapper.rb +1 -12
  272. data/lib/karafka/routing/features/active_job/builder.rb +33 -0
  273. data/lib/karafka/routing/features/active_job/config.rb +15 -0
  274. data/lib/karafka/routing/features/active_job/contracts/topic.rb +44 -0
  275. data/lib/karafka/routing/features/active_job/proxy.rb +14 -0
  276. data/lib/karafka/routing/features/active_job/topic.rb +33 -0
  277. data/lib/karafka/routing/features/active_job.rb +13 -0
  278. data/lib/karafka/routing/features/base/expander.rb +59 -0
  279. data/lib/karafka/routing/features/base.rb +71 -0
  280. data/lib/karafka/routing/features/dead_letter_queue/config.rb +19 -0
  281. data/lib/karafka/routing/features/dead_letter_queue/contracts/topic.rb +46 -0
  282. data/lib/karafka/routing/features/dead_letter_queue/topic.rb +41 -0
  283. data/lib/karafka/routing/features/dead_letter_queue.rb +16 -0
  284. data/lib/karafka/routing/features/declaratives/config.rb +18 -0
  285. data/lib/karafka/routing/features/declaratives/contracts/topic.rb +33 -0
  286. data/lib/karafka/routing/features/declaratives/topic.rb +44 -0
  287. data/lib/karafka/routing/features/declaratives.rb +14 -0
  288. data/lib/karafka/routing/features/inline_insights/config.rb +15 -0
  289. data/lib/karafka/routing/features/inline_insights/contracts/topic.rb +27 -0
  290. data/lib/karafka/routing/features/inline_insights/topic.rb +31 -0
  291. data/lib/karafka/routing/features/inline_insights.rb +40 -0
  292. data/lib/karafka/routing/features/manual_offset_management/config.rb +15 -0
  293. data/lib/karafka/routing/features/manual_offset_management/contracts/topic.rb +27 -0
  294. data/lib/karafka/routing/features/manual_offset_management/topic.rb +35 -0
  295. data/lib/karafka/routing/features/manual_offset_management.rb +18 -0
  296. data/lib/karafka/routing/proxy.rb +22 -21
  297. data/lib/karafka/routing/router.rb +24 -10
  298. data/lib/karafka/routing/subscription_group.rb +110 -0
  299. data/lib/karafka/routing/subscription_groups_builder.rb +65 -0
  300. data/lib/karafka/routing/topic.rb +87 -24
  301. data/lib/karafka/routing/topics.rb +46 -0
  302. data/lib/karafka/runner.rb +52 -0
  303. data/lib/karafka/serialization/json/deserializer.rb +7 -15
  304. data/lib/karafka/server.rb +113 -37
  305. data/lib/karafka/setup/attributes_map.rb +348 -0
  306. data/lib/karafka/setup/config.rb +256 -175
  307. data/lib/karafka/status.rb +54 -7
  308. data/lib/karafka/templates/example_consumer.rb.erb +16 -0
  309. data/lib/karafka/templates/karafka.rb.erb +33 -55
  310. data/lib/karafka/time_trackers/base.rb +14 -0
  311. data/lib/karafka/time_trackers/pause.rb +122 -0
  312. data/lib/karafka/time_trackers/poll.rb +69 -0
  313. data/lib/karafka/version.rb +1 -1
  314. data/lib/karafka.rb +91 -17
  315. data/renovate.json +9 -0
  316. data.tar.gz.sig +0 -0
  317. metadata +330 -168
  318. metadata.gz.sig +0 -0
  319. data/MIT-LICENCE +0 -18
  320. data/certs/mensfeld.pem +0 -25
  321. data/config/errors.yml +0 -41
  322. data/lib/karafka/assignment_strategies/round_robin.rb +0 -13
  323. data/lib/karafka/attributes_map.rb +0 -63
  324. data/lib/karafka/backends/inline.rb +0 -16
  325. data/lib/karafka/base_responder.rb +0 -226
  326. data/lib/karafka/cli/flow.rb +0 -48
  327. data/lib/karafka/cli/missingno.rb +0 -19
  328. data/lib/karafka/code_reloader.rb +0 -67
  329. data/lib/karafka/connection/api_adapter.rb +0 -158
  330. data/lib/karafka/connection/batch_delegator.rb +0 -55
  331. data/lib/karafka/connection/builder.rb +0 -23
  332. data/lib/karafka/connection/message_delegator.rb +0 -36
  333. data/lib/karafka/consumers/batch_metadata.rb +0 -10
  334. data/lib/karafka/consumers/callbacks.rb +0 -71
  335. data/lib/karafka/consumers/includer.rb +0 -64
  336. data/lib/karafka/consumers/responders.rb +0 -24
  337. data/lib/karafka/consumers/single_params.rb +0 -15
  338. data/lib/karafka/contracts/consumer_group_topic.rb +0 -19
  339. data/lib/karafka/contracts/responder_usage.rb +0 -54
  340. data/lib/karafka/fetcher.rb +0 -42
  341. data/lib/karafka/helpers/class_matcher.rb +0 -88
  342. data/lib/karafka/helpers/config_retriever.rb +0 -46
  343. data/lib/karafka/helpers/inflector.rb +0 -26
  344. data/lib/karafka/instrumentation/stdout_listener.rb +0 -140
  345. data/lib/karafka/params/batch_metadata.rb +0 -26
  346. data/lib/karafka/params/builders/batch_metadata.rb +0 -30
  347. data/lib/karafka/params/builders/params.rb +0 -38
  348. data/lib/karafka/params/builders/params_batch.rb +0 -25
  349. data/lib/karafka/params/params_batch.rb +0 -60
  350. data/lib/karafka/patches/ruby_kafka.rb +0 -47
  351. data/lib/karafka/persistence/client.rb +0 -29
  352. data/lib/karafka/persistence/consumers.rb +0 -45
  353. data/lib/karafka/persistence/topics.rb +0 -48
  354. data/lib/karafka/responders/builder.rb +0 -36
  355. data/lib/karafka/responders/topic.rb +0 -55
  356. data/lib/karafka/routing/topic_mapper.rb +0 -53
  357. data/lib/karafka/serialization/json/serializer.rb +0 -31
  358. data/lib/karafka/setup/configurators/water_drop.rb +0 -36
  359. data/lib/karafka/templates/application_responder.rb.erb +0 -11
@@ -0,0 +1,69 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Karafka
4
+ module ActiveJob
5
+ # Dispatcher that sends the ActiveJob job to a proper topic based on the queue name
6
+ class Dispatcher
7
+ # Defaults for dispatching
8
+ # The can be updated by using `#karafka_options` on the job
9
+ DEFAULTS = {
10
+ dispatch_method: :produce_async,
11
+ dispatch_many_method: :produce_many_async
12
+ }.freeze
13
+
14
+ private_constant :DEFAULTS
15
+
16
+ # @param job [ActiveJob::Base] job
17
+ def dispatch(job)
18
+ ::Karafka.producer.public_send(
19
+ fetch_option(job, :dispatch_method, DEFAULTS),
20
+ topic: job.queue_name,
21
+ payload: ::ActiveSupport::JSON.encode(serialize_job(job))
22
+ )
23
+ end
24
+
25
+ # Bulk dispatches multiple jobs using the Rails 7.1+ API
26
+ # @param jobs [Array<ActiveJob::Base>] jobs we want to dispatch
27
+ def dispatch_many(jobs)
28
+ # Group jobs by their desired dispatch method
29
+ # It can be configured per job class, so we need to make sure we divide them
30
+ dispatches = Hash.new { |hash, key| hash[key] = [] }
31
+
32
+ jobs.each do |job|
33
+ d_method = fetch_option(job, :dispatch_many_method, DEFAULTS)
34
+
35
+ dispatches[d_method] << {
36
+ topic: job.queue_name,
37
+ payload: ::ActiveSupport::JSON.encode(serialize_job(job))
38
+ }
39
+ end
40
+
41
+ dispatches.each do |type, messages|
42
+ ::Karafka.producer.public_send(
43
+ type,
44
+ messages
45
+ )
46
+ end
47
+ end
48
+
49
+ private
50
+
51
+ # @param job [ActiveJob::Base] job
52
+ # @param key [Symbol] key we want to fetch
53
+ # @param defaults [Hash]
54
+ # @return [Object] options we are interested in
55
+ def fetch_option(job, key, defaults)
56
+ job
57
+ .class
58
+ .karafka_options
59
+ .fetch(key, defaults.fetch(key))
60
+ end
61
+
62
+ # @param job [ActiveJob::Base] job
63
+ # @return [Hash] json representation of the job
64
+ def serialize_job(job)
65
+ job.serialize
66
+ end
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Karafka
4
+ module ActiveJob
5
+ # Allows for setting karafka specific options in ActiveJob jobs
6
+ module JobExtensions
7
+ class << self
8
+ # Defines all the needed accessors and sets defaults
9
+ # @param klass [ActiveJob::Base] active job base
10
+ def extended(klass)
11
+ klass.class_attribute :_karafka_options
12
+ klass._karafka_options = {}
13
+ end
14
+ end
15
+
16
+ # @param new_options [Hash] additional options that allow for jobs Karafka related options
17
+ # customization
18
+ # @return [Hash] karafka options
19
+ def karafka_options(new_options = {})
20
+ return _karafka_options if new_options.empty?
21
+
22
+ # Make sure, that karafka options that someone wants to use are valid before assigning
23
+ # them
24
+ App.config.internal.active_job.job_options_contract.validate!(new_options)
25
+
26
+ new_options.each do |name, value|
27
+ _karafka_options[name] = value
28
+ end
29
+
30
+ _karafka_options
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Karafka
4
+ module ActiveJob
5
+ # Contract for validating the options that can be altered with `#karafka_options` per job class
6
+ # @note We keep this in the `Karafka::ActiveJob` namespace instead of `Karafka::Contracts` as
7
+ # we want to keep ActiveJob related Karafka components outside of the core Karafka code and
8
+ # all in the same place
9
+ class JobOptionsContract < Contracts::Base
10
+ configure do |config|
11
+ config.error_messages = YAML.safe_load(
12
+ File.read(
13
+ File.join(Karafka.gem_root, 'config', 'locales', 'errors.yml')
14
+ )
15
+ ).fetch('en').fetch('validations').fetch('job_options')
16
+ end
17
+
18
+ optional(:dispatch_method) do |val|
19
+ %i[
20
+ produce_async
21
+ produce_sync
22
+ ].include?(val)
23
+ end
24
+ optional(:dispatch_many_method) do |val|
25
+ %i[
26
+ produce_many_async
27
+ produce_many_sync
28
+ ].include?(val)
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,313 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Karafka
4
+ # Simple admin actions that we can perform via Karafka on our Kafka cluster
5
+ #
6
+ # @note It always initializes a new admin instance as we want to ensure it is always closed
7
+ # Since admin actions are not performed that often, that should be ok.
8
+ #
9
+ # @note It always uses the primary defined cluster and does not support multi-cluster work.
10
+ # Cluster on which operations are performed can be changed via `admin.kafka` config, however
11
+ # there is no multi-cluster runtime support.
12
+ module Admin
13
+ class << self
14
+ # Allows us to read messages from the topic
15
+ #
16
+ # @param name [String, Symbol] topic name
17
+ # @param partition [Integer] partition
18
+ # @param count [Integer] how many messages we want to get at most
19
+ # @param start_offset [Integer, Time] offset from which we should start. If -1 is provided
20
+ # (default) we will start from the latest offset. If time is provided, the appropriate
21
+ # offset will be resolved. If negative beyond -1 is provided, we move backwards more.
22
+ # @param settings [Hash] kafka extra settings (optional)
23
+ #
24
+ # @return [Array<Karafka::Messages::Message>] array with messages
25
+ def read_topic(name, partition, count, start_offset = -1, settings = {})
26
+ messages = []
27
+ tpl = Rdkafka::Consumer::TopicPartitionList.new
28
+ low_offset, high_offset = nil
29
+
30
+ with_consumer(settings) do |consumer|
31
+ # Convert the time offset (if needed)
32
+ start_offset = resolve_offset(consumer, name.to_s, partition, start_offset)
33
+
34
+ low_offset, high_offset = consumer.query_watermark_offsets(name, partition)
35
+
36
+ # Select offset dynamically if -1 or less and move backwards with the negative
37
+ # offset, allowing to start from N messages back from high-watermark
38
+ start_offset = high_offset - count - start_offset.abs + 1 if start_offset.negative?
39
+ start_offset = low_offset if start_offset.negative?
40
+
41
+ # Build the requested range - since first element is on the start offset we need to
42
+ # subtract one from requested count to end up with expected number of elements
43
+ requested_range = (start_offset..start_offset + (count - 1))
44
+ # Establish theoretical available range. Note, that this does not handle cases related to
45
+ # log retention or compaction
46
+ available_range = (low_offset..(high_offset - 1))
47
+ # Select only offset that we can select. This will remove all the potential offsets that
48
+ # are below the low watermark offset
49
+ possible_range = requested_range.select { |offset| available_range.include?(offset) }
50
+
51
+ start_offset = possible_range.first
52
+ count = possible_range.count
53
+
54
+ tpl.add_topic_and_partitions_with_offsets(name, partition => start_offset)
55
+ consumer.assign(tpl)
56
+
57
+ # We should poll as long as we don't have all the messages that we need or as long as
58
+ # we do not read all the messages from the topic
59
+ loop do
60
+ # If we've got as many messages as we've wanted stop
61
+ break if messages.size >= count
62
+
63
+ message = consumer.poll(200)
64
+
65
+ next unless message
66
+
67
+ # If the message we've got is beyond the requested range, stop
68
+ break unless possible_range.include?(message.offset)
69
+
70
+ messages << message
71
+ rescue Rdkafka::RdkafkaError => e
72
+ # End of partition
73
+ break if e.code == :partition_eof
74
+
75
+ raise e
76
+ end
77
+ end
78
+
79
+ # Use topic from routes if we can match it or create a dummy one
80
+ # Dummy one is used in case we cannot match the topic with routes. This can happen
81
+ # when admin API is used to read topics that are not part of the routing
82
+ topic = ::Karafka::Routing::Router.find_or_initialize_by_name(name)
83
+
84
+ messages.map! do |message|
85
+ Messages::Builders::Message.call(
86
+ message,
87
+ topic,
88
+ Time.now
89
+ )
90
+ end
91
+ end
92
+
93
+ # Creates Kafka topic with given settings
94
+ #
95
+ # @param name [String] topic name
96
+ # @param partitions [Integer] number of partitions we expect
97
+ # @param replication_factor [Integer] number of replicas
98
+ # @param topic_config [Hash] topic config details as described here:
99
+ # https://kafka.apache.org/documentation/#topicconfigs
100
+ def create_topic(name, partitions, replication_factor, topic_config = {})
101
+ with_admin do |admin|
102
+ handler = admin.create_topic(name, partitions, replication_factor, topic_config)
103
+
104
+ with_re_wait(
105
+ -> { handler.wait(max_wait_timeout: app_config.admin.max_wait_time) },
106
+ -> { topics_names.include?(name) }
107
+ )
108
+ end
109
+ end
110
+
111
+ # Deleted a given topic
112
+ #
113
+ # @param name [String] topic name
114
+ def delete_topic(name)
115
+ with_admin do |admin|
116
+ handler = admin.delete_topic(name)
117
+
118
+ with_re_wait(
119
+ -> { handler.wait(max_wait_timeout: app_config.admin.max_wait_time) },
120
+ -> { !topics_names.include?(name) }
121
+ )
122
+ end
123
+ end
124
+
125
+ # Creates more partitions for a given topic
126
+ #
127
+ # @param name [String] topic name
128
+ # @param partitions [Integer] total number of partitions we expect to end up with
129
+ def create_partitions(name, partitions)
130
+ with_admin do |admin|
131
+ handler = admin.create_partitions(name, partitions)
132
+
133
+ with_re_wait(
134
+ -> { handler.wait(max_wait_timeout: app_config.admin.max_wait_time) },
135
+ -> { topic(name).fetch(:partition_count) >= partitions }
136
+ )
137
+ end
138
+ end
139
+
140
+ # Fetches the watermark offsets for a given topic partition
141
+ #
142
+ # @param name [String, Symbol] topic name
143
+ # @param partition [Integer] partition
144
+ # @return [Array<Integer, Integer>] low watermark offset and high watermark offset
145
+ def read_watermark_offsets(name, partition)
146
+ with_consumer do |consumer|
147
+ # For newly created topics or in cases where we're trying to get them but there is no
148
+ # leader, this can fail. It happens more often for new topics under KRaft, however we
149
+ # still want to make sure things operate as expected even then
150
+ with_rdkafka_retry(codes: %i[not_leader_for_partition]) do
151
+ consumer.query_watermark_offsets(name, partition)
152
+ end
153
+ end
154
+ end
155
+
156
+ # @return [Rdkafka::Metadata] cluster metadata info
157
+ def cluster_info
158
+ with_admin do |admin|
159
+ admin.instance_variable_get('@native_kafka').with_inner do |inner|
160
+ Rdkafka::Metadata.new(inner)
161
+ end
162
+ end
163
+ end
164
+
165
+ # Creates consumer instance and yields it. After usage it closes the consumer instance
166
+ # This API can be used in other pieces of code and allows for low-level consumer usage
167
+ #
168
+ # @param settings [Hash] extra settings to customize consumer
169
+ #
170
+ # @note We always ship and yield a proxied consumer because admin API performance is not
171
+ # that relevant. That is, there are no high frequency calls that would have to be delegated
172
+ def with_consumer(settings = {})
173
+ consumer = config(:consumer, settings).consumer
174
+ proxy = ::Karafka::Connection::Proxy.new(consumer)
175
+ yield(proxy)
176
+ ensure
177
+ # Always unsubscribe consumer just to be sure, that no metadata requests are running
178
+ # when we close the consumer. This in theory should prevent from some race-conditions
179
+ # that originate from librdkafka
180
+ begin
181
+ consumer&.unsubscribe
182
+ # Ignore any errors and continue to close consumer despite them
183
+ rescue Rdkafka::RdkafkaError
184
+ nil
185
+ end
186
+
187
+ consumer&.close
188
+ end
189
+
190
+ private
191
+
192
+ # @return [Array<String>] topics names
193
+ def topics_names
194
+ cluster_info.topics.map { |topic| topic.fetch(:topic_name) }
195
+ end
196
+
197
+ # Finds details about given topic
198
+ # @param name [String] topic name
199
+ # @return [Hash] topic details
200
+ def topic(name)
201
+ cluster_info.topics.find { |topic| topic[:topic_name] == name }
202
+ end
203
+
204
+ # Creates admin instance and yields it. After usage it closes the admin instance
205
+ def with_admin
206
+ admin = config(:producer, {}).admin
207
+ yield(admin)
208
+ ensure
209
+ admin&.close
210
+ end
211
+
212
+ # There are some cases where rdkafka admin operations finish successfully but without the
213
+ # callback being triggered to materialize the post-promise object. Until this is fixed we
214
+ # can figure out, that operation we wanted to do finished successfully by checking that the
215
+ # effect of the command (new topic, more partitions, etc) is handled. Exactly for that we
216
+ # use the breaker. It we get a timeout, we can check that what we wanted to achieve has
217
+ # happened via the breaker check, hence we do not need to wait any longer.
218
+ #
219
+ # @param handler [Proc] the wait handler operation
220
+ # @param breaker [Proc] extra condition upon timeout that indicates things were finished ok
221
+ def with_re_wait(handler, breaker)
222
+ attempt ||= 0
223
+ attempt += 1
224
+
225
+ handler.call
226
+
227
+ # If breaker does not operate, it means that the requested change was applied but is still
228
+ # not visible and we need to wait
229
+ raise(Errors::ResultNotVisibleError) unless breaker.call
230
+ rescue Rdkafka::AbstractHandle::WaitTimeoutError, Errors::ResultNotVisibleError
231
+ return if breaker.call
232
+
233
+ retry if attempt <= app_config.admin.max_attempts
234
+
235
+ raise
236
+ end
237
+
238
+ # Handles retries for rdkafka related errors that we specify in `:codes`.
239
+ #
240
+ # Some operations temporarily fail, especially for cases where we changed something fast
241
+ # like topic creation or repartitioning. In cases like this it is ok to retry operations that
242
+ # do not change the state as it will usually recover.
243
+ #
244
+ # @param codes [Array<Symbol>] librdkafka error codes on which we want to retry
245
+ # @param max_attempts [Integer] number of attempts (including initial) after which we should
246
+ # give up
247
+ #
248
+ # @note This code implements a simple backoff that increases with each attempt.
249
+ def with_rdkafka_retry(codes:, max_attempts: 5)
250
+ attempt ||= 0
251
+ attempt += 1
252
+
253
+ yield
254
+ rescue Rdkafka::RdkafkaError => e
255
+ raise unless codes.include?(e.code)
256
+ raise if attempt >= max_attempts
257
+
258
+ sleep(max_attempts)
259
+
260
+ retry
261
+ end
262
+
263
+ # @param type [Symbol] type of config we want
264
+ # @param settings [Hash] extra settings for config (if needed)
265
+ # @return [::Rdkafka::Config] rdkafka config
266
+ def config(type, settings)
267
+ group_id = app_config.consumer_mapper.call(
268
+ app_config.admin.group_id
269
+ )
270
+
271
+ app_config
272
+ .kafka
273
+ .then(&:dup)
274
+ .merge(app_config.admin.kafka)
275
+ .merge!(settings)
276
+ .tap { |config| config[:'group.id'] = group_id }
277
+ .then { |config| Karafka::Setup::AttributesMap.public_send(type, config) }
278
+ .then { |config| ::Rdkafka::Config.new(config) }
279
+ end
280
+
281
+ # Resolves the offset if offset is in a time format. Otherwise returns the offset without
282
+ # resolving.
283
+ # @param consumer [::Rdkafka::Consumer]
284
+ # @param name [String, Symbol] expected topic name
285
+ # @param partition [Integer]
286
+ # @param offset [Integer, Time]
287
+ # @return [Integer] expected offset
288
+ def resolve_offset(consumer, name, partition, offset)
289
+ if offset.is_a?(Time)
290
+ tpl = ::Rdkafka::Consumer::TopicPartitionList.new
291
+ tpl.add_topic_and_partitions_with_offsets(
292
+ name, partition => offset
293
+ )
294
+
295
+ real_offsets = consumer.offsets_for_times(tpl)
296
+ detected_offset = real_offsets
297
+ .to_h
298
+ .fetch(name)
299
+ .find { |p_data| p_data.partition == partition }
300
+
301
+ detected_offset&.offset || raise(Errors::InvalidTimeBasedOffsetError)
302
+ else
303
+ offset
304
+ end
305
+ end
306
+
307
+ # @return [Karafka::Core::Configurable::Node] root node config
308
+ def app_config
309
+ ::Karafka::App.config
310
+ end
311
+ end
312
+ end
313
+ end
data/lib/karafka/app.rb CHANGED
@@ -6,35 +6,57 @@ module Karafka
6
6
  extend Setup::Dsl
7
7
 
8
8
  class << self
9
- # Sets up all the internal components and bootstrap whole app
10
- # We need to know details about consumers in order to setup components,
11
- # that's why we don't setup them after std setup is done
12
- # @raise [Karafka::Errors::InvalidConfigurationError] raised when configuration
13
- # doesn't match with the config contract
14
- def boot!
15
- initialize!
16
- Setup::Config.validate!
17
- Setup::Config.setup_components
18
- initialized!
9
+ # @return [Karafka::Routing::Builder] consumers builder instance alias
10
+ def consumer_groups
11
+ config
12
+ .internal
13
+ .routing
14
+ .builder
19
15
  end
20
16
 
21
- # @return [Karafka::Routing::Builder] consumers builder instance
22
- def consumer_groups
23
- config.internal.routing_builder
17
+ # @return [Hash] active subscription groups grouped based on consumer group in a hash
18
+ def subscription_groups
19
+ # We first build all the subscription groups, so they all get the same position, despite
20
+ # later narrowing that. It allows us to maintain same position number for static members
21
+ # even when we want to run subset of consumer groups or subscription groups
22
+ #
23
+ # We then narrow this to active consumer groups from which we select active subscription
24
+ # groups.
25
+ consumer_groups
26
+ .map { |cg| [cg, cg.subscription_groups] }
27
+ .select { |cg, _| cg.active? }
28
+ .select { |_, sgs| sgs.delete_if { |sg| !sg.active? } }
29
+ .delete_if { |_, sgs| sgs.empty? }
30
+ .each { |_, sgs| sgs.each { |sg| sg.topics.delete_if { |top| !top.active? } } }
31
+ .each { |_, sgs| sgs.delete_if { |sg| sg.topics.empty? } }
32
+ .reject { |cg, _| cg.subscription_groups.empty? }
33
+ .to_h
24
34
  end
25
35
 
26
- # Triggers reload of all cached Karafka app components, so we can use in-process
27
- # in-development hot code reloading without Karafka process restart
28
- def reload
29
- Karafka::Persistence::Consumers.clear
30
- Karafka::Persistence::Topics.clear
31
- config.internal.routing_builder.reload
36
+ # Just a nicer name for the consumer groups
37
+ alias routes consumer_groups
38
+
39
+ # Allow for easier status management via `Karafka::App` by aliasing status methods here
40
+ Status::STATES.each do |state, transition|
41
+ class_eval <<~RUBY, __FILE__, __LINE__ + 1
42
+ def #{state}
43
+ App.config.internal.status.#{state}
44
+ end
45
+
46
+ def #{state}?
47
+ App.config.internal.status.#{state}?
48
+ end
49
+
50
+ def #{transition}
51
+ App.config.internal.status.#{transition}
52
+ end
53
+ RUBY
32
54
  end
33
55
 
34
- Status.instance_methods(false).each do |delegated|
35
- define_method(delegated) do
36
- App.config.internal.status.send(delegated)
37
- end
56
+ # @return [Boolean] true if we should be done in general with processing anything
57
+ # @note It is a meta status from the status object
58
+ def done?
59
+ App.config.internal.status.done?
38
60
  end
39
61
 
40
62
  # Methods that should be delegated to Karafka module
@@ -42,7 +64,9 @@ module Karafka
42
64
  root
43
65
  env
44
66
  logger
67
+ producer
45
68
  monitor
69
+ pro?
46
70
  ].each do |delegated|
47
71
  define_method(delegated) do
48
72
  Karafka.send(delegated)