karafka 2.4.0 → 2.4.18

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 (326) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/.github/ISSUE_TEMPLATE/bug_report.md +26 -34
  4. data/.github/workflows/ci.yml +18 -6
  5. data/.ruby-version +1 -1
  6. data/CHANGELOG.md +146 -1
  7. data/Gemfile +10 -5
  8. data/Gemfile.lock +60 -39
  9. data/LICENSE +8 -3
  10. data/bin/integrations +13 -1
  11. data/certs/cert.pem +26 -0
  12. data/config/locales/errors.yml +18 -2
  13. data/config/locales/pro_errors.yml +44 -0
  14. data/docker-compose.yml +1 -3
  15. data/karafka.gemspec +6 -4
  16. data/lib/active_job/queue_adapters/karafka_adapter.rb +18 -7
  17. data/lib/karafka/active_job/dispatcher.rb +13 -0
  18. data/lib/karafka/active_job/job_extensions.rb +3 -0
  19. data/lib/karafka/admin.rb +86 -0
  20. data/lib/karafka/app.rb +17 -0
  21. data/lib/karafka/base_consumer.rb +130 -19
  22. data/lib/karafka/cli/base.rb +24 -8
  23. data/lib/karafka/cli/install.rb +2 -1
  24. data/lib/karafka/cli/server.rb +1 -0
  25. data/lib/karafka/cli/swarm.rb +1 -0
  26. data/lib/karafka/cli/topics/align.rb +12 -2
  27. data/lib/karafka/cli/topics/plan.rb +54 -6
  28. data/lib/karafka/cli/topics.rb +45 -18
  29. data/lib/karafka/connection/client.rb +102 -35
  30. data/lib/karafka/connection/listener.rb +48 -11
  31. data/lib/karafka/connection/messages_buffer.rb +19 -6
  32. data/lib/karafka/connection/proxy.rb +3 -0
  33. data/lib/karafka/connection/raw_messages_buffer.rb +43 -9
  34. data/lib/karafka/connection/rebalance_manager.rb +24 -13
  35. data/lib/karafka/contracts/config.rb +4 -0
  36. data/lib/karafka/contracts/consumer_group.rb +17 -0
  37. data/lib/karafka/contracts/routing.rb +59 -0
  38. data/lib/karafka/contracts/topic.rb +14 -0
  39. data/lib/karafka/embedded.rb +46 -3
  40. data/lib/karafka/errors.rb +3 -2
  41. data/lib/karafka/helpers/async.rb +11 -2
  42. data/lib/karafka/helpers/config_importer.rb +13 -0
  43. data/lib/karafka/instrumentation/assignments_tracker.rb +7 -2
  44. data/lib/karafka/instrumentation/logger_listener.rb +45 -4
  45. data/lib/karafka/instrumentation/notifications.rb +12 -0
  46. data/lib/karafka/instrumentation/vendors/appsignal/client.rb +32 -11
  47. data/lib/karafka/instrumentation/vendors/appsignal/errors_listener.rb +1 -1
  48. data/lib/karafka/instrumentation/vendors/appsignal/metrics_listener.rb +3 -1
  49. data/lib/karafka/instrumentation/vendors/datadog/logger_listener.rb +17 -19
  50. data/lib/karafka/instrumentation/vendors/datadog/metrics_listener.rb +27 -18
  51. data/lib/karafka/instrumentation/vendors/kubernetes/base_listener.rb +2 -2
  52. data/lib/karafka/instrumentation/vendors/kubernetes/liveness_listener.rb +41 -13
  53. data/lib/karafka/messages/message.rb +9 -9
  54. data/lib/karafka/pro/active_job/consumer.rb +2 -10
  55. data/lib/karafka/pro/active_job/dispatcher.rb +67 -19
  56. data/lib/karafka/pro/active_job/job_options_contract.rb +12 -10
  57. data/lib/karafka/pro/base_consumer.rb +2 -10
  58. data/lib/karafka/pro/cleaner/errors.rb +2 -10
  59. data/lib/karafka/pro/cleaner/messages/message.rb +14 -12
  60. data/lib/karafka/pro/cleaner/messages/messages.rb +2 -10
  61. data/lib/karafka/pro/cleaner/messages/metadata.rb +41 -0
  62. data/lib/karafka/pro/cleaner.rb +3 -10
  63. data/lib/karafka/pro/connection/manager.rb +6 -10
  64. data/lib/karafka/pro/connection/multiplexing/listener.rb +2 -10
  65. data/lib/karafka/pro/contracts/base.rb +2 -10
  66. data/lib/karafka/pro/contracts/server_cli_options.rb +2 -10
  67. data/lib/karafka/pro/encryption/cipher.rb +2 -10
  68. data/lib/karafka/pro/encryption/contracts/config.rb +2 -10
  69. data/lib/karafka/pro/encryption/errors.rb +2 -10
  70. data/lib/karafka/pro/encryption/messages/middleware.rb +2 -10
  71. data/lib/karafka/pro/encryption/messages/parser.rb +2 -10
  72. data/lib/karafka/pro/encryption/setup/config.rb +2 -10
  73. data/lib/karafka/pro/encryption.rb +2 -10
  74. data/lib/karafka/pro/instrumentation/performance_tracker.rb +2 -10
  75. data/lib/karafka/pro/iterator/expander.rb +2 -10
  76. data/lib/karafka/pro/iterator/tpl_builder.rb +2 -10
  77. data/lib/karafka/pro/iterator.rb +2 -10
  78. data/lib/karafka/pro/loader.rb +5 -11
  79. data/lib/karafka/pro/processing/adaptive_iterator/consumer.rb +54 -0
  80. data/lib/karafka/pro/processing/adaptive_iterator/tracker.rb +67 -0
  81. data/lib/karafka/pro/processing/collapser.rb +2 -10
  82. data/lib/karafka/pro/processing/coordinator.rb +2 -10
  83. data/lib/karafka/pro/processing/coordinators/errors_tracker.rb +2 -10
  84. data/lib/karafka/pro/processing/coordinators/filters_applier.rb +19 -10
  85. data/lib/karafka/pro/processing/coordinators/virtual_offset_manager.rb +2 -10
  86. data/lib/karafka/pro/processing/executor.rb +2 -10
  87. data/lib/karafka/pro/processing/expansions_selector.rb +3 -10
  88. data/lib/karafka/pro/processing/filters/base.rb +14 -10
  89. data/lib/karafka/pro/processing/filters/delayer.rb +4 -12
  90. data/lib/karafka/pro/processing/filters/expirer.rb +2 -10
  91. data/lib/karafka/pro/processing/filters/inline_insights_delayer.rb +2 -10
  92. data/lib/karafka/pro/processing/filters/throttler.rb +2 -10
  93. data/lib/karafka/pro/processing/filters/virtual_limiter.rb +2 -10
  94. data/lib/karafka/pro/processing/jobs/consume_non_blocking.rb +4 -10
  95. data/lib/karafka/pro/processing/jobs/eofed_non_blocking.rb +26 -0
  96. data/lib/karafka/pro/processing/jobs/periodic.rb +4 -10
  97. data/lib/karafka/pro/processing/jobs/periodic_non_blocking.rb +4 -10
  98. data/lib/karafka/pro/processing/jobs/revoked_non_blocking.rb +4 -10
  99. data/lib/karafka/pro/processing/jobs_builder.rb +14 -10
  100. data/lib/karafka/pro/processing/jobs_queue.rb +2 -10
  101. data/lib/karafka/pro/processing/offset_metadata/consumer.rb +2 -10
  102. data/lib/karafka/pro/processing/offset_metadata/fetcher.rb +2 -10
  103. data/lib/karafka/pro/processing/offset_metadata/listener.rb +2 -10
  104. data/lib/karafka/pro/processing/partitioner.rb +35 -24
  105. data/lib/karafka/pro/processing/periodic_job/consumer.rb +2 -10
  106. data/lib/karafka/pro/processing/piping/consumer.rb +2 -10
  107. data/lib/karafka/pro/processing/schedulers/base.rb +2 -10
  108. data/lib/karafka/pro/processing/schedulers/default.rb +3 -10
  109. data/lib/karafka/pro/processing/strategies/aj/dlq_ftr_lrj_mom.rb +3 -11
  110. data/lib/karafka/pro/processing/strategies/aj/dlq_ftr_lrj_mom_vp.rb +3 -11
  111. data/lib/karafka/pro/processing/strategies/aj/dlq_ftr_mom.rb +2 -10
  112. data/lib/karafka/pro/processing/strategies/aj/dlq_ftr_mom_vp.rb +2 -10
  113. data/lib/karafka/pro/processing/strategies/aj/dlq_lrj_mom.rb +3 -11
  114. data/lib/karafka/pro/processing/strategies/aj/dlq_lrj_mom_vp.rb +3 -11
  115. data/lib/karafka/pro/processing/strategies/aj/dlq_mom.rb +2 -10
  116. data/lib/karafka/pro/processing/strategies/aj/dlq_mom_vp.rb +2 -10
  117. data/lib/karafka/pro/processing/strategies/aj/ftr_lrj_mom.rb +2 -10
  118. data/lib/karafka/pro/processing/strategies/aj/ftr_lrj_mom_vp.rb +3 -11
  119. data/lib/karafka/pro/processing/strategies/aj/ftr_mom.rb +2 -10
  120. data/lib/karafka/pro/processing/strategies/aj/ftr_mom_vp.rb +2 -10
  121. data/lib/karafka/pro/processing/strategies/aj/lrj_mom.rb +2 -10
  122. data/lib/karafka/pro/processing/strategies/aj/lrj_mom_vp.rb +5 -13
  123. data/lib/karafka/pro/processing/strategies/aj/mom.rb +2 -10
  124. data/lib/karafka/pro/processing/strategies/aj/mom_vp.rb +2 -10
  125. data/lib/karafka/pro/processing/strategies/base.rb +2 -10
  126. data/lib/karafka/pro/processing/strategies/default.rb +140 -58
  127. data/lib/karafka/pro/processing/strategies/dlq/default.rb +23 -15
  128. data/lib/karafka/pro/processing/strategies/dlq/ftr.rb +2 -10
  129. data/lib/karafka/pro/processing/strategies/dlq/ftr_lrj.rb +3 -11
  130. data/lib/karafka/pro/processing/strategies/dlq/ftr_lrj_mom.rb +7 -11
  131. data/lib/karafka/pro/processing/strategies/dlq/ftr_lrj_mom_vp.rb +2 -10
  132. data/lib/karafka/pro/processing/strategies/dlq/ftr_lrj_vp.rb +2 -10
  133. data/lib/karafka/pro/processing/strategies/dlq/ftr_mom.rb +19 -11
  134. data/lib/karafka/pro/processing/strategies/dlq/ftr_mom_vp.rb +2 -10
  135. data/lib/karafka/pro/processing/strategies/dlq/ftr_vp.rb +2 -10
  136. data/lib/karafka/pro/processing/strategies/dlq/lrj.rb +3 -11
  137. data/lib/karafka/pro/processing/strategies/dlq/lrj_mom.rb +19 -11
  138. data/lib/karafka/pro/processing/strategies/dlq/lrj_mom_vp.rb +2 -10
  139. data/lib/karafka/pro/processing/strategies/dlq/lrj_vp.rb +2 -10
  140. data/lib/karafka/pro/processing/strategies/dlq/mom.rb +24 -16
  141. data/lib/karafka/pro/processing/strategies/dlq/mom_vp.rb +2 -10
  142. data/lib/karafka/pro/processing/strategies/dlq/vp.rb +2 -10
  143. data/lib/karafka/pro/processing/strategies/ftr/default.rb +17 -12
  144. data/lib/karafka/pro/processing/strategies/ftr/vp.rb +2 -10
  145. data/lib/karafka/pro/processing/strategies/lrj/default.rb +5 -13
  146. data/lib/karafka/pro/processing/strategies/lrj/ftr.rb +3 -11
  147. data/lib/karafka/pro/processing/strategies/lrj/ftr_mom.rb +2 -10
  148. data/lib/karafka/pro/processing/strategies/lrj/ftr_mom_vp.rb +2 -10
  149. data/lib/karafka/pro/processing/strategies/lrj/ftr_vp.rb +2 -10
  150. data/lib/karafka/pro/processing/strategies/lrj/mom.rb +4 -12
  151. data/lib/karafka/pro/processing/strategies/lrj/mom_vp.rb +2 -10
  152. data/lib/karafka/pro/processing/strategies/lrj/vp.rb +2 -10
  153. data/lib/karafka/pro/processing/strategies/mom/default.rb +2 -10
  154. data/lib/karafka/pro/processing/strategies/mom/ftr.rb +2 -10
  155. data/lib/karafka/pro/processing/strategies/mom/ftr_vp.rb +2 -10
  156. data/lib/karafka/pro/processing/strategies/mom/vp.rb +2 -10
  157. data/lib/karafka/pro/processing/strategies/vp/default.rb +5 -10
  158. data/lib/karafka/pro/processing/strategies.rb +2 -10
  159. data/lib/karafka/pro/processing/strategy_selector.rb +2 -10
  160. data/lib/karafka/pro/processing/subscription_groups_coordinator.rb +2 -10
  161. data/lib/karafka/pro/recurring_tasks/consumer.rb +97 -0
  162. data/lib/karafka/pro/recurring_tasks/contracts/config.rb +45 -0
  163. data/lib/karafka/pro/recurring_tasks/contracts/task.rb +33 -0
  164. data/lib/karafka/pro/recurring_tasks/deserializer.rb +27 -0
  165. data/lib/karafka/pro/recurring_tasks/dispatcher.rb +79 -0
  166. data/lib/karafka/pro/recurring_tasks/errors.rb +26 -0
  167. data/lib/karafka/pro/recurring_tasks/executor.rb +144 -0
  168. data/lib/karafka/pro/recurring_tasks/listener.rb +30 -0
  169. data/lib/karafka/pro/recurring_tasks/matcher.rb +30 -0
  170. data/lib/karafka/pro/recurring_tasks/schedule.rb +55 -0
  171. data/lib/karafka/pro/recurring_tasks/serializer.rb +105 -0
  172. data/lib/karafka/pro/recurring_tasks/setup/config.rb +44 -0
  173. data/lib/karafka/pro/recurring_tasks/task.rb +143 -0
  174. data/lib/karafka/pro/recurring_tasks.rb +79 -0
  175. data/lib/karafka/pro/routing/features/active_job/builder.rb +2 -10
  176. data/lib/karafka/pro/routing/features/active_job.rb +2 -10
  177. data/lib/karafka/pro/routing/features/adaptive_iterator/config.rb +26 -0
  178. data/lib/karafka/pro/routing/features/adaptive_iterator/contracts/topic.rb +66 -0
  179. data/lib/karafka/pro/routing/features/adaptive_iterator/topic.rb +54 -0
  180. data/lib/karafka/pro/routing/features/adaptive_iterator.rb +23 -0
  181. data/lib/karafka/pro/routing/features/base.rb +2 -10
  182. data/lib/karafka/pro/routing/features/dead_letter_queue/contracts/topic.rb +2 -10
  183. data/lib/karafka/pro/routing/features/dead_letter_queue/topic.rb +2 -10
  184. data/lib/karafka/pro/routing/features/dead_letter_queue.rb +2 -10
  185. data/lib/karafka/pro/routing/features/delaying/config.rb +2 -10
  186. data/lib/karafka/pro/routing/features/delaying/contracts/topic.rb +2 -10
  187. data/lib/karafka/pro/routing/features/delaying/topic.rb +2 -10
  188. data/lib/karafka/pro/routing/features/delaying.rb +2 -10
  189. data/lib/karafka/pro/routing/features/direct_assignments/config.rb +2 -10
  190. data/lib/karafka/pro/routing/features/direct_assignments/contracts/consumer_group.rb +2 -10
  191. data/lib/karafka/pro/routing/features/direct_assignments/contracts/topic.rb +2 -10
  192. data/lib/karafka/pro/routing/features/direct_assignments/subscription_group.rb +2 -10
  193. data/lib/karafka/pro/routing/features/direct_assignments/topic.rb +2 -10
  194. data/lib/karafka/pro/routing/features/direct_assignments.rb +2 -10
  195. data/lib/karafka/pro/routing/features/expiring/config.rb +2 -10
  196. data/lib/karafka/pro/routing/features/expiring/contracts/topic.rb +2 -10
  197. data/lib/karafka/pro/routing/features/expiring/topic.rb +2 -10
  198. data/lib/karafka/pro/routing/features/expiring.rb +2 -10
  199. data/lib/karafka/pro/routing/features/filtering/config.rb +2 -10
  200. data/lib/karafka/pro/routing/features/filtering/contracts/topic.rb +2 -10
  201. data/lib/karafka/pro/routing/features/filtering/topic.rb +2 -10
  202. data/lib/karafka/pro/routing/features/filtering.rb +2 -10
  203. data/lib/karafka/pro/routing/features/inline_insights/config.rb +2 -10
  204. data/lib/karafka/pro/routing/features/inline_insights/contracts/topic.rb +2 -10
  205. data/lib/karafka/pro/routing/features/inline_insights/topic.rb +2 -10
  206. data/lib/karafka/pro/routing/features/inline_insights.rb +2 -10
  207. data/lib/karafka/pro/routing/features/long_running_job/config.rb +2 -10
  208. data/lib/karafka/pro/routing/features/long_running_job/contracts/topic.rb +2 -10
  209. data/lib/karafka/pro/routing/features/long_running_job/topic.rb +2 -10
  210. data/lib/karafka/pro/routing/features/long_running_job.rb +2 -10
  211. data/lib/karafka/pro/routing/features/multiplexing/config.rb +2 -10
  212. data/lib/karafka/pro/routing/features/multiplexing/contracts/topic.rb +2 -10
  213. data/lib/karafka/pro/routing/features/multiplexing/patches/contracts/consumer_group.rb +2 -10
  214. data/lib/karafka/pro/routing/features/multiplexing/proxy.rb +2 -10
  215. data/lib/karafka/pro/routing/features/multiplexing/subscription_group.rb +2 -10
  216. data/lib/karafka/pro/routing/features/multiplexing/subscription_groups_builder.rb +2 -10
  217. data/lib/karafka/pro/routing/features/multiplexing.rb +2 -10
  218. data/lib/karafka/pro/routing/features/non_blocking_job/topic.rb +2 -10
  219. data/lib/karafka/pro/routing/features/non_blocking_job.rb +2 -10
  220. data/lib/karafka/pro/routing/features/offset_metadata/config.rb +2 -10
  221. data/lib/karafka/pro/routing/features/offset_metadata/contracts/topic.rb +2 -10
  222. data/lib/karafka/pro/routing/features/offset_metadata/topic.rb +2 -10
  223. data/lib/karafka/pro/routing/features/offset_metadata.rb +2 -10
  224. data/lib/karafka/pro/routing/features/patterns/builder.rb +2 -10
  225. data/lib/karafka/pro/routing/features/patterns/config.rb +2 -10
  226. data/lib/karafka/pro/routing/features/patterns/consumer_group.rb +2 -10
  227. data/lib/karafka/pro/routing/features/patterns/contracts/consumer_group.rb +2 -10
  228. data/lib/karafka/pro/routing/features/patterns/contracts/pattern.rb +2 -10
  229. data/lib/karafka/pro/routing/features/patterns/contracts/topic.rb +2 -10
  230. data/lib/karafka/pro/routing/features/patterns/detector.rb +2 -10
  231. data/lib/karafka/pro/routing/features/patterns/pattern.rb +2 -10
  232. data/lib/karafka/pro/routing/features/patterns/patterns.rb +2 -10
  233. data/lib/karafka/pro/routing/features/patterns/topic.rb +2 -10
  234. data/lib/karafka/pro/routing/features/patterns/topics.rb +2 -10
  235. data/lib/karafka/pro/routing/features/patterns.rb +2 -10
  236. data/lib/karafka/pro/routing/features/pausing/contracts/topic.rb +2 -10
  237. data/lib/karafka/pro/routing/features/pausing/topic.rb +2 -10
  238. data/lib/karafka/pro/routing/features/pausing.rb +2 -10
  239. data/lib/karafka/pro/routing/features/periodic_job/config.rb +2 -10
  240. data/lib/karafka/pro/routing/features/periodic_job/contracts/topic.rb +2 -10
  241. data/lib/karafka/pro/routing/features/periodic_job/topic.rb +2 -10
  242. data/lib/karafka/pro/routing/features/periodic_job.rb +2 -10
  243. data/lib/karafka/pro/routing/features/recurring_tasks/builder.rb +123 -0
  244. data/lib/karafka/pro/routing/features/recurring_tasks/config.rb +20 -0
  245. data/lib/karafka/pro/routing/features/recurring_tasks/contracts/topic.rb +32 -0
  246. data/lib/karafka/pro/routing/features/recurring_tasks/proxy.rb +19 -0
  247. data/lib/karafka/pro/routing/features/recurring_tasks/topic.rb +36 -0
  248. data/lib/karafka/pro/routing/features/recurring_tasks.rb +17 -0
  249. data/lib/karafka/pro/routing/features/scheduled_messages/builder.rb +123 -0
  250. data/lib/karafka/pro/routing/features/scheduled_messages/config.rb +20 -0
  251. data/lib/karafka/pro/routing/features/scheduled_messages/contracts/topic.rb +32 -0
  252. data/lib/karafka/pro/routing/features/scheduled_messages/proxy.rb +19 -0
  253. data/lib/karafka/pro/routing/features/scheduled_messages/topic.rb +36 -0
  254. data/lib/karafka/pro/routing/features/scheduled_messages.rb +16 -0
  255. data/lib/karafka/pro/routing/features/swarm/config.rb +2 -10
  256. data/lib/karafka/pro/routing/features/swarm/contracts/routing.rb +2 -10
  257. data/lib/karafka/pro/routing/features/swarm/contracts/topic.rb +2 -10
  258. data/lib/karafka/pro/routing/features/swarm/topic.rb +2 -10
  259. data/lib/karafka/pro/routing/features/swarm.rb +2 -10
  260. data/lib/karafka/pro/routing/features/throttling/config.rb +2 -10
  261. data/lib/karafka/pro/routing/features/throttling/contracts/topic.rb +2 -10
  262. data/lib/karafka/pro/routing/features/throttling/topic.rb +2 -10
  263. data/lib/karafka/pro/routing/features/throttling.rb +2 -10
  264. data/lib/karafka/pro/routing/features/virtual_partitions/config.rb +3 -10
  265. data/lib/karafka/pro/routing/features/virtual_partitions/contracts/topic.rb +3 -10
  266. data/lib/karafka/pro/routing/features/virtual_partitions/topic.rb +10 -12
  267. data/lib/karafka/pro/routing/features/virtual_partitions.rb +2 -10
  268. data/lib/karafka/pro/scheduled_messages/consumer.rb +177 -0
  269. data/lib/karafka/pro/scheduled_messages/contracts/config.rb +48 -0
  270. data/lib/karafka/pro/scheduled_messages/contracts/message.rb +88 -0
  271. data/lib/karafka/pro/scheduled_messages/daily_buffer.rb +71 -0
  272. data/lib/karafka/pro/scheduled_messages/day.rb +37 -0
  273. data/lib/karafka/pro/scheduled_messages/deserializers/headers.rb +38 -0
  274. data/lib/karafka/pro/scheduled_messages/deserializers/payload.rb +27 -0
  275. data/lib/karafka/pro/scheduled_messages/dispatcher.rb +114 -0
  276. data/lib/karafka/pro/scheduled_messages/errors.rb +20 -0
  277. data/lib/karafka/pro/scheduled_messages/max_epoch.rb +33 -0
  278. data/lib/karafka/pro/scheduled_messages/proxy.rb +177 -0
  279. data/lib/karafka/pro/scheduled_messages/schema_validator.rb +29 -0
  280. data/lib/karafka/pro/scheduled_messages/serializer.rb +47 -0
  281. data/lib/karafka/pro/scheduled_messages/setup/config.rb +52 -0
  282. data/lib/karafka/pro/scheduled_messages/state.rb +54 -0
  283. data/lib/karafka/pro/scheduled_messages/tracker.rb +56 -0
  284. data/lib/karafka/pro/scheduled_messages.rb +59 -0
  285. data/lib/karafka/pro/swarm/liveness_listener.rb +2 -10
  286. data/lib/karafka/processing/coordinator.rb +14 -0
  287. data/lib/karafka/processing/executor.rb +29 -1
  288. data/lib/karafka/processing/jobs/base.rb +13 -0
  289. data/lib/karafka/processing/jobs/consume.rb +2 -0
  290. data/lib/karafka/processing/jobs/eofed.rb +29 -0
  291. data/lib/karafka/processing/jobs/idle.rb +2 -0
  292. data/lib/karafka/processing/jobs/revoked.rb +2 -0
  293. data/lib/karafka/processing/jobs/shutdown.rb +2 -0
  294. data/lib/karafka/processing/jobs_builder.rb +6 -0
  295. data/lib/karafka/processing/schedulers/default.rb +1 -0
  296. data/lib/karafka/processing/strategies/aj_dlq_mom.rb +1 -1
  297. data/lib/karafka/processing/strategies/default.rb +45 -13
  298. data/lib/karafka/processing/strategies/dlq.rb +19 -5
  299. data/lib/karafka/processing/strategies/dlq_mom.rb +27 -8
  300. data/lib/karafka/processing/worker.rb +26 -13
  301. data/lib/karafka/railtie.rb +11 -42
  302. data/lib/karafka/routing/builder.rb +19 -1
  303. data/lib/karafka/routing/consumer_group.rb +9 -14
  304. data/lib/karafka/routing/features/dead_letter_queue/config.rb +3 -0
  305. data/lib/karafka/routing/features/dead_letter_queue/contracts/topic.rb +1 -0
  306. data/lib/karafka/routing/features/dead_letter_queue/topic.rb +7 -2
  307. data/lib/karafka/routing/features/eofed/config.rb +15 -0
  308. data/lib/karafka/routing/features/eofed/contracts/topic.rb +39 -0
  309. data/lib/karafka/routing/features/eofed/topic.rb +31 -0
  310. data/lib/karafka/routing/features/eofed.rb +14 -0
  311. data/lib/karafka/routing/subscription_group.rb +29 -1
  312. data/lib/karafka/routing/topic.rb +24 -1
  313. data/lib/karafka/runner.rb +10 -9
  314. data/lib/karafka/server.rb +37 -1
  315. data/lib/karafka/setup/attributes_map.rb +11 -4
  316. data/lib/karafka/setup/config.rb +11 -52
  317. data/lib/karafka/setup/defaults_injector.rb +64 -0
  318. data/lib/karafka/swarm/node.rb +2 -0
  319. data/lib/karafka/swarm/supervisor.rb +11 -2
  320. data/lib/karafka/templates/karafka.rb.erb +2 -2
  321. data/lib/karafka/version.rb +1 -1
  322. data/lib/karafka.rb +47 -7
  323. data.tar.gz.sig +0 -0
  324. metadata +116 -33
  325. metadata.gz.sig +0 -0
  326. data/certs/cert_chain.pem +0 -26
