karafka 1.2.8 → 1.4.0

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 (113) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data.tar.gz.sig +0 -0
  4. data/.coditsu/ci.yml +3 -0
  5. data/.console_irbrc +1 -3
  6. data/.diffend.yml +3 -0
  7. data/.github/FUNDING.yml +3 -0
  8. data/.github/ISSUE_TEMPLATE/bug_report.md +50 -0
  9. data/.github/ISSUE_TEMPLATE/feature_request.md +20 -0
  10. data/.github/workflows/ci.yml +52 -0
  11. data/.gitignore +1 -0
  12. data/.ruby-version +1 -1
  13. data/CHANGELOG.md +134 -14
  14. data/CODE_OF_CONDUCT.md +1 -1
  15. data/CONTRIBUTING.md +1 -1
  16. data/Gemfile +4 -5
  17. data/Gemfile.lock +92 -81
  18. data/README.md +9 -12
  19. data/bin/karafka +1 -1
  20. data/certs/mensfeld.pem +25 -0
  21. data/config/errors.yml +38 -5
  22. data/docker-compose.yml +17 -0
  23. data/karafka.gemspec +18 -17
  24. data/lib/karafka.rb +10 -16
  25. data/lib/karafka/app.rb +14 -6
  26. data/lib/karafka/attributes_map.rb +5 -10
  27. data/lib/karafka/base_consumer.rb +19 -30
  28. data/lib/karafka/base_responder.rb +45 -27
  29. data/lib/karafka/cli.rb +2 -2
  30. data/lib/karafka/cli/console.rb +11 -9
  31. data/lib/karafka/cli/flow.rb +9 -7
  32. data/lib/karafka/cli/info.rb +4 -2
  33. data/lib/karafka/cli/install.rb +30 -6
  34. data/lib/karafka/cli/server.rb +11 -6
  35. data/lib/karafka/code_reloader.rb +67 -0
  36. data/lib/karafka/connection/api_adapter.rb +22 -9
  37. data/lib/karafka/connection/batch_delegator.rb +55 -0
  38. data/lib/karafka/connection/builder.rb +5 -3
  39. data/lib/karafka/connection/client.rb +31 -31
  40. data/lib/karafka/connection/listener.rb +26 -15
  41. data/lib/karafka/connection/message_delegator.rb +36 -0
  42. data/lib/karafka/consumers/batch_metadata.rb +10 -0
  43. data/lib/karafka/consumers/callbacks.rb +32 -15
  44. data/lib/karafka/consumers/includer.rb +31 -18
  45. data/lib/karafka/consumers/responders.rb +2 -2
  46. data/lib/karafka/contracts.rb +10 -0
  47. data/lib/karafka/contracts/config.rb +21 -0
  48. data/lib/karafka/contracts/consumer_group.rb +206 -0
  49. data/lib/karafka/contracts/consumer_group_topic.rb +19 -0
  50. data/lib/karafka/contracts/responder_usage.rb +54 -0
  51. data/lib/karafka/contracts/server_cli_options.rb +31 -0
  52. data/lib/karafka/errors.rb +17 -16
  53. data/lib/karafka/fetcher.rb +28 -30
  54. data/lib/karafka/helpers/class_matcher.rb +12 -2
  55. data/lib/karafka/helpers/config_retriever.rb +1 -1
  56. data/lib/karafka/helpers/inflector.rb +26 -0
  57. data/lib/karafka/helpers/multi_delegator.rb +0 -1
  58. data/lib/karafka/instrumentation/logger.rb +9 -6
  59. data/lib/karafka/instrumentation/monitor.rb +15 -9
  60. data/lib/karafka/instrumentation/proctitle_listener.rb +36 -0
  61. data/lib/karafka/instrumentation/stdout_listener.rb +140 -0
  62. data/lib/karafka/params/batch_metadata.rb +26 -0
  63. data/lib/karafka/params/builders/batch_metadata.rb +30 -0
  64. data/lib/karafka/params/builders/params.rb +38 -0
  65. data/lib/karafka/params/builders/params_batch.rb +25 -0
  66. data/lib/karafka/params/metadata.rb +20 -0
  67. data/lib/karafka/params/params.rb +54 -0
  68. data/lib/karafka/params/params_batch.rb +35 -21
  69. data/lib/karafka/patches/ruby_kafka.rb +21 -8
  70. data/lib/karafka/persistence/client.rb +15 -11
  71. data/lib/karafka/persistence/{consumer.rb → consumers.rb} +20 -13
  72. data/lib/karafka/persistence/topics.rb +48 -0
  73. data/lib/karafka/process.rb +0 -2
  74. data/lib/karafka/responders/builder.rb +1 -1
  75. data/lib/karafka/responders/topic.rb +6 -8
  76. data/lib/karafka/routing/builder.rb +36 -8
  77. data/lib/karafka/routing/consumer_group.rb +1 -1
  78. data/lib/karafka/routing/consumer_mapper.rb +9 -9
  79. data/lib/karafka/routing/proxy.rb +10 -1
  80. data/lib/karafka/routing/topic.rb +5 -3
  81. data/lib/karafka/routing/topic_mapper.rb +16 -18
  82. data/lib/karafka/serialization/json/deserializer.rb +27 -0
  83. data/lib/karafka/serialization/json/serializer.rb +31 -0
  84. data/lib/karafka/server.rb +29 -28
  85. data/lib/karafka/setup/config.rb +67 -37
  86. data/lib/karafka/setup/configurators/water_drop.rb +7 -3
  87. data/lib/karafka/setup/dsl.rb +0 -1
  88. data/lib/karafka/status.rb +7 -3
  89. data/lib/karafka/templates/{application_consumer.rb.example → application_consumer.rb.erb} +2 -1
  90. data/lib/karafka/templates/{application_responder.rb.example → application_responder.rb.erb} +0 -0
  91. data/lib/karafka/templates/karafka.rb.erb +92 -0
  92. data/lib/karafka/version.rb +1 -1
  93. metadata +94 -72
  94. metadata.gz.sig +0 -0
  95. data/.travis.yml +0 -21
  96. data/lib/karafka/callbacks.rb +0 -30
  97. data/lib/karafka/callbacks/config.rb +0 -22
  98. data/lib/karafka/callbacks/dsl.rb +0 -16
  99. data/lib/karafka/connection/delegator.rb +0 -46
  100. data/lib/karafka/instrumentation/listener.rb +0 -112
  101. data/lib/karafka/loader.rb +0 -28
  102. data/lib/karafka/params/dsl.rb +0 -156
  103. data/lib/karafka/parsers/json.rb +0 -38
  104. data/lib/karafka/patches/dry_configurable.rb +0 -35
  105. data/lib/karafka/persistence/topic.rb +0 -29
  106. data/lib/karafka/schemas/config.rb +0 -24
  107. data/lib/karafka/schemas/consumer_group.rb +0 -78
  108. data/lib/karafka/schemas/consumer_group_topic.rb +0 -18
  109. data/lib/karafka/schemas/responder_usage.rb +0 -39
  110. data/lib/karafka/schemas/server_cli_options.rb +0 -43
  111. data/lib/karafka/setup/configurators/base.rb +0 -29
  112. data/lib/karafka/setup/configurators/params.rb +0 -25
  113. data/lib/karafka/templates/karafka.rb.example +0 -54
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Karafka
4
+ module Contracts
5
+ # Consumer group topic validation rules
6
+ class ConsumerGroupTopic < Dry::Validation::Contract
7
+ params do
8
+ required(:id).filled(:str?, format?: Karafka::Contracts::TOPIC_REGEXP)
9
+ required(:name).filled(:str?, format?: Karafka::Contracts::TOPIC_REGEXP)
10
+ required(:backend).filled(included_in?: %i[inline sidekiq])
11
+ required(:consumer).filled
12
+ required(:deserializer).filled
13
+ required(:max_bytes_per_partition).filled(:int?, gteq?: 0)
14
+ required(:start_from_beginning).filled(:bool?)
15
+ required(:batch_consuming).filled(:bool?)
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,54 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Karafka
4
+ module Contracts
5
+ # Validator to check responder topic usage
6
+ class ResponderUsageTopic < Dry::Validation::Contract
7
+ config.messages.load_paths << File.join(Karafka.gem_root, 'config', 'errors.yml')
8
+
9
+ params do
10
+ required(:name).filled(:str?, format?: Karafka::Contracts::TOPIC_REGEXP)
11
+ required(:required).filled(:bool?)
12
+ required(:usage_count).filled(:int?, gteq?: 0)
13
+ required(:registered).filled(eql?: true)
14
+ required(:async).filled(:bool?)
15
+ required(:serializer).filled
16
+ end
17
+
18
+ rule(:required, :usage_count) do
19
+ key(:name).failure(:required_usage_count) if values[:required] && values[:usage_count] < 1
20
+ end
21
+ end
22
+
23
+ # Validator to check that everything in a responder flow matches responder rules
24
+ class ResponderUsage < Dry::Validation::Contract
25
+ include Dry::Core::Constants
26
+
27
+ # Contract for verifying the topic usage details
28
+ TOPIC_CONTRACT = ResponderUsageTopic.new.freeze
29
+
30
+ private_constant :TOPIC_CONTRACT
31
+
32
+ params do
33
+ required(:used_topics)
34
+ required(:registered_topics)
35
+ end
36
+
37
+ rule(:used_topics) do
38
+ (value || EMPTY_ARRAY).each do |used_topic|
39
+ TOPIC_CONTRACT.call(used_topic).errors.each do |error|
40
+ key([:used_topics, used_topic, error.path[0]]).failure(error.text)
41
+ end
42
+ end
43
+ end
44
+
45
+ rule(:registered_topics) do
46
+ (value || EMPTY_ARRAY).each do |used_topic|
47
+ TOPIC_CONTRACT.call(used_topic).errors.each do |error|
48
+ key([:registered_topics, used_topic, error.path[0]]).failure(error.text)
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Karafka
4
+ module Contracts
5
+ # Contract for validating correctness of the server cli command options
6
+ # We validate some basics + the list of consumer_groups on which we want to use, to make
7
+ # sure that all of them are defined, plus that a pidfile does not exist
8
+ class ServerCliOptions < Dry::Validation::Contract
9
+ config.messages.load_paths << File.join(Karafka.gem_root, 'config', 'errors.yml')
10
+
11
+ params do
12
+ optional(:pid).filled(:str?)
13
+ optional(:daemon).filled(:bool?)
14
+ optional(:consumer_groups).value(:array, :filled?)
15
+ end
16
+
17
+ rule(:pid) do
18
+ key(:pid).failure(:pid_already_exists) if value && File.exist?(value)
19
+ end
20
+
21
+ rule(:consumer_groups) do
22
+ # If there were no consumer_groups declared in the server cli, it means that we will
23
+ # run all of them and no need to validate them here at all
24
+ if !value.nil? &&
25
+ !(value - Karafka::App.config.internal.routing_builder.map(&:name)).empty?
26
+ key(:consumer_groups).failure(:consumer_groups_inclusion)
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
@@ -6,17 +6,18 @@ module Karafka
6
6
  # Base class for all the Karafka internal errors
