karafka 2.0.0.rc2 → 2.0.0.rc5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (43) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/CHANGELOG.md +29 -2
  4. data/CONTRIBUTING.md +4 -8
  5. data/Gemfile.lock +9 -21
  6. data/LICENSE-COMM +1 -1
  7. data/README.md +46 -8
  8. data/bin/integrations +1 -1
  9. data/config/errors.yml +4 -0
  10. data/docker-compose.yml +3 -0
  11. data/karafka.gemspec +5 -5
  12. data/lib/karafka/active_job/consumer.rb +2 -0
  13. data/lib/karafka/connection/client.rb +9 -4
  14. data/lib/karafka/connection/listener.rb +21 -7
  15. data/lib/karafka/contracts/base.rb +1 -1
  16. data/lib/karafka/errors.rb +0 -3
  17. data/lib/karafka/instrumentation/callbacks/statistics.rb +1 -2
  18. data/lib/karafka/instrumentation/logger_listener.rb +27 -3
  19. data/lib/karafka/instrumentation/monitor.rb +14 -59
  20. data/lib/karafka/instrumentation/notifications.rb +52 -0
  21. data/lib/karafka/instrumentation/vendors/datadog/dashboard.json +1 -0
  22. data/lib/karafka/instrumentation/vendors/datadog/listener.rb +232 -0
  23. data/lib/karafka/patches/rdkafka/consumer.rb +1 -1
  24. data/lib/karafka/pro/active_job/dispatcher.rb +5 -2
  25. data/lib/karafka/pro/active_job/job_options_contract.rb +1 -0
  26. data/lib/karafka/pro/base_consumer.rb +2 -2
  27. data/lib/karafka/pro/contracts/base.rb +21 -0
  28. data/lib/karafka/pro/contracts/consumer_group.rb +34 -0
  29. data/lib/karafka/pro/contracts/consumer_group_topic.rb +33 -0
  30. data/lib/karafka/pro/loader.rb +21 -3
  31. data/lib/karafka/pro/processing/partitioner.rb +22 -3
  32. data/lib/karafka/pro/routing/builder_extensions.rb +30 -0
  33. data/lib/karafka/pro/routing/{extensions.rb → topic_extensions.rb} +2 -2
  34. data/lib/karafka/process.rb +1 -0
  35. data/lib/karafka/processing/jobs_queue.rb +11 -0
  36. data/lib/karafka/processing/worker.rb +4 -2
  37. data/lib/karafka/server.rb +3 -0
  38. data/lib/karafka/setup/config.rb +3 -3
  39. data/lib/karafka/version.rb +1 -1
  40. data/lib/karafka.rb +3 -2
  41. data.tar.gz.sig +0 -0
  42. metadata +27 -14
  43. metadata.gz.sig +0 -0
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f4103f2661316cc4e24aab8bf9715472c04f6911b3e56bb3ecdab775a0e32139
4
- data.tar.gz: 0174d1cd163ac4fd046e4d432c0a4e2a07e2d57494ada9846ec61b6c73c2e434
3
+ metadata.gz: 52fde84aac9ffef63ecbc1d367e3bfdc7209f6c2904f2a7142f51b1bd0613002
4
+ data.tar.gz: 4b4e7838e19505469ec20a654a3d7bb9c2588f351f9ea64fdb05e224b476d562
5
5
  SHA512:
6
- metadata.gz: b3214a7e89730e932dbfbd3e5463ab0ce592d06589b5940f0987ded07f738d1f13557ae1d8251d8971beae7334c91bc655a8f64dd83049520d7ca889b2519e69
7
- data.tar.gz: 803ae22e05ad14025dc4bda185571c586727227154e201d979052223b8270b157d7bd8a9588c0aa074c9d5bf0ef31dba6a1a696c7c392171c585945343d2fd10
6
+ metadata.gz: 0ebdda184eca4c7dcfe39821125ba7a8645336607300395de792a7c8808cde07f54f5382278946fab58af0e95f3a85ab05be9ed6e7b292fc7692922cb7890747
7
+ data.tar.gz: 416cb01ed1b6dac0681969ce72505d19855a041e7ceb29e369ed9fc80675755ad1f478a20ee2d1f2c0e925036ba6f08adda727f8ecd3f4f07f39f0be347ae4a1
checksums.yaml.gz.sig CHANGED
Binary file
data/CHANGELOG.md CHANGED
@@ -1,5 +1,32 @@
1
1
  # Karafka framework changelog
2
2
 