@@ -5,6 +5,8 @@ module Karafka
5
5
  module Jobs
6
6
  # Job that runs on each active consumer upon process shutdown (one job per consumer).
7
7
  class Shutdown < Base
8
+ self.action = :shutdown
9
+
8
10
  # @param executor [Karafka::Processing::Executor] executor that is suppose to run a given
9
11
  # job on an active consumer
10
12
  # @return [Shutdown]
@@ -12,6 +12,12 @@ module Karafka
12
12
  Jobs::Consume.new(executor, messages)
13
13
  end
14
14
 
15
+ # @param executor [Karafka::Processing::Executor]
16
+ # @return [Karafka::Processing::Jobs::Eofed] eofed job
17
+ def eofed(executor)
18
+ Jobs::Eofed.new(executor)
19
+ end
20
+
15
21
  # @param executor [Karafka::Processing::Executor]
16
22
  # @return [Karafka::Processing::Jobs::Revoked] revocation job
17
23
  def revoked(executor)
@@ -24,6 +24,7 @@ module Karafka
24
24
  alias on_schedule_revocation on_schedule_consumption
25
25
  alias on_schedule_shutdown on_schedule_consumption
26
26
  alias on_schedule_idle on_schedule_consumption
27
+ alias on_schedule_eofed on_schedule_consumption
27
28
 