7
7
  BaseError = Class.new(StandardError)
8
8
 
9
- # Should be raised when we attemp to parse incoming params but parsing fails
10
- # If this error (or its descendant) is detected, we will pass the raw message
11
- # into params and proceed further
12
- ParserError = Class.new(BaseError)
9
+ # Should be raised when we have that that we cannot serialize
10
+ SerializationError = Class.new(BaseError)
11
+
12
+ # Should be raised when we tried to deserialize incoming data but we failed
13
+ DeserializationError = Class.new(BaseError)
13
14
 
14
15
  # Raised when router receives topic name which does not correspond with any routes
15
16
  # This can only happen in a case when:
16
17
  # - you've received a message and we cannot match it with a consumer
17
18
  # - you've changed the routing, so router can no longer associate your topic to
18
19
  # any consumer
19
- # - or in a case when you do a lot of metaprogramming and you change routing/etc on runtime
20
+ # - or in a case when you do a lot of meta-programming and you change routing/etc on runtime
20
21
  #
21
22
  # In case this happens, you will have to create a temporary route that will allow
22
23
  # you to "eat" everything from the Sidekiq queue.
@@ -25,26 +26,26 @@ module Karafka
25
26
 
26
27
  # Raised when we don't use or use responder not in the way it expected to based on the