3
+ ## 2.0.0.rc5 (2022-08-01)
4
+ - Improve specs stability
5
+ - Improve forceful shutdown
6
+ - Add support for debug `TTIN` backtrace printing
7
+ - Fix a case where logger listener would not intercept `warn` level
8
+ - Require `rdkafka` >= `0.12`
9
+ - Replace statistics decorator with the one from `karafka-core`
10
+
11
+ ## 2.0.0.rc4 (2022-07-28)
12
+ - Remove `dry-monitor`
13
+ - Use `karafka-core`
14
+ - Improve forceful shutdown resources finalization
15
+ - Cache consumer client name
16
+
17
+ ## 2.0.0.rc3 (2022-07-26)
18
+ - Fix Pro partitioner hash function may not utilize all the threads (#907).
19
+ - Improve virtual partitions messages distribution.
20
+ - Add StatsD/DataDog optional monitoring listener + dashboard template.
21
+ - Validate that Pro consumer is always used for Pro subscription.
22
+ - Improve ActiveJob consumer shutdown behaviour.
23
+ - Change default `max_wait_time` to 1 second.
24
+ - Change default `max_messages` to 100 (#915).
25
+ - Move logger listener polling reporting level to debug when no messages (#916).
26
+ - Improve stability on aggressive rebalancing (multiple rebalances in a short period).
27
+ - Improve specs stability.
28
+ - Allow using `:key` and `:partition_key` for Enhanced Active Job partitioning.
29
+
3
30
  ## 2.0.0.rc2 (2022-07-19)
4
31
  - Fix `example_consumer.rb.erb` `#shutdown` and `#revoked` signatures to correct once.
5
32
  - Improve the install user experience (print status and created files).
@@ -52,8 +79,8 @@
52
79
  - Fix a case where consecutive CTRL+C (non-stop) would case an exception during forced shutdown
53
80
  - Add missing `consumer.prepared.error` into `LoggerListener`
54
81
  - Delegate partition resuming from the consumers to listeners threads.
55
- - Add support for Long Running Jobs (LRJ) for ActiveJob [PRO]
56
- - Add support for Long Running Jobs for consumers [PRO]
82
+ - Add support for Long-Running Jobs (LRJ) for ActiveJob [PRO]
83
+ - Add support for Long-Running Jobs for consumers [PRO]
57
84
  - Allow `active_job_topic` to accept a block for extra topic related settings
58
85
  - Remove no longer needed logger threads
59
86
  - Auto-adapt number of processes for integration specs based on the number of CPUs
data/CONTRIBUTING.md CHANGED
@@ -1,4 +1,4 @@
1
- # Contribute
1
+ # Contributing
2
2
 
3
3
  ## Introduction
4
4
 
@@ -8,11 +8,7 @@ We welcome any type of contribution, not only code. You can help with:
8
8
  - **QA**: file bug reports, the more details you can give the better (e.g. screenshots with the console open)
9
9
  - **Marketing**: writing blog posts, howto's, printing stickers, ...
10
10
  - **Community**: presenting the project at meetups, organizing a dedicated meetup for the local community, ...
11
- - **Code**: take a look at the [open issues](issues). Even if you can't write code, commenting on them, showing that you care about a given issue matters. It helps us triage them.
12
-
13
- ## Your First Contribution
14
-
15
- Working on your first Pull Request? You can learn how from this *free* series, [How to Contribute to an Open Source Project on GitHub](https://egghead.io/series/how-to-contribute-to-an-open-source-project-on-github).
11
+ - **Code**: take a look at the [open issues](https://github.com/karafka/karafka/issues). Even if you can't write code, commenting on them, showing that you care about a given issue matters. It helps us triage them.
16
12
 
17
13
  ## Submitting code
18
14
 
@@ -32,5 +28,5 @@ By sending a pull request to the pro components, you are agreeing to transfer th
32
28
 
33
29
  ## Questions
34
30
 
35
- If you have any questions, create an [issue](issue) (protip: do a quick search first to see if someone else didn't ask the same question before!).
36
- You can also reach us at hello@karafka.opencollective.com.
31
+ If you have any questions, create an [issue](https://github.com/karafka/karafka/issues) (protip: do a quick search first to see if someone else didn't ask the same question before!).
32
+ You can also reach us at contact@karafka.io.
data/Gemfile.lock CHANGED
@@ -1,11 +1,11 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- karafka (2.0.0.rc2)
5
- dry-monitor (~> 0.5)
6
- rdkafka (>= 0.10)
4
+ karafka (2.0.0.rc5)
5
+ karafka-core (>= 2.0.2, < 3.0.0)
6
+ rdkafka (>= 0.12)
7
7
  thor (>= 0.20)
8
- waterdrop (>= 2.3.3, < 3.0.0)
8
+ waterdrop (>= 2.4.1, < 3.0.0)
9
9
  zeitwerk (~> 2.3)
10
10
 
11
11
  GEM
@@ -23,19 +23,6 @@ GEM
23
23
  concurrent-ruby (1.1.10)
24
24
  diff-lcs (1.5.0)
25
25
  docile (1.4.0)
26
- dry-configurable (0.15.0)
27
- concurrent-ruby (~> 1.0)
28
- dry-core (~> 0.6)
29
- dry-core (0.8.0)
30
- concurrent-ruby (~> 1.0)
31
- dry-events (0.3.0)
32
- concurrent-ruby (~> 1.0)
33
- dry-core (~> 0.5, >= 0.5)
34
- dry-monitor (0.6.1)
35
- dry-configurable (~> 0.13, >= 0.13.0)
36
- dry-core (~> 0.5, >= 0.5)
37
- dry-events (~> 0.2)
38
- zeitwerk (~> 2.5)
39
26
  factory_bot (6.2.1)
40
27
  activesupport (>= 5.0.0)
41
28
  ffi (1.15.5)
@@ -43,6 +30,8 @@ GEM
43
30
  activesupport (>= 5.0)
44
31
  i18n (1.12.0)
45
32
  concurrent-ruby (~> 1.0)
33
+ karafka-core (2.0.2)
34
+ concurrent-ruby (>= 1.1)
46
35
  mini_portile2 (2.8.0)
47
36
  minitest (5.16.2)
48
37
  rake (13.0.6)
@@ -70,11 +59,10 @@ GEM
70
59
  simplecov-html (0.12.3)
71
60
  simplecov_json_formatter (0.1.4)
72
61
  thor (1.2.1)
73
- tzinfo (2.0.4)
62
+ tzinfo (2.0.5)
74
63
  concurrent-ruby (~> 1.0)
75
- waterdrop (2.3.3)
76
- concurrent-ruby (>= 1.1)
77
- dry-monitor (~> 0.5)
64
+ waterdrop (2.4.1)
65
+ karafka-core (>= 2.0.2, < 3.0.0)
78
66
  rdkafka (>= 0.10)
79
67
  zeitwerk (~> 2.3)
80
68
  zeitwerk (2.6.0)
data/LICENSE-COMM CHANGED
@@ -30,7 +30,7 @@ The Open Source version of the Software (“LGPL Version”) is licensed under t
30
30
 
31
31
  4. Ownership. Notwithstanding anything to the contrary contained herein, except for the limited license rights expressly provided herein, Maciej Mensfeld and its suppliers have and will retain all rights, title and interest (including, without limitation, all patent, copyright, trademark, trade secret and other intellectual property rights) in and to the Software and all copies, modifications and derivative works thereof (including any changes which incorporate any of your ideas, feedback or suggestions). You acknowledge that you are obtaining only a limited license right to the Software, and that irrespective of any use of the words “purchase”, “sale” or like terms hereunder no ownership rights are being conveyed to you under this Agreement or otherwise.
32
32
 
33
- 5. Fees and Payment. The Software license fees will be due and payable in full as set forth in the applicable invoice or at the time of purchase. If the Software does not function properly within two weeks of purchase, please contact us within those two weeks for a refund. You shall be responsible for all taxes, withholdings, duties and levies arising from the order (excluding taxes based on the net income of Maciej Mensfeld).
33
+ 5. Fees and Payment. The Software license fees will be due and payable in full as set forth in the applicable invoice or at the time of purchase. There are no refunds beyond the remedy refund.
34
34
 
35
35
  6. Support, Maintenance and Services. Subject to the terms and conditions of this Agreement, as set forth in your invoice, and as set forth on the Karafka Pro support page (https://github.com/karafka/karafka/wiki/Commercial-Support), support and maintenance services may be included with the purchase of your license subscription.
36
36
 
data/README.md CHANGED
@@ -4,11 +4,18 @@
4
4
  [![Gem Version](https://badge.fury.io/rb/karafka.svg)](http://badge.fury.io/rb/karafka)
5
5
  [![Join the chat at https://slack.karafka.io](https://raw.githubusercontent.com/karafka/misc/master/slack.svg)](https://slack.karafka.io)
6
6
 
7
- **Note**: All of the documentation here refers to Karafka `2.0`. If you are looking for the documentation to Karafka `1.4` please click here (TBA).
7
+ **Note**: All of the documentation here refers to Karafka `2.0.0.rc4` or higher. If you are looking for the documentation for Karafka `1.4`, please click [here](https://github.com/karafka/wiki/tree/1.4).
8
8
 
9
9
  ## About Karafka
10
10
 
11
- Karafka is a multi-threaded framework used to simplify Apache Kafka based Ruby and Ruby on Rails applications development.
11
+ Karafka is a Ruby and Rails multi-threaded efficient Kafka processing framework that:
12
+
13
+ - Supports parallel processing in [multiple threads](https://github.com/karafka/karafka/wiki/Concurrency-and-multithreading) (also for a [single topic partition](https://github.com/karafka/karafka/wiki/Pro-Virtual-Partitions) work)
14
+ - Has [ActiveJob backend](https://github.com/karafka/karafka/wiki/Active-Job) support (including ordered jobs)
15
+ - [Automatically integrates](https://github.com/karafka/karafka/wiki/Integrating-with-Ruby-on-Rails-and-other-frameworks#integrating-with-ruby-on-rails=) with Ruby on Rails
16
+ - Supports in-development [code reloading](https://github.com/karafka/karafka/wiki/Auto-reload-of-code-changes-in-development)
17
+ - Is powered by [librdkafka](https://github.com/edenhill/librdkafka) (the Apache Kafka C/C++ client library)
18
+ - Has an out-of the box [StatsD/DataDog monitoring](https://github.com/karafka/karafka/wiki/Monitoring-and-logging) with a dashboard template.
12
19
 
13
20
  ```ruby
14
21
  # Define what topics you want to consume with which consumers in karafka.rb
@@ -28,22 +35,51 @@ class EventsConsumer < ApplicationConsumer
28
35
  end
29
36
  ```
30
37
 
31
- Karafka **uses** threads to handle many messages at the same time in the same process. It does not require Rails but will integrate tightly with any Ruby on Rails applications to make event processing dead simple.
38
+ Karafka **uses** threads to handle many messages simultaneously in the same process. It does not require Rails but will integrate tightly with any Ruby on Rails applications to make event processing dead simple.
32
39
 
33
40
  ## Getting started
34
41
 
35
- If you're completely new to the subject, you can start with our "Kafka on Rails" articles series, that will get you up and running with the terminology and basic ideas behind using Kafka:
42
+ If you're entirely new to the subject, you can start with our "Kafka on Rails" articles series, which will get you up and running with the terminology and basic ideas behind using Kafka:
36
43
 
37
44
  - [Kafka on Rails: Using Kafka with Ruby on Rails – Part 1 – Kafka basics and its advantages](https://mensfeld.pl/2017/11/kafka-on-rails-using-kafka-with-ruby-on-rails-part-1-kafka-basics-and-its-advantages/)
38
- - [Kafka on Rails: Using Kafka with Ruby on Rails – Part 2 – Getting started with Ruby and Kafka](https://mensfeld.pl/2018/01/kafka-on-rails-using-kafka-with-ruby-on-rails-part-2-getting-started-with-ruby-and-kafka/)
45
+ - [Kafka on Rails: Using Kafka with Ruby on Rails – Part 2 – Getting started with Rails and Kafka](https://mensfeld.pl/2018/01/kafka-on-rails-using-kafka-with-ruby-on-rails-part-2-getting-started-with-ruby-and-kafka/)
39
46
 
40
47
  If you want to get started with Kafka and Karafka as fast as possible, then the best idea is to visit our [Getting started](https://github.com/karafka/karafka/wiki/Getting-started) guides and the [example apps repository](https://github.com/karafka/example-apps).
41
48
 
42
49
  We also maintain many [integration specs](https://github.com/karafka/karafka/tree/master/spec/integrations) illustrating various use-cases and features of the framework.
43
50
 
51
+ ### TL;DR (1 minute from setup to publishing and consuming messages)
52
+
53
+ **Prerequisites**: Kafka running. You can start it by following instructions from [here](https://github.com/karafka/karafka/wiki/Setting-up-Kafka).
54
+
55
+ 1. Add and install Karafka:
56
+
57
+ ```bash
58
+ bundle add karafka -v 2.0.0.rc4
59
+
60
+ bundle exec karafka install
61
+ ```
62
+
63
+ 2. Dispatch a message to the example topic using the Rails or Ruby console:
64
+
65
+ ```ruby
66
+ Karafka.producer.produce_sync(topic: 'example', payload: { 'ping' => 'pong' }.to_json)
67
+ ```
68
+
69
+ 3. Run Karafka server and see the consumption magic happen:
70
+
71
+ ```bash
72
+ bundle exec karafka server
73
+
74
+ [7616dc24-505a-417f-b87b-6bf8fc2d98c5] Polled 1 message in 1000ms
75
+ [dcf3a8d8-0bd9-433a-8f63-b70a0cdb0732] Consume job for ExampleConsumer on example started
76
+ {"ping"=>"pong"}
77
+ [dcf3a8d8-0bd9-433a-8f63-b70a0cdb0732] Consume job for ExampleConsumer on example finished in 0ms
78
+ ```
79
+
44
80
  ## Want to Upgrade? LGPL is not for you? Want to help?
45
81
 
46
- I also sell Karafka Pro subscription. It includes commercial-friendly license, priority support, architecture consultations and high throughput data processing-related features (virtual partitions, long running jobs and more).
82
+ I also sell Karafka Pro subscriptions. It includes a commercial-friendly license, priority support, architecture consultations, and high throughput data processing-related features (virtual partitions, long-running jobs, and more).
47
83
 
48
84
  **20%** of the income will be distributed back to other OSS projects that Karafka uses under the hood.
49
85
 
@@ -51,6 +87,8 @@ Help me provide high-quality open-source software. Please see the Karafka [homep
51
87
 
52
88
  ## Support
53
89
 
54
- Karafka has [Wiki pages](https://github.com/karafka/karafka/wiki) for almost everything and a pretty decent [FAQ](https://github.com/karafka/karafka/wiki/FAQ). It covers the whole installation, setup and deployment along with other useful details on how to run Karafka.
90
+ Karafka has [Wiki pages](https://github.com/karafka/karafka/wiki) for almost everything and a pretty decent [FAQ](https://github.com/karafka/karafka/wiki/FAQ). It covers the installation, setup, and deployment, along with other useful details on how to run Karafka.
91
+
92
+ If you have questions about using Karafka, feel free to join our [Slack](https://slack.karafka.io) channel.
55
93
 
56
- If you have any questions about using Karafka, feel free to join our [Slack](https://slack.karafka.io) channel.
94
+ Karafka has priority support for technical and architectural questions that is part of the Karafka Pro subscription.
data/bin/integrations CHANGED
@@ -37,7 +37,7 @@ class Scenario
37
37
  'consumption/worker_critical_error_behaviour.rb' => [0, 2].freeze,
38
38
  'shutdown/on_hanging_jobs_and_a_shutdown.rb' => [2].freeze,
39
39
  'shutdown/on_hanging_on_shutdown_job_and_a_shutdown.rb' => [2].freeze,
40
- 'shutdown/on_hanging_poll_and_shutdown.rb' => [2].freeze
40
+ 'shutdown/on_hanging_listener_and_shutdown.rb' => [2].freeze
41
41
  }.freeze
42
42
 
43
43
  private_constant :MAX_RUN_TIME, :EXIT_CODES
data/config/errors.yml CHANGED
@@ -46,7 +46,11 @@ en:
46
46
  missing: needs to be present
47
47
  dispatch_method_format: needs to be either :produce_async or :produce_sync
48
48
  partitioner_format: 'needs to respond to #call'
49
+ partition_key_type_format: 'needs to be either :key or :partition_key'
49
50
 
50
51
  test:
51
52
  missing: needs to be present
52
53
  id_format: needs to be a String
54
+
55
+ pro_consumer_group_topic:
56
+ consumer_format: needs to inherit from Karafka::Pro::BaseConsumer and not Karafka::Consumer
data/docker-compose.yml CHANGED
@@ -36,10 +36,13 @@ services:
36
36
  integrations_17_02:2:1,\
37
37
  integrations_18_02:2:1,\
38
38
  integrations_19_02:2:1,\
39
+ integrations_20_02:2:1,\
40
+ integrations_21_02:2:1,\
39
41
  integrations_00_03:3:1,\
40
42
  integrations_01_03:3:1,\
41
43
  integrations_02_03:3:1,\
42
44
  integrations_03_03:3:1,\
45
+ integrations_04_03:3:1,\
43
46
  integrations_00_10:10:1,\
44
47
  integrations_01_10:10:1,\
45
48
  benchmarks_00_01:1:1,\
data/karafka.gemspec CHANGED
@@ -12,17 +12,17 @@ Gem::Specification.new do |spec|
12
12
  spec.authors = ['Maciej Mensfeld']
13
13
  spec.email = %w[maciej@mensfeld.pl]
14
14
  spec.homepage = 'https://karafka.io'
15
- spec.summary = 'Ruby framework for working with Apache Kafka'
15
+ spec.summary = 'Efficient Kafka processing framework for Ruby and Rails'
16
16
  spec.description = 'Framework used to simplify Apache Kafka based Ruby applications development'
17
17
  spec.licenses = ['LGPL-3.0', 'Commercial']
18
18
 
19
- spec.add_dependency 'dry-monitor', '~> 0.5'
20
- spec.add_dependency 'rdkafka', '>= 0.10'
19
+ spec.add_dependency 'karafka-core', '>= 2.0.2', '< 3.0.0'
20
+ spec.add_dependency 'rdkafka', '>= 0.12'
21
21
  spec.add_dependency 'thor', '>= 0.20'
22
- spec.add_dependency 'waterdrop', '>= 2.3.3', '< 3.0.0'
22
+ spec.add_dependency 'waterdrop', '>= 2.4.1', '< 3.0.0'
23
23
  spec.add_dependency 'zeitwerk', '~> 2.3'
24
24
 
25
- spec.required_ruby_version = '>= 2.6.0'
25
+ spec.required_ruby_version = '>= 2.7.0'
26
26
 
27
27
  if $PROGRAM_NAME.end_with?('gem')
28
28
  spec.signing_key = File.expand_path('~/.ssh/gem-private_key.pem')
@@ -9,6 +9,8 @@ module Karafka
9
9
  # @note ActiveJob does not support batches, so we just run one message after another
10
10
  def consume
11
11
  messages.each do |message|
12
+ break if Karafka::App.stopping?
13
+
12
14
  ::ActiveJob::Base.execute(
13
15
  # We technically speaking could set this as deserializer and reference it from the
14
16
  # message instead of using the `#raw_payload`. This is not done on purpose to simplify
@@ -69,9 +69,6 @@ module Karafka
69
69
  # Put a message to the buffer if there is one
70
70
  @buffer << message if message
71
71
 
72
- # Track time spent on all of the processing and polling
73
- time_poll.checkpoint
74
-
75
72
  # Upon polling rebalance manager might have been updated.
76
73
  # If partition revocation happens, we need to remove messages from revoked partitions
77
74
  # as well as ensure we do not have duplicated due to the offset reset for partitions
@@ -82,6 +79,9 @@ module Karafka
82
79
  break
83
80
  end
84
81
 
82
+ # Track time spent on all of the processing and polling
83
+ time_poll.checkpoint
84
+
85
85
  # Finally once we've (potentially) removed revoked, etc, if no messages were returned
86
86
  # we can break.
87
87
  # Worth keeping in mind, that the rebalance manager might have been updated despite no
@@ -268,13 +268,18 @@ module Karafka
268
268
  true
269
269
  rescue Rdkafka::RdkafkaError => e
270
270
  return false if e.code == :assignment_lost
271
- return false if e.code == :no_offset
271
+ return true if e.code == :no_offset
272
272
 
273
273
  raise e
274
274
  end
275
275
 
276
276
  # Commits the stored offsets in a sync way and closes the consumer.
277
277
  def close
278
+ # Once client is closed, we should not close it again
279
+ # This could only happen in case of a race-condition when forceful shutdown happens
280
+ # and triggers this from a different thread
281
+ return if @closed
282
+
278
283
  @mutex.synchronize do
279
284
  internal_commit_offsets(async: false)
280
285
 
@@ -34,6 +34,8 @@ module Karafka
34
34
  # We can do this that way because we always first schedule jobs using messages before we
35
35
  # fetch another batch.
36
36
  @messages_buffer = MessagesBuffer.new(subscription_group)
37
+ @mutex = Mutex.new
38
+ @stopped = false
37
39
  end
38
40
 
39
41
  # Runs the main listener fetch loop.
@@ -51,6 +53,25 @@ module Karafka
51
53
  fetch_loop
52
54
  end
53
55
 
56
+ # Stops the jobs queue, triggers shutdown on all the executors (sync), commits offsets and
57
+ # stops kafka client.
58
+ #
59
+ # @note This method is not private despite being part of the fetch loop because in case of
60
+ # a forceful shutdown, it may be invoked from a separate thread
61
+ #
62
+ # @note We wrap it with a mutex exactly because of the above case of forceful shutdown
63
+ def shutdown
64
+ return if @stopped
65
+
66
+ @mutex.synchronize do
67
+ @stopped = true
68
+ @executors.clear
69
+ @coordinators.reset
70
+ @client.commit_offsets!
71
+ @client.stop
72
+ end
73
+ end
74
+
54
75
  private
55
76
 
56
77
  # Fetches the data and adds it to the jobs queue.
@@ -239,13 +260,6 @@ module Karafka
239
260
  @client.batch_poll until @jobs_queue.empty?(@subscription_group.id)
240
261
  end
241
262
 
242
- # Stops the jobs queue, triggers shutdown on all the executors (sync), commits offsets and
243
- # stops kafka client.
244
- def shutdown
245
- @client.commit_offsets!
246
- @client.stop
247
- end
248
-
249
263
  # We can stop client without a problem, as it will reinitialize itself when running the
250
264
  # `#fetch_loop` again. We just need to remember to also reset the runner as it is a long
251
265
  # running one, so with a new connection to Kafka, we need to initialize the state of the
@@ -3,7 +3,7 @@
3
3
  module Karafka
4
4
  module Contracts
5
5
  # Base contract for all Karafka contracts
6
- class Base < ::WaterDrop::Contractable::Contract
6
+ class Base < ::Karafka::Core::Contractable::Contract
7
7
  # @param data [Hash] data for validation
8
8
  # @return [Boolean] true if all good
9
9
  # @raise [Errors::InvalidConfigurationError] invalid configuration error
@@ -24,9 +24,6 @@ module Karafka
24
24
  # Raised when we try to use Karafka CLI commands (except install) without a boot file
25
25
  MissingBootFileError = Class.new(BaseError)
26
26
 
27
- # Raised when want to hook up to an event that is not registered and supported
28
- UnregisteredMonitorEventError = Class.new(BaseError)
29
-
30
27
  # Raised when we've waited enough for shutting down a non-responsive process
31
28
  ForcefulShutdownError = Class.new(BaseError)
32
29
 
@@ -16,8 +16,7 @@ module Karafka
16
16
  @consumer_group_id = consumer_group_id
17
17
  @client_name = client_name
18
18
  @monitor = monitor
19
- # We decorate both Karafka and WaterDrop statistics the same way
20
- @statistics_decorator = ::WaterDrop::Instrumentation::Callbacks::StatisticsDecorator.new
19
+ @statistics_decorator = ::Karafka::Core::Monitoring::StatisticsDecorator.new
21
20
  end
22
21
 
23
22
  # Emits decorated statistics to the monitor
@@ -9,6 +9,7 @@ module Karafka
9
9
  USED_LOG_LEVELS = %i[
10
10
  debug
11
11
  info
12
+ warn
12
13
  error
13
14
  fatal
14
15
  ].freeze
@@ -18,7 +19,7 @@ module Karafka
18
19
  # @param event [Dry::Events::Event] event details including payload
19
20
  def on_connection_listener_fetch_loop(event)
20
21
  listener = event[:caller]
21
- info "[#{listener.id}] Polling messages..."
22
+ debug "[#{listener.id}] Polling messages..."
22
23
  end
23
24
 
24
25
  # Logs about messages that we've received from Kafka
@@ -28,7 +29,13 @@ module Karafka
28
29
  listener = event[:caller]
29
30
  time = event[:time]
30
31
  messages_count = event[:messages_buffer].size
31
- info "[#{listener.id}] Polled #{messages_count} messages in #{time}ms"
32
+
33
+ message = "[#{listener.id}] Polled #{messages_count} messages in #{time}ms"
34
+
35
+ # We don't want the "polled 0" in dev as it would spam the log
36
+ # Instead we publish only info when there was anything we could poll and fail over to the
37
+ # zero notifications when in debug mode
38
+ messages_count.zero? ? debug(message) : info(message)
32
39
  end
33
40
 
34
41
  # Prints info about the fact that a given job has started
@@ -54,11 +61,28 @@ module Karafka
54
61
  info "[#{job.id}] #{job_type} job for #{consumer} on #{topic} finished in #{time}ms"
55
62
  end
56
63
 
57
- # Logs info about system signals that Karafka received.
64
+ # Logs info about system signals that Karafka received and prints backtrace for threads in
65
+ # case of ttin
58
66
  #
59
67
  # @param event [Dry::Events::Event] event details including payload
60
68
  def on_process_notice_signal(event)
61
69
  info "Received #{event[:signal]} system signal"
70
+
71
+ # We print backtrace only for ttin
72
+ return unless event[:signal] == :SIGTTIN
73
+
74
+ # Inspired by Sidekiq
75
+ Thread.list.each do |thread|
76
+ tid = (thread.object_id ^ ::Process.pid).to_s(36)
77
+
78
+ warn "Thread TID-#{tid} #{thread['label']}"
79
+
80
+ if thread.backtrace
81
+ warn thread.backtrace.join("\n")
82
+ else
83
+ warn '<no backtrace available>'
84
+ end
85
+ end
62
86
  end
63
87
 
64
88
  # Logs info that we're initializing Karafka app.
@@ -1,66 +1,21 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Karafka
4
- # Namespace for all the things related with Karafka instrumentation process
5
4
  module Instrumentation
6
- # Monitor is used to hookup external monitoring services to monitor how Karafka works
7
- # It provides a standardized API for checking incoming messages/enqueueing etc
8
- # Since it is a pub-sub based on dry-monitor, you can use as many subscribers/loggers at the
9
- # same time, which means that you might have for example file logging and NewRelic at the same
10
- # time
11
- # @note This class acts as a singleton because we are only permitted to have single monitor
12
- # per running process (just as logger)
13
- class Monitor < Dry::Monitor::Notifications
14
- # List of events that we support in the system and to which a monitor client can hook up
15
- # @note The non-error once support timestamp benchmarking
16
- # @note Depending on Karafka extensions and additional engines, this might not be the
17
- # complete list of all the events. Please use the #available_events on fully loaded
18
- # Karafka system to determine all of the events you can use.
19
- BASE_EVENTS = %w[
20
- app.initialized
21
- app.running
22
- app.stopping
23
- app.stopped
24
-
25
- consumer.consumed
26
- consumer.revoked
27
- consumer.shutdown
28
-
29
- process.notice_signal
30
-
31
- connection.listener.before_fetch_loop
32
- connection.listener.fetch_loop
33
- connection.listener.fetch_loop.received
34
-
35
- worker.process
36
- worker.processed
37
-
38
- statistics.emitted
39
-
40
- error.occurred
41
- ].freeze
42
-
43
- private_constant :BASE_EVENTS
44
-
45
- # @return [Karafka::Instrumentation::Monitor] monitor instance for system instrumentation
46
- def initialize
47
- super(:karafka)
48
- BASE_EVENTS.each(&method(:register_event))
49
- end
50
-
51
- # Allows us to subscribe to events with a code that will be yielded upon events
52
- # @param event_name_or_listener [String, Object] name of the event we want to subscribe to
53
- # or a listener if we decide to go with object listener
54
- def subscribe(event_name_or_listener)
55
- return super unless event_name_or_listener.is_a?(String)
56
- return super if available_events.include?(event_name_or_listener)
57
-
58
- raise Errors::UnregisteredMonitorEventError, event_name_or_listener
59
- end
60
-
61
- # @return [Array<String>] names of available events to which we can subscribe
62
- def available_events
63
- __bus__.events.keys
5
+ # Karafka instrumentation monitor that we use to publish events
6
+ # By default uses our internal notifications bus but can be used with
7
+ # `ActiveSupport::Notifications` as well
8
+ class Monitor < ::Karafka::Core::Monitoring::Monitor
9
+ attr_reader :notifications_bus
10
+
11
+ # @param notifications_bus [Object] either our internal notifications bus or
12
+ # `ActiveSupport::Notifications`
13
+ # @param namespace [String, nil] namespace for events or nil if no namespace
14
+ def initialize(
15
+ notifications_bus = ::Karafka::Instrumentation::Notifications.new,
16
+ namespace = nil
17
+ )
18
+ super(notifications_bus, namespace)
64
19
  end
65
20
  end
66
21
  end
@@ -0,0 +1,52 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Karafka
4
+ # Namespace for all the things related with Karafka instrumentation process
5
+ module Instrumentation
6
+ # Monitor is used to hookup external monitoring services to monitor how Karafka works
7
+ # It provides a standardized API for checking incoming messages/enqueueing etc
8
+ # Since it is a pub-sub based on dry-monitor, you can use as many subscribers/loggers at the
9
+ # same time, which means that you might have for example file logging and NewRelic at the same
10
+ # time
11
+ # @note This class acts as a singleton because we are only permitted to have single monitor
12
+ # per running process (just as logger)
13
+ class Notifications < Karafka::Core::Monitoring::Notifications
14
+ # List of events that we support in the system and to which a monitor client can hook up
15
+ # @note The non-error once support timestamp benchmarking
16
+ # @note Depending on Karafka extensions and additional engines, this might not be the
17
+ # complete list of all the events. Please use the #available_events on fully loaded
18
+ # Karafka system to determine all of the events you can use.
19
+ EVENTS = %w[
20
+ app.initialized
21
+ app.running
22
+ app.stopping
23
+ app.stopped
24
+
25
+ consumer.consumed
26
+ consumer.revoked
27
+ consumer.shutdown
28
+
29
+ process.notice_signal
30
+
31
+ connection.listener.before_fetch_loop
32
+ connection.listener.fetch_loop
33
+ connection.listener.fetch_loop.received
34
+
35
+ worker.process
36
+ worker.processed
37
+
38
+ statistics.emitted
39
+
40
+ error.occurred
41
+ ].freeze
42
+
43
+ private_constant :EVENTS
44
+
45
+ # @return [Karafka::Instrumentation::Monitor] monitor instance for system instrumentation
46
+ def initialize
47
+ super
48
+ EVENTS.each { |event| register_event(event) }
49
+ end
50
+ end
51
+ end
52
+ end