28
29
  # This scheduler does not have anything to manage as it is a pass through and has no state
29
30
  def on_manage
@@ -35,7 +35,7 @@ module Karafka
35
35
  # since AJ consumer commits the offset after each job, we also know that the
36
36
  # previous job was successful
37
37
  mark_dispatched_to_dlq(skippable_message)
38
- pause(coordinator.seek_offset, nil, false)
38
+ pause(seek_offset, nil, false)
39
39
  end
40
40
  end
41
41
  end
@@ -17,19 +17,30 @@ module Karafka
17
17
  %i[
18
18
  consume
19
19
  idle
20
+ eofed
20
21
  revoked
21
22
  shutdown
22
23
  ].each do |action|
23
24
  class_eval <<~RUBY, __FILE__, __LINE__ + 1
24
25
  # No actions needed for the standard flow here
25
26
  def handle_before_schedule_#{action}
26
- Karafka.monitor.instrument('consumer.before_schedule_#{action}', caller: self)
27
+ monitor.instrument('consumer.before_schedule_#{action}', caller: self)
27
28
 
28
29
  nil
29
30
  end
30
31
  RUBY
31
32
  end
32
33
 
34
+ # Runs the post-creation, post-assignment code
35
+ # @note It runs in the listener loop. Should **not** be used for anything heavy or
36
+ # with any potential errors. Mostly for initialization of states, etc.
37
+ def handle_initialized
38
+ monitor.instrument('consumer.initialize', caller: self)
39
+ monitor.instrument('consumer.initialized', caller: self) do
40
+ initialized
41
+ end
42
+ end
43
+
33
44
  # Marks message as consumed in an async way.