27
28
  # topics usage definitions
28
- InvalidResponderUsage = Class.new(BaseError)
29
+ InvalidResponderUsageError = Class.new(BaseError)
29
30
 
30
- # Raised when options that we provide to the responder to respond aren't what the schema
31
+ # Raised when options that we provide to the responder to respond aren't what the contract
31
32
  # requires
32
- InvalidResponderMessageOptions = Class.new(BaseError)
33
+ InvalidResponderMessageOptionsError = Class.new(BaseError)
33
34
 
34
- # Raised when configuration doesn't match with validation schema
35
- InvalidConfiguration = Class.new(BaseError)
35
+ # Raised when configuration doesn't match with validation contract
36
+ InvalidConfigurationError = Class.new(BaseError)
36
37
 
37
- # Raised when we try to use Karafka CLI commands (except install) without a bootfile
38
- MissingBootFile = Class.new(BaseError)
38
+ # Raised when we try to use Karafka CLI commands (except install) without a boot file
39
+ MissingBootFileError = Class.new(BaseError)
39
40
 
40
41
  # Raised when we want to read a persisted thread messages consumer but it is unavailable
41
42
  # This should never happen and if it does, please contact us
42
- MissingClient = Class.new(BaseError)
43
+ MissingClientError = Class.new(BaseError)
43
44
 