34
45
  #
35
46
  # @param message [Messages::Message] last successfully processed message.
@@ -43,13 +54,13 @@ module Karafka
43
54
  def mark_as_consumed(message)
44
55
  # seek offset can be nil only in case `#seek` was invoked with offset reset request
45
56
  # In case like this we ignore marking
46
- return true if coordinator.seek_offset.nil?
57
+ return true if seek_offset.nil?
47
58
  # Ignore earlier offsets than the one we already committed
48
- return true if coordinator.seek_offset > message.offset
59
+ return true if seek_offset > message.offset
49
60
  return false if revoked?
50
61
  return revoked? unless client.mark_as_consumed(message)
51
62
 
52
- coordinator.seek_offset = message.offset + 1
63
+ self.seek_offset = message.offset + 1
53
64
 
54
65
  true
55
66
  end
@@ -62,14 +73,14 @@ module Karafka
62
73
  def mark_as_consumed!(message)
63
74
  # seek offset can be nil only in case `#seek` was invoked with offset reset request
64
75
  # In case like this we ignore marking
65
- return true if coordinator.seek_offset.nil?
76
+ return true if seek_offset.nil?
66
77
  # Ignore earlier offsets than the one we already committed
67
- return true if coordinator.seek_offset > message.offset
78
+ return true if seek_offset > message.offset
68
79
  return false if revoked?
69
80
 
70
81
  return revoked? unless client.mark_as_consumed!(message)
71
82
 
72
- coordinator.seek_offset = message.offset + 1
83
+ self.seek_offset = message.offset + 1
73
84
 
74
85
  true
75
86
  end
@@ -104,10 +115,21 @@ module Karafka
104
115
  coordinator.pause_tracker.increment
105
116
  end
106
117
 
118
+ # Runs the wrapping to execute appropriate action wrapped with the wrapper method code
119
+ #
120
+ # @param action [Symbol]
121
+ # @param block [Proc]
122
+ def handle_wrap(action, &block)
123
+ monitor.instrument('consumer.wrap', caller: self)
124
+ monitor.instrument('consumer.wrapped', caller: self) do
125
+ wrap(action, &block)
126
+ end
127
+ end
128
+
107
129
  # Run the user consumption code
108
130
  def handle_consume
109
- Karafka.monitor.instrument('consumer.consume', caller: self)
110
- Karafka.monitor.instrument('consumer.consumed', caller: self) do
131
+ monitor.instrument('consumer.consume', caller: self)
132
+ monitor.instrument('consumer.consumed', caller: self) do
111
133
  consume
112
134
  end
113
135
 
@@ -151,6 +173,16 @@ module Karafka
151
173
  coordinator.decrement(:idle)
152
174
  end