44
45
  # Raised when want to hook up to an event that is not registered and supported
45
- UnregisteredMonitorEvent = Class.new(BaseError)
46
+ UnregisteredMonitorEventError = Class.new(BaseError)
46
47
 
47
- # Raised when we've waited enough for shutting down an unresponding process
48
- ForcefulShutdown = Class.new(BaseError)
48
+ # Raised when we've waited enough for shutting down a non-responsive process
49
+ ForcefulShutdownError = Class.new(BaseError)
49
50
  end
50
51
  end
@@ -5,39 +5,37 @@ module Karafka
5
5
  # @note Creating multiple fetchers will result in having multiple connections to the same
6
6
  # topics, which means that if there are no partitions, it won't use them.
7
7
  class Fetcher
8
- class << self
9
- # Starts listening on all the listeners asynchronously
10
- # Fetch loop should never end, which means that we won't create more actor clusters
11
- # so we don't have to terminate them
12
- def call
13
- threads = listeners.map do |listener|
14
- # We abort on exception because there should be an exception handling developed for
15
- # each listener running in separate threads, so the exceptions should never leak
16
- # and if that happens, it means that something really bad happened and we should stop
17
- # the whole process
18
- Thread
19
- .new { listener.call }
20
- .tap { |thread| thread.abort_on_exception = true }
21
- end
22
-
23
- # We aggregate threads here for a supervised shutdown process
24
- threads.each { |thread| Karafka::Server.consumer_threads << thread }
25
- threads.each(&:join)
26
- # If anything crashes here, we need to raise the error and crush the runner because it means
27
- # that something terrible happened
28
- rescue StandardError => e
29
- Karafka.monitor.instrument('fetcher.call.error', caller: self, error: e)
30
- Karafka::App.stop!
31
- raise e
8
+ # Starts listening on all the listeners asynchronously
9
+ # Fetch loop should never end, which means that we won't create more actor clusters
10
+ # so we don't have to terminate them
11
+ def call
12
+ threads = listeners.map do |listener|
13
+ # We abort on exception because there should be an exception handling developed for
14
+ # each listener running in separate threads, so the exceptions should never leak
15
+ # and if that happens, it means that something really bad happened and we should stop
16
+ # the whole process
17
+ Thread
18
+ .new { listener.call }
19
+ .tap { |thread| thread.abort_on_exception = true }
32
20
  end
33
21
 
34
- private
22
+ # We aggregate threads here for a supervised shutdown process
23
+ threads.each { |thread| Karafka::Server.consumer_threads << thread }
24
+ threads.each(&:join)
25
+ # If anything crashes here, we need to raise the error and crush the runner because it means
26
+ # that something terrible happened
27
+ rescue StandardError => e
28
+ Karafka.monitor.instrument('fetcher.call.error', caller: self, error: e)
29
+ Karafka::App.stop!
30
+ raise e
31
+ end
32
+
33
+ private
35
34
 
36
- # @return [Array<Karafka::Connection::Listener>] listeners that will consume messages
37
- def listeners
38
- @listeners ||= App.consumer_groups.active.map do |consumer_group|
39
- Karafka::Connection::Listener.new(consumer_group)
40
- end
35
+ # @return [Array<Karafka::Connection::Listener>] listeners that will consume messages
36
+ def listeners
37
+ @listeners ||= App.consumer_groups.active.map do |consumer_group|
38
+ Karafka::Connection::Listener.new(consumer_group)
41
39
  end
42
40
  end
43
41
  end
@@ -8,7 +8,9 @@ module Karafka
8
8
  class ClassMatcher
9
9
  # Regexp used to remove any non classy like characters that might be in the consumer
10
10
  # class name (if defined dynamically, etc)
11
- CONSTANT_REGEXP = %r{[?!=+\-\*/\^\|&\[\]<>%~\#\:\s\(\)]}
11
+ CONSTANT_REGEXP = %r{[?!=+\-\*/\^\|&\[\]<>%~\#\:\s\(\)]}.freeze
12
+
13
+ private_constant :CONSTANT_REGEXP
12
14
 
13
15
  # @param klass [Class] class to which we want to find a corresponding class
14
16
  # @param from [String] what type of object is it (based on postfix name part)
@@ -30,6 +32,7 @@ module Karafka
30
32
  def match
31
33
  return nil if name.empty?
32
34
  return nil unless scope.const_defined?(name)
35
+
33
36
  matching = scope.const_get(name)
34
37
  same_scope?(matching) ? matching : nil
35
38
  end
@@ -41,7 +44,13 @@ module Karafka
41
44
  # @example From Namespaced::Super2Consumer matching responder
42
45
  # matcher.name #=> Super2Responder
43
46
  def name
44
- inflected = @klass.to_s.split('::').last.to_s
47
+ inflected = +@klass.to_s.split('::').last.to_s
48
+ # We inject the from into the name just in case it is missing as in a situation like
49
+ # that it would just sanitize the name without adding the "to" postfix.
50
+ # It could create cases when we want to build for example a responder to a consumer
51
+ # that does not have the "Consumer" postfix and would do nothing returning the same name.
52
+ # That would be bad as the matching classes shouldn't be matched to themselves.
53
+ inflected << @from unless inflected.include?(@from)
45
54
  inflected.gsub!(@from, @to)
46
55
  inflected.gsub!(CONSTANT_REGEXP, '')
47
56
  inflected
@@ -65,6 +74,7 @@ module Karafka
65
74
  def scope_of(klass)
66
75
  enclosing = klass.to_s.split('::')[0...-1]
67
76
  return ::Object if enclosing.empty?
77
+
68
78
  ::Object.const_get(enclosing.join('::'))
69
79
  end
70
80
 
@@ -5,7 +5,7 @@ module Karafka
5
5
  # A helper method that allows us to build methods that try to get a given
6
6
  # attribute from its instance value and if it fails, will fallback to
7
7
  # the default config or config.kafka value for a given attribute.
8
- # It is used to simplify the checkings.
8
+ # It is used to simplify the checks.
9
9
  # @note Worth noticing, that the value might be equal to false, so even
10
10
  # then we need to return it. That's why we check for nil?
11
11
  # @example Define config retried attribute for start_from_beginning
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Karafka
4
+ module Helpers
5
+ # Inflector provides inflection for the whole Karafka framework with additional inflection
6
+ # caching (due to the fact, that Dry::Inflector is slow)
7
+ module Inflector
8
+ # What inflection engine do we want to use
9
+ ENGINE = Dry::Inflector.new
10
+
11
+ @map = Concurrent::Hash.new
12
+
13
+ private_constant :ENGINE
14
+
15
+ class << self
16
+ # @param string [String] string that we want to convert to our underscore format
17
+ # @return [String] inflected string
18
+ # @example
19
+ # Karafka::Helpers::Inflector.map('Module/ControllerName') #=> 'module_controller_name'
20
+ def map(string)
21
+ @map[string] ||= ENGINE.underscore(string).tr('/', '_')
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
@@ -7,7 +7,6 @@ module Karafka
7
7
  # Multidelegator is used to delegate calls to multiple targets
8
8
  class MultiDelegator
9
9
  # @param targets to which we want to delegate methods
10
- #
11
10
  def initialize(*targets)
12
11
  @targets = targets
13
12
  end
@@ -5,8 +5,6 @@ module Karafka
5
5
  # Default logger for Event Delegator
6
6
  # @note It uses ::Logger features - providing basic logging
7
7
  class Logger < ::Logger
8
- include Singleton
9
-
10
8
  # Map containing information about log level for given environment
11
9
  ENV_MAP = {
12
10
  'production' => ::Logger::ERROR,
@@ -16,7 +14,11 @@ module Karafka
16
14
  'default' => ::Logger::INFO
17
15
  }.freeze
18
16
 
17
+ private_constant :ENV_MAP
18
+
19
19
  # Creates a new instance of logger ensuring that it has a place to write to
20
+ # @param _args Any arguments that we don't care about but that are needed in order to
21
+ # make this logger compatible with the default Ruby one
20
22
  def initialize(*_args)
21
23
  ensure_dir_exists
22
24
  super(target)
@@ -26,7 +28,7 @@ module Karafka
26
28
  private
27
29
 
28
30
  # @return [Karafka::Helpers::MultiDelegator] multi delegator instance
29
- # to which we will be writtng logs
31
+ # to which we will be writing logs
30
32
  # We use this approach to log stuff to file and to the STDOUT at the same time
31
33
  def target
32
34
  Karafka::Helpers::MultiDelegator
@@ -34,10 +36,11 @@ module Karafka
34
36
  .to(STDOUT, file)
35
37
  end
36
38
 
37
- # Makes sure the log directory exists
39
+ # Makes sure the log directory exists as long as we can write to it
38
40
  def ensure_dir_exists
39
- dir = File.dirname(log_path)
40
- FileUtils.mkdir_p(dir) unless Dir.exist?(dir)
41
+ FileUtils.mkdir_p(File.dirname(log_path))
42
+ rescue Errno::EACCES
43
+ nil
41
44
  end
42
45
 
43
46
  # @return [Pathname] Path to a file to which we should log
@@ -6,13 +6,11 @@ module Karafka
6
6
  # Monitor is used to hookup external monitoring services to monitor how Karafka works
7
7
  # It provides a standardized API for checking incoming messages/enqueueing etc
8
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
9
+ # same time, which means that you might have for example file logging and NewRelic at the same
10
10
  # time
11
11
  # @note This class acts as a singleton because we are only permitted to have single monitor
12
12
  # per running process (just as logger)
13
13
  class Monitor < Dry::Monitor::Notifications
14
- include Singleton
15
-
16
14
  # List of events that we support in the system and to which a monitor client can hook up
17
15
  # @note The non-error once support timestamp benchmarking
18
16
  # @note Depending on Karafka extensions and additional engines, this might not be the
@@ -21,11 +19,14 @@ module Karafka
21
19
  # Last 4 events are from WaterDrop but for convenience we use the same monitor for the
22
20
  # whole karafka ecosystem
23
21
  BASE_EVENTS = %w[
24
- params.params.parse
25
- params.params.parse.error
22
+ params.params.deserialize
23
+ params.params.deserialize.error
24
+ connection.listener.before_fetch_loop
25
+ connection.listener.fetch_loop
26
26
  connection.listener.fetch_loop.error
27
27
  connection.client.fetch_loop.error
28
- connection.delegator.call
28
+ connection.batch_delegator.call
29
+ connection.message_delegator.call
29
30
  fetcher.call.error
30
31
  backends.inline.process
31
32
  process.notice_signal
@@ -34,8 +35,12 @@ module Karafka
34
35
  async_producer.call.retry
35
36
  sync_producer.call.error
36
37
  sync_producer.call.retry
37
- server.stop
38
- server.stop.error
38
+ app.initializing
39
+ app.initialized
40
+ app.running
41
+ app.stopping
42
+ app.stopping.error
43
+ app.stopped
39
44
  ].freeze
40
45
 
41
46
  private_constant :BASE_EVENTS
@@ -52,7 +57,8 @@ module Karafka
52
57
  def subscribe(event_name_or_listener)
53
58
  return super unless event_name_or_listener.is_a?(String)
54
59
  return super if available_events.include?(event_name_or_listener)
55
- raise Errors::UnregisteredMonitorEvent, event_name_or_listener
60
+
61
+ raise Errors::UnregisteredMonitorEventError, event_name_or_listener
56
62
  end
57
63
 
58
64
  # @return [Array<String>] names of available events to which we can subscribe
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Karafka
4
+ module Instrumentation
5
+ # Listener that sets a proc title with a nice descriptive value
6
+ class ProctitleListener
7
+ # Updates proc title to an initializing one
8
+ # @param _event [Dry::Events::Event] event details including payload
9
+ def on_app_initializing(_event)
10
+ setproctitle('initializing')
11
+ end
12
+
13
+ # Updates proc title to a running one
14
+ # @param _event [Dry::Events::Event] event details including payload
15
+ def on_app_running(_event)
16
+ setproctitle('running')
17
+ end
18
+
19
+ # Updates proc title to a stopping one
20
+ # @param _event [Dry::Events::Event] event details including payload
21
+ def on_app_stopping(_event)
22
+ setproctitle('stopping')
23
+ end
24
+
25
+ private
26
+
27
+ # Sets a proper proc title with our constant prefix
28
+ # @param status [String] any status we want to set
29
+ def setproctitle(status)
30
+ ::Process.setproctitle(
31
+ "karafka #{Karafka::App.config.client_id} (#{status})"
32
+ )
33
+ end
34
+ end
35
+ end
36
+ end