153
175
 
176
+ # Runs the consumer `#eofed` method with reporting
177
+ def handle_eofed
178
+ monitor.instrument('consumer.eof', caller: self)
179
+ monitor.instrument('consumer.eofed', caller: self) do
180
+ eofed
181
+ end
182
+ ensure
183
+ coordinator.decrement(:eofed)
184
+ end
185
+
154
186
  # We need to always un-pause the processing in case we have lost a given partition.
155
187
  # Otherwise the underlying librdkafka would not know we may want to continue processing and
156
188
  # the pause could in theory last forever
@@ -159,8 +191,8 @@ module Karafka
159
191
 
160
192
  coordinator.revoke
161
193
 
162
- Karafka.monitor.instrument('consumer.revoke', caller: self)
163
- Karafka.monitor.instrument('consumer.revoked', caller: self) do
194
+ monitor.instrument('consumer.revoke', caller: self)
195
+ monitor.instrument('consumer.revoked', caller: self) do
164
196
  revoked
165
197
  end
166
198
  ensure
@@ -169,8 +201,8 @@ module Karafka
169
201
 
170
202
  # Runs the shutdown code
171
203
  def handle_shutdown
172
- Karafka.monitor.instrument('consumer.shutting_down', caller: self)
173
- Karafka.monitor.instrument('consumer.shutdown', caller: self) do
204
+ monitor.instrument('consumer.shutting_down', caller: self)
205
+ monitor.instrument('consumer.shutdown', caller: self) do
174
206
  shutdown
175
207
  end
176
208
  ensure
@@ -76,12 +76,16 @@ module Karafka
76
76
  dispatch_to_dlq(skippable_message)
77
77
 
78
78
  # We mark the broken message as consumed and move on
79
- mark_dispatched_to_dlq(skippable_message)
79
+ if mark_after_dispatch?
80
+ mark_dispatched_to_dlq(skippable_message)
80
81
 
81
- return if revoked?
82
+ return if revoked?
83
+ else
84
+ self.seek_offset = skippable_message.offset + 1
85
+ end
82
86
 
83
87
  # We pause to backoff once just in case.
84
- pause(coordinator.seek_offset, nil, false)
88
+ pause(seek_offset, nil, false)
85
89
  end
86
90
  end
87
91
 
@@ -91,7 +95,7 @@ module Karafka
91
95
  # information if this message was from marked offset or figured out via mom flow
92
96
  def find_skippable_message
93
97
  skippable_message = messages.find do |msg|
94
- coordinator.marked? && msg.offset == coordinator.seek_offset
98
+ coordinator.marked? && msg.offset == seek_offset
95
99
  end
96
100
 
97
101
  # If we don't have the message matching the last comitted offset, it means that
@@ -113,13 +117,23 @@ module Karafka
113
117
  )
114
118
 
115
119
  # Notify about dispatch on the events bus
116
- Karafka.monitor.instrument(
120
+ monitor.instrument(
117
121
  'dead_letter_queue.dispatched',
118
122
  caller: self,
119
123
  message: skippable_message
120
124
  )
121
125
  end
122
126
 
127
+ # @return [Boolean] should we mark given message as consumed after dispatch. For default
128
+ # non MOM strategies if user did not explicitly tell us not to, we mark it. Default is
129
+ # `nil`, which means `true` in this case. If user provided alternative value, we go
130
+ # with it.
131
+ def mark_after_dispatch?
132
+ return true if topic.dead_letter_queue.mark_after_dispatch.nil?
133
+
134
+ topic.dead_letter_queue.mark_after_dispatch
135
+ end
136
+
123
137
  # Marks message that went to DLQ (if applicable) based on the requested method
124
138
  # @param skippable_message [Karafka::Messages::Message]
125
139
  def mark_dispatched_to_dlq(skippable_message)
@@ -33,16 +33,35 @@ module Karafka
33
33
 
34
34
  dispatch_to_dlq(skippable_message)
35
35
 
36
- # Save the next offset we want to go with after moving given message to DLQ
37
- # Without this, we would not be able to move forward and we would end up
38
- # in an infinite loop trying to un-pause from the message we've already processed
39
- # Of course, since it's a MoM a rebalance or kill, will move it back as no
40
- # offsets are being committed
41
- coordinator.seek_offset = skippable_message.offset + 1
42
-
43
- pause(coordinator.seek_offset, nil, false)
36
+ # We mark the broken message as consumed and move on
37
+ if mark_after_dispatch?
38
+ mark_dispatched_to_dlq(skippable_message)
39
+
40
+ return if revoked?
41
+ else
42
+ # Save the next offset we want to go with after moving given message to DLQ
43
+ # Without this, we would not be able to move forward and we would end up
44
+ # in an infinite loop trying to un-pause from the message we've already processed
45
+ # Of course, since it's a MoM a rebalance or kill, will move it back as no
46
+ # offsets are being committed
47
+ self.seek_offset = skippable_message.offset + 1
48
+ end
49
+
50
+ pause(seek_offset, nil, false)
44
51
  end
45
52
  end
53
+
54
+ # @return [Boolean] should we mark given message as consumed after dispatch. For
55
+ # MOM strategies if user did not explicitly tell us to mark, we do not mark. Default is
56
+ # `nil`, which means `false` in this case. If user provided alternative value, we go
57
+ # with it.
58
+ #
59
+ # @note Please note, this is the opposite behavior than in case of AOM strategies.
60
+ def mark_after_dispatch?
61
+ return false if topic.dead_letter_queue.mark_after_dispatch.nil?
62
+
63
+ topic.dead_letter_queue.mark_after_dispatch
64
+ end
46
65
  end
47
66
  end
48
67
  end
@@ -18,6 +18,10 @@ module Karafka
18
18
  # not user code but need to run after user code base is executed.
19
19
  class Worker
20
20
  include Helpers::Async
21
+ include Helpers::ConfigImporter.new(
22
+ worker_job_call_wrapper: %i[internal processing worker_job_call_wrapper],
23
+ monitor: %i[monitor]
24
+ )
21
25
 
22
26
  # @return [String] id of this worker
23
27
  attr_reader :id
@@ -27,6 +31,7 @@ module Karafka
27
31
  def initialize(jobs_queue)
28
32
  @id = SecureRandom.hex(6)
29
33
  @jobs_queue = jobs_queue
34
+ @non_wrapped_flow = worker_job_call_wrapper == false
30
35
  end
31
36
 
32
37
  private
@@ -49,22 +54,30 @@ module Karafka
49
54
  instrument_details = { caller: self, job: job, jobs_queue: @jobs_queue }
50
55
 
51
56
  if job
52
- Karafka.monitor.instrument('worker.process', instrument_details)
57
+ job.wrap do
58
+ monitor.instrument('worker.process', instrument_details)
53
59
 
54
- Karafka.monitor.instrument('worker.processed', instrument_details) do
55
- job.before_call
60
+ monitor.instrument('worker.processed', instrument_details) do
61
+ job.before_call
56
62
 
57
- # If a job is marked as non blocking, we can run a tick in the job queue and if there
58
- # are no other blocking factors, the job queue will be unlocked.
59
- # If this does not run, all the things will be blocking and job queue won't allow to
60
- # pass it until done.
61
- @jobs_queue.tick(job.group_id) if job.non_blocking?
63
+ # If a job is marked as non blocking, we can run a tick in the job queue and if there
64
+ # are no other blocking factors, the job queue will be unlocked.
65
+ # If this does not run, all the things will be blocking and job queue won't allow to
66
+ # pass it until done.
67
+ @jobs_queue.tick(job.group_id) if job.non_blocking?
62
68
 
63
- job.call
69
+ if @non_wrapped_flow
70
+ job.call
71
+ else
72
+ worker_job_call_wrapper.wrap do
73
+ job.call
74
+ end
75
+ end
64
76
 
65
- job.after_call
77
+ job.after_call
66
78
 
67
- true
79
+ true
80
+ end
68
81
  end
69
82
  else
70
83
  false
@@ -73,7 +86,7 @@ module Karafka
73
86
  # rubocop:disable Lint/RescueException
74
87
  rescue Exception => e
75
88
  # rubocop:enable Lint/RescueException
76
- Karafka.monitor.instrument(
89
+ monitor.instrument(
77
90
  'error.occurred',
78
91
  caller: self,
79
92
  job: job,
@@ -89,7 +102,7 @@ module Karafka
89
102
  end
90
103
 
91
104
  # Always publish info, that we completed all the work despite its result
92
- Karafka.monitor.instrument('worker.completed', instrument_details)
105
+ monitor.instrument('worker.completed', instrument_details)
93
106
  end
94
107
  end
95
108
  end
@@ -65,48 +65,6 @@ if Karafka.rails?
65
65
  app.config.autoload_paths += %w[app/consumers]
66
66
  end
67
67
 
68
- initializer 'karafka.configure_rails_code_reloader' do
69
- # There are components that won't work with older Rails version, so we check it and
70
- # provide a failover
71
- rails6plus = Rails.gem_version >= Gem::Version.new('6.0.0')
72
-
73
- next unless Rails.env.development?
74
- next unless ENV.key?('KARAFKA_CLI')
75
- next unless rails6plus
76
-
77
- # We can have many listeners, but it does not matter in which we will reload the code
78
- # as long as all the consumers will be re-created as Rails reload is thread-safe
79
- ::Karafka::App.monitor.subscribe('connection.listener.fetch_loop') do
80
- # If consumer persistence is enabled, no reason to reload because we will still keep
81
- # old consumer instances in memory.
82
- next if Karafka::App.config.consumer_persistence
83
- # Reload code each time there is a change in the code
84
- next unless Rails.application.reloaders.any?(&:updated?)
85
-
86
- Rails.application.reloader.reload!
87
- end
88
- end
89
-
90
- initializer 'karafka.release_active_record_connections' do
91
- rails7plus = Rails.gem_version >= Gem::Version.new('7.0.0')
92
-
93
- ActiveSupport.on_load(:active_record) do
94
- ::Karafka::App.monitor.subscribe('worker.completed') do
95
- # Always release the connection after processing is done. Otherwise thread may hang
96
- # blocking the reload and further processing
97
- # @see https://github.com/rails/rails/issues/44183
98
- #
99
- # The change technically happens in 7.1 but 7.0 already supports this so we can make
100
- # a proper change for 7.0+
101
- if rails7plus
102
- ActiveRecord::Base.connection_handler.clear_active_connections!
103
- else
104
- ActiveRecord::Base.clear_active_connections!
105
- end
106
- end
107
- end
108
- end
109
-
110
68
  initializer 'karafka.require_karafka_boot_file' do |app|
111
69
  rails6plus = Rails.gem_version >= Gem::Version.new('6.0.0')
112
70
 
@@ -136,6 +94,17 @@ if Karafka.rails?
136
94
  end
137
95
  end
138
96
  end
97
+
98
+ initializer 'karafka.configure_worker_external_executor' do |app|
99
+ app.config.after_initialize do
100
+ app_config = Karafka::App.config
101
+
102
+ # We need to wrap execution of the core user code with a wrapper in case of Rails, so
103
+ # the auto-reload works as expected
104
+ worker_job_call_wrapper = app_config.consumer_persistence ? app.executor : app.reloader
105
+ app_config.internal.processing.worker_job_call_wrapper = worker_job_call_wrapper
106
+ end
107
+ end
139
108
  end
140
109
  end
141
110
  end
@@ -48,6 +48,10 @@ module Karafka
48
48
 
49
49
  instance_eval(&block)
50
50
 
51
+ # Ensures high-level routing details consistency
52
+ # Contains checks that require knowledge about all the consumer groups to operate
53
+ Contracts::Routing.new.validate!(map(&:to_h))
54
+
51
55
  each do |consumer_group|
52
56
  # Validate consumer group settings
53
57
  Contracts::ConsumerGroup.new.validate!(consumer_group.to_h)
@@ -63,6 +67,20 @@ module Karafka
63
67
  end
64
68
  end
65
69
 
70
+ # Clear out the drawn routes.
71
+ alias array_clear clear
72
+ private :array_clear
73
+
74
+ # Clear routes and draw them again with the given block. Helpful for testing purposes.
75
+ # @param block [Proc] block we will evaluate within the builder context
76
+ def redraw(&block)
77
+ @mutex.synchronize do
78
+ @draws.clear
79
+ array_clear
80
+ end
81
+ draw(&block)
82
+ end
83
+
66
84
  # @return [Array<Karafka::Routing::ConsumerGroup>] only active consumer groups that
67
85
  # we want to use. Since Karafka supports multi-process setup, we need to be able
68
86
  # to pick only those consumer groups that should be active in our given process context
@@ -75,7 +93,7 @@ module Karafka
75
93
  @mutex.synchronize do
76
94
  @defaults = EMPTY_DEFAULTS
77
95
  @draws.clear
78
- super
96
+ array_clear
79
97
  end
80
98
  end
81
99
 
@@ -8,6 +8,12 @@ module Karafka
8
8
  # @note A single consumer group represents Kafka consumer group, but it may not match 1:1 with
9
9
  # subscription groups. There can be more subscription groups than consumer groups
10
10
  class ConsumerGroup
11
+ include Helpers::ConfigImporter.new(
12
+ activity_manager: %i[internal routing activity_manager],
13
+ builder: %i[internal routing builder],
14
+ subscription_groups_builder: %i[internal routing subscription_groups_builder]
15
+ )
16
+
11
17
  attr_reader :id, :topics, :name
12
18
 
13
19
  # This is a "virtual" attribute that is not building subscription groups.
@@ -32,7 +38,7 @@ module Karafka
32
38
 
33
39
  # @return [Boolean] true if this consumer group should be active in our current process
34
40
  def active?
35
- config.internal.routing.activity_manager.active?(:consumer_groups, name)
41
+ activity_manager.active?(:consumer_groups, name)
36
42
  end
37
43
 
38
44
  # Builds a topic representation inside of a current consumer group route
@@ -43,7 +49,7 @@ module Karafka
43
49
  topic = Topic.new(name, self)
44
50
  @topics << Proxy.new(
45
51
  topic,
46
- config.internal.routing.builder.defaults,
52
+ builder.defaults,
47
53
  &block
48
54
  ).target
49
55
  built_topic = @topics.last
@@ -72,11 +78,7 @@ module Karafka
72
78
  # @return [Array<Routing::SubscriptionGroup>] all the subscription groups build based on
73
79
  # the consumer group topics
74
80
  def subscription_groups
75
- @subscription_groups ||= config
76
- .internal
77
- .routing
78
- .subscription_groups_builder
79
- .call(topics)
81
+ @subscription_groups ||= subscription_groups_builder.call(topics)
80
82
  end
81
83
 
82
84
  # Hashed version of consumer group that can be used for validation purposes
@@ -88,13 +90,6 @@ module Karafka
88
90
  id: id
89
91
  }.freeze
90
92
  end
91
-
92
- private
93
-
94
- # @return [Karafka::Core::Configurable::Node] root node config
95
- def config
96
- ::Karafka::App.config
97
- end
98
93
  end
99
94
  end
100
95
  end
@@ -21,6 +21,9 @@ module Karafka
21
21
  :dispatch_method,
22
22
  # Should we use `#mark_as_consumed` or `#mark_as_consumed!` (in flows that mark)
23
23
  :marking_method,
24
+ # Should we mark as consumed after dispatch or not. True for most cases, except MOM where
25
+ # it is on user to decide (false by default)
26
+ :mark_after_dispatch,
24
27
  # Initialize with kwargs
25
28
  keyword_init: true
26
29
  ) do
@@ -21,6 +21,7 @@ module Karafka
21
21
  required(:independent) { |val| [true, false].include?(val) }
22
22
  required(:max_retries) { |val| val.is_a?(Integer) && val >= 0 }
23
23
  required(:transactional) { |val| [true, false].include?(val) }
24
+ required(:mark_after_dispatch) { |val| [true, false, nil].include?(val) }
24
25
 
25
26
  required(:dispatch_method) do |val|
26
27
  %i[produce_async produce_sync].include?(val)
@@ -22,6 +22,9 @@ module Karafka
22
22
  # whether dispatch on dlq should be sync or async (async by default)
23
23
  # @param marking_method [Symbol] `:mark_as_consumed` or `:mark_as_consumed!`. Describes
24
24
  # whether marking on DLQ should be async or sync (async by default)
25
+ # @param mark_after_dispatch [Boolean, nil] Should we mark after dispatch. `nil` means
26
+ # that the default strategy approach to marking will be used. `true` or `false`
27
+ # overwrites the default
25
28
  # @return [Config] defined config
26
29
  def dead_letter_queue(
27
30
  max_retries: DEFAULT_MAX_RETRIES,
@@ -29,7 +32,8 @@ module Karafka
29
32
  independent: false,
30
33
  transactional: true,
31
34
  dispatch_method: :produce_async,
32
- marking_method: :mark_as_consumed
35
+ marking_method: :mark_as_consumed,
36
+ mark_after_dispatch: nil
33
37
  )
34
38
  @dead_letter_queue ||= Config.new(
35
39
  active: !topic.nil?,
@@ -38,7 +42,8 @@ module Karafka
38
42
  independent: independent,
39
43
  transactional: transactional,
40
44
  dispatch_method: dispatch_method,
41
- marking_method: marking_method
45
+ marking_method: marking_method,
46
+ mark_after_dispatch: mark_after_dispatch
42
47
  )
43
48
  end
44
49
 
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Karafka
4
+ module Routing
5
+ module Features
6
+ class Eofed < Base
7
+ # Config of this feature
8
+ Config = Struct.new(
9
+ :active,
10
+ keyword_init: true
11
+ ) { alias_method :active?, :active }
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Karafka
4
+ module Routing
5
+ module Features
6
+ class Eofed < Base
7
+ # Eofed related contracts namespace
8
+ module Contracts
9
+ # Contract for eofed topic setup
10
+ class Topic < Karafka::Contracts::Base
11
+ configure do |config|
12
+ config.error_messages = YAML.safe_load(
13
+ File.read(
14
+ File.join(Karafka.gem_root, 'config', 'locales', 'errors.yml')
15
+ )
16
+ ).fetch('en').fetch('validations').fetch('topic')
17
+ end
18
+
19
+ nested :eofed do
20
+ required(:active) { |val| [true, false].include?(val) }
21
+ end
22
+
23
+ virtual do |data, errors|
24
+ next unless errors.empty?
25
+
26
+ eofed = data[:eofed]
27
+
28
+ next unless eofed[:active]
29
+
30
+ next if data[:kafka][:'enable.partition.eof']
31
+
32
+ [[%i[eofed kafka], :enable]]
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Karafka
4
+ module Routing
5
+ module Features
6
+ class Eofed < Base
7
+ # Routing topic eofed API
8
+ module Topic
9
+ # @param active [Boolean] should the `#eofed` job run on eof
10
+ def eofed(active = false)
11
+ @eofed ||= Config.new(
12
+ active: active
13
+ )
14
+ end
15
+
16
+ # @return [Boolean] Are `#eofed` jobs active
17
+ def eofed?
18
+ eofed.active?
19
+ end
20
+
21
+ # @return [Hash] topic setup hash
22
+ def to_h
23
+ super.merge(
24
+ eofed: eofed.to_h
25
+ ).freeze
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Karafka
4
+ module Routing
5
+ module Features
6
+ # Namespace for feature allowing to enable the `#eofed` jobs.
7
+ # We do not enable it always because users may only be interested in fast eofed yielding
8
+ # without running the `#eofed` operation at all. This safes on empty cycles of running
9
+ # pointless empty jobs.
10
+ class Eofed < Base
11
+ end
12
+ end
13
+ end
14
+